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 + "!")
def generate(args: PmbArgs, pkgname, fork_alpine=False):
def generate(pkgname, fork_alpine=False):
if fork_alpine:
prefix, folder, options = (pkgname, "temp",
{"confirm_overwrite": True})
@ -83,7 +83,7 @@ def generate(args: PmbArgs, pkgname, fork_alpine=False):
else:
# Run pmb.aportgen.PREFIX.generate()
# 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
if os.path.exists(path_target):

View file

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

View file

@ -101,7 +101,7 @@ def ask_for_flash_method():
" 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"
" automatically fill out the flasher information for your"
" deviceinfo file. Either specify the path to an image or"
@ -114,7 +114,7 @@ def ask_for_bootimg(args: PmbArgs):
if not path:
return None
try:
return pmb.parse.bootimg(args, path)
return pmb.parse.bootimg(path)
except Exception as e:
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")
def generate(args: PmbArgs, pkgname):
def generate(pkgname):
arch = ask_for_architecture()
manufacturer = ask_for_manufacturer()
name = ask_for_name(manufacturer)
@ -330,7 +330,7 @@ def generate(args: PmbArgs, pkgname):
flash_method = ask_for_flash_method()
bootimg = None
if flash_method in ["fastboot", "heimdall-bootimg"]:
bootimg = ask_for_bootimg(args)
bootimg = ask_for_bootimg()
generate_deviceinfo(pkgname, name, manufacturer, year, arch,
chassis, has_keyboard, has_external_storage,

View file

@ -2,18 +2,17 @@
# SPDX-License-Identifier: GPL-3.0-or-later
import pmb.aportgen.core
from pmb.core import get_context
from pmb.types import PmbArgs
import pmb.helpers.git
import pmb.helpers.run
def generate(args: PmbArgs, pkgname):
def generate(pkgname):
# Copy original aport
prefix = pkgname.split("-")[0]
arch = pkgname.split("-")[1]
context = get_context()
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)"
elif prefix == "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
def generate(args: PmbArgs, pkgname):
def generate(pkgname):
arch = "x86"
if pkgname != "grub-efi-x86":
raise RuntimeError("only grub-efi-x86 is available")

View file

@ -8,7 +8,7 @@ import pmb.parse.apkindex
import pmb.parse.arch
def generate_apkbuild(args: PmbArgs, pkgname, deviceinfo, patches):
def generate_apkbuild(pkgname, deviceinfo, patches):
device = "-".join(pkgname.split("-")[1:])
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")
def generate(args: PmbArgs, pkgname):
def generate(pkgname):
device = "-".join(pkgname.split("-")[1:])
deviceinfo = pmb.parse.deviceinfo(device)
work = get_context().config.work
@ -129,4 +129,4 @@ def generate(args: PmbArgs, pkgname):
"../../.shared-patches/linux/" + 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.chroot.apk
import pmb.chroot.apk_static
from pmb.types import PmbArgs
import pmb.helpers.run
import pmb.parse.apkindex
from pmb.core import Chroot, get_context
def generate(args: PmbArgs, pkgname):
def generate(pkgname):
arch = pkgname.split("-")[1]
# Parse musl version from APKINDEX

View file

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

View file

@ -190,7 +190,7 @@ def package_kernel(args: PmbArgs):
modify_apkbuild(pkgname, aport)
apkbuild_path = context.config.work / "aportgen/APKBUILD"
arch = args.deviceinfo["arch"]
arch = pmb.parse.deviceinfo()["arch"]
apkbuild = pmb.parse.apkbuild(apkbuild_path, check_pkgname=False)
if apkbuild["_outdir"]:
kbuild_out = apkbuild["_outdir"]

View file

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

View file

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

View file

@ -9,6 +9,7 @@ from typing import List
import pmb.chroot
from pmb.types import PmbArgs
import pmb.build
import pmb.helpers.file
import pmb.helpers.git
import pmb.helpers.pmaports
@ -99,7 +100,7 @@ def index_repo(args: PmbArgs, arch=None):
:param arch: when not defined, re-index all repos
"""
pmb.build.init(args)
pmb.build.init()
channel = pmb.config.pmaports.read_config()["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
def build(args: PmbArgs, flavor, chroot: Chroot):
def build(flavor, chroot: Chroot):
# Update mkinitfs and hooks
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()
# Call mkinitfs
@ -30,7 +30,7 @@ def build(args: PmbArgs, flavor, 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
/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
def ls(args: PmbArgs, flavor, suffix, extra=False):
def ls( flavor, suffix, extra=False):
tmp = "/tmp/initfs-extracted"
if extra:
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(["rm", "-r", tmp], suffix)
@ -95,17 +95,17 @@ def frontend(args: PmbArgs):
# Handle initfs actions
action = args.action_initfs
if action == "build":
build(args, flavor, chroot)
build(flavor, chroot)
elif action == "extract":
dir = extract(args, flavor, chroot)
dir = extract(flavor, chroot)
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}")
elif action == "ls":
logging.info("*** initramfs ***")
ls(args, flavor, chroot)
ls(flavor, chroot)
logging.info("*** initramfs-extra ***")
ls(args, flavor, chroot, True)
ls(flavor, chroot, True)
# Handle hook actions
elif action == "hook_ls":
@ -117,7 +117,7 @@ def frontend(args: PmbArgs):
pmb.chroot.initfs_hooks.delete(args.hook, chroot)
# Rebuild the initfs after adding/removing a hook
build(args, flavor, chroot)
build(flavor, chroot)
if action in ["ls", "extract"]:
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)
def update(args: PmbArgs, suffix: Chroot):
def update(suffix: Chroot):
"""
Rebuild and update all hooks that are out of date
"""

View file

@ -5,6 +5,7 @@ import os
from pathlib import Path
from typing import Dict
import pmb.config
import pmb.chroot.apk
from pmb.types import PmbArgs
import pmb.helpers.run
import pmb.parse
@ -98,7 +99,7 @@ def mount(chroot: Chroot):
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
target = chroot / "native"
pmb.helpers.mount.bind(source, target)

View file

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

View file

@ -114,7 +114,7 @@ def ask_which_scripts_to_run(scripts_available):
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
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.chroot.init(args)
pmb.chroot.init()
tarball_path = Chroot.native() / "tmp/git.tar.gz"
files = pmb.helpers.git.get_files(topdir)
@ -141,7 +141,7 @@ def copy_git_repo_to_chroot(args: PmbArgs, topdir):
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
chroot. Display a progress message and stop on error (without printing
a python stack trace).
@ -174,7 +174,7 @@ def run_scripts(args: PmbArgs, topdir, scripts):
else:
# Run inside pmbootstrap chroot
if not repo_copied:
copy_git_repo_to_chroot(args, topdir)
copy_git_repo_to_chroot(topdir)
repo_copied = True
env = {"TESTUSER": "pmos"}

View file

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

View file

@ -2,6 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from pmb.core import get_context
from pmb.core.chroot import Chroot
from pmb.core.context import Context
from pmb.helpers import logging
import glob
import json
@ -38,13 +39,13 @@ def require_programs():
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.
:returns: the username
"""
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_-]*")
if ret == "root":
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.")
def ask_for_ui(args: PmbArgs, info):
ui_list = pmb.helpers.ui.list_ui(args, info["arch"])
def ask_for_ui(info):
ui_list = pmb.helpers.ui.list_ui(info["arch"])
hidden_ui_count = 0
device_is_accelerated = info.get("gpu_accelerated") == "true"
if not device_is_accelerated:
@ -154,7 +155,7 @@ def ask_for_ui(args: PmbArgs, info):
hidden_ui_count += 1
# Get default
default: Any = args.ui
default: Any = get_context().config.ui
if default not in dict(ui_list).keys():
default = pmb.config.defaults["ui"]
@ -176,7 +177,7 @@ def ask_for_ui(args: PmbArgs, info):
" 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}",
subpackages=False, must_exist=False)
if not apkbuild:
@ -190,17 +191,17 @@ def ask_for_ui_extras(args: PmbArgs, ui):
f" {extra['pkgdesc']}")
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():
return args.systemd
return config.systemd
if pmb.helpers.ui.check_option(ui, "pmb:systemd-never"):
logging.info("Based on your UI selection, OpenRC will be used as init"
" system. This UI does not support systemd.")
return args.systemd
return config.systemd
default_is_systemd = pmb.helpers.ui.check_option(ui, "pmb:systemd")
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"]
answer = pmb.helpers.cli.ask("Install systemd?",
choices,
args.systemd,
config.systemd,
validation_regex=f"^({'|'.join(choices)})$",
complete=choices)
return answer
@ -314,7 +315,7 @@ def ask_for_provider_select(apkbuild, providers_cfg):
" 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
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:
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.
:param device: code name, e.g. "lg-mako"
@ -343,10 +344,10 @@ def ask_for_device_kernel(args: PmbArgs, device: str):
# Get kernels
kernels = pmb.parse._apkbuild.kernels(device)
if not kernels:
return args.kernel
return config.kernel
# Get default
default = args.kernel
default = config.kernel
if default not in kernels:
default = list(kernels.keys())[0]
@ -374,7 +375,7 @@ def ask_for_device_kernel(args: PmbArgs, device: str):
return ret
def ask_for_device(config: Config):
def ask_for_device(context: Context):
"""
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
* 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 "
"existing one, or a new one for porting).")
logging.info(f"Available vendors ({len(vendors)}): {', '.join(vendors)}")
current_vendor = None
current_codename = None
if config.device:
current_vendor = config.device.split("-", 1)[0]
current_codename = config.device.split("-", 1)[1]
if context.config.device:
current_vendor = context.config.device.split("-", 1)[0]
current_codename = context.config.device.split("-", 1)[1]
while True:
vendor = pmb.helpers.cli.ask("Vendor", None, current_vendor,
@ -409,7 +410,7 @@ def ask_for_device(config: Config):
else:
# Archived devices can be selected, but are not displayed
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
codenames = [x.split('-', 1)[1] for x in devices]
logging.info(f"Available codenames ({len(codenames)}): " +
@ -424,7 +425,7 @@ def ask_for_device(config: Config):
device = f"{vendor}-{codename}"
device_path = pmb.helpers.devices.find_path(device, 'deviceinfo')
if device_path is None:
if device == args.devicesdhbfvhubsud:
if device == context.device:
raise RuntimeError(
"This device does not exist anymore, check"
" <https://postmarketos.org/renamed>"
@ -440,14 +441,14 @@ def ask_for_device(config: Config):
pmb.aportgen.generate(f"device-{device}")
pmb.aportgen.generate(f"linux-{device}")
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)
logging.info(f"WARNING: {device} is archived: {archived}")
if not pmb.helpers.cli.confirm(args):
if not pmb.helpers.cli.confirm():
continue
break
kernel = ask_for_device_kernel(args, device)
kernel = ask_for_device_kernel(context.config, device)
return (device, device_path is not None, kernel)
@ -675,7 +676,7 @@ def frontend(args: PmbArgs):
pmb.config.pmaports.install_githooks()
# Device
device, device_exists, kernel = ask_for_device(config)
device, device_exists, kernel = ask_for_device(get_context())
config.device = device
config.kernel = kernel
@ -689,19 +690,19 @@ def frontend(args: PmbArgs):
if device_exists:
config.keymap = ask_for_keymaps(args, info)
config.user = ask_for_username(args)
ask_for_provider_select_pkg(args, "postmarketos-base", config.providers)
ask_for_provider_select_pkg(args, "postmarketos-base-ui", config.providers)
config.user = ask_for_username(args, config.user)
ask_for_provider_select_pkg("postmarketos-base", config.providers)
ask_for_provider_select_pkg("postmarketos-base-ui", config.providers)
# UI and various build options
ui = ask_for_ui(args, info)
ui = ask_for_ui(info)
config.ui = ui
config.ui_extras = ask_for_ui_extras(args, ui)
config.ui_extras = ask_for_ui_extras(config, ui)
# 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)
ask_for_additional_options(args, config)

View file

@ -13,14 +13,22 @@ class Context():
quiet: bool = False
command_timeout: float = 900
sudo_timer: bool = False
force: bool = False
log: Path
# The architecture of the selected device
device_arch: Optional[str] = None
offline: bool = False
# assume yes to prompts
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
command: str = ""
@ -41,6 +49,7 @@ class Context():
__context: Context
# mypy: disable-error-code="return-value"
def get_context(allow_failure: bool=False) -> Context:
"""Get immutable global runtime context."""
global __context

View file

@ -1,3 +1,4 @@
from pmb.core.context import get_context
from pmb.helpers import logging
import os
@ -9,7 +10,8 @@ import pmb.export
from pmb.core import Chroot, ChrootType
def frontend(args: PmbArgs):
def frontend(args: PmbArgs): # FIXME: ARGS_REFACTOR
context = get_context()
# Create the export folder
target = args.export_folder
if not os.path.exists(target):
@ -17,18 +19,18 @@ def frontend(args: PmbArgs):
# Rootfs image note
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"):
logging.info("NOTE: To export the rootfs image, run 'pmbootstrap"
" install' first (without the 'disk' parameter).")
# 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:
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
logging.info(f"Export symlinks to: {target}")
if args.odin_flashable_tar:
pmb.export.odin(args, flavor, target)
pmb.export.odin(context, flavor, target)
pmb.export.symlinks(args, flavor, target)

View file

@ -1,25 +1,26 @@
# Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
from pmb.core.context import Context
from pmb.helpers import logging
from pathlib import Path
import pmb.build
import pmb.chroot.apk
import pmb.config
from pmb.types import PmbArgs
import pmb.flasher
import pmb.helpers.file
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
for devices configured with the flasher method 'heimdall-isorec'
and with boot.img for devices with 'heimdall-bootimg'
"""
pmb.flasher.init(args)
suffix = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud)
pmb.flasher.init(context.device)
suffix = Chroot(ChrootType.ROOTFS, context.device)
deviceinfo = pmb.parse.deviceinfo(context.device)
# Backwards compatibility with old mkinitfs (pma#660)
suffix_flavor = f"-{flavor}"
@ -28,7 +29,7 @@ def odin(args: PmbArgs, flavor, folder: Path):
suffix_flavor = ""
# Validate method
method = args.deviceinfo["flash_method"]
method = deviceinfo["flash_method"]
if not method.startswith("heimdall-"):
raise RuntimeError("An odin flashable tar is not supported"
f" for the flash method '{method}' specified"
@ -36,10 +37,8 @@ def odin(args: PmbArgs, flavor, folder: Path):
" Only 'heimdall' methods are supported.")
# Partitions
partition_kernel = \
args.deviceinfo["flash_heimdall_partition_kernel"] or "KERNEL"
partition_initfs = \
args.deviceinfo["flash_heimdall_partition_initfs"] or "RECOVERY"
partition_kernel = deviceinfo["flash_heimdall_partition_kernel"] or "KERNEL"
partition_initfs = deviceinfo["flash_heimdall_partition_initfs"] or "RECOVERY"
# Temporary folder
temp_folder = "/tmp/odin-flashable-tar"
@ -49,12 +48,12 @@ def odin(args: PmbArgs, flavor, folder: Path):
# Odin flashable tar generation script
# (because redirecting stdin/stdout is not allowed
# 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:
odin_kernel_md5 = f"{partition_kernel}.bin.md5"
odin_initfs_md5 = f"{partition_initfs}.bin.md5"
odin_device_tar = f"{args.devicesdhbfvhubsud}.tar"
odin_device_tar_md5 = f"{args.devicesdhbfvhubsud}.tar.md5"
odin_device_tar = f"{context.device}.tar"
odin_device_tar_md5 = f"{context.device}.tar.md5"
handle.write(
"#!/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
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/"]),
pmb.chroot.root(["chown", "pmos:pmos",
f"/home/pmos/rootfs/{odin_device_tar_md5}"])
@ -99,7 +98,7 @@ def odin(args: PmbArgs, flavor, folder: Path):
# Create the symlink
file = Chroot.native() / "home/pmos/rootfs" / 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
msg = f" * {odin_device_tar_md5}"

View file

@ -1,5 +1,6 @@
# Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
from pmb.core.context import get_context
from pmb.helpers import logging
from pathlib import Path
from typing import List
@ -19,6 +20,9 @@ def symlinks(args: PmbArgs, flavor, folder: Path):
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)
suffix = f"-{flavor}"
pmaports_cfg = pmb.config.pmaports.read_config()
@ -35,28 +39,28 @@ def symlinks(args: PmbArgs, flavor, folder: Path):
f"uInitrd{suffix}": "Initramfs, legacy u-boot image format",
f"uImage{suffix}": "Kernel, legacy u-boot image format",
f"vmlinuz{suffix}": "Linux kernel",
f"{args.devicesdhbfvhubsud}.img": "Rootfs with partitions for /boot and /",
f"{args.devicesdhbfvhubsud}-boot.img": "Boot partition image",
f"{args.devicesdhbfvhubsud}-root.img": "Root partition image",
f"pmos-{args.devicesdhbfvhubsud}.zip": "Android recovery flashable zip",
f"{context.device}.img": "Rootfs with partitions for /boot and /",
f"{context.device}-boot.img": "Boot partition image",
f"{context.device}-root.img": "Root partition image",
f"pmos-{context.device}.zip": "Android recovery flashable zip",
"lk2nd.img": "Secondary Android bootloader",
}
# Generate a list of patterns
chroot_native = Chroot.native()
path_boot = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud) / "boot"
chroot_buildroot = Chroot.buildroot(args.deviceinfo['arch'])
path_boot = Chroot(ChrootType.ROOTFS, context.device) / "boot"
chroot_buildroot = Chroot.buildroot(arch)
files: List[Path] = [
path_boot / f"boot.img{suffix}",
path_boot / f"uInitrd{suffix}",
path_boot / f"uImage{suffix}",
path_boot / f"vmlinuz{suffix}",
path_boot / "dtbo.img",
chroot_native / "home/pmos/rootfs" / f"{args.devicesdhbfvhubsud}.img",
chroot_native / "home/pmos/rootfs" / f"{args.devicesdhbfvhubsud}-boot.img",
chroot_native / "home/pmos/rootfs" / f"{args.devicesdhbfvhubsud}-root.img",
chroot_native / "home/pmos/rootfs" / f"{context.device}.img",
chroot_native / "home/pmos/rootfs" / f"{context.device}-boot.img",
chroot_native / "home/pmos/rootfs" / f"{context.device}-root.img",
chroot_buildroot / "var/libpostmarketos-android-recovery-installer" /
f"pmos-{args.devicesdhbfvhubsud}.zip",
f"pmos-{context.device}.zip",
path_boot / "lk2nd.img"
]
@ -73,4 +77,4 @@ def symlinks(args: PmbArgs, flavor, folder: Path):
msg += " (" + info[basename] + ")"
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
# SPDX-License-Identifier: GPL-3.0-or-later
from typing import Dict
from pmb.core.context import get_context
from pmb.helpers import logging
import pmb.config
@ -15,106 +17,93 @@ import pmb.parse.kconfig
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)
flavor = pmb.helpers.frontend._parse_flavor(args, args.autoinstall)
if args.autoinstall:
pmb.chroot.initfs.build(args, flavor, Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud))
flavor = pmb.helpers.frontend._parse_flavor(device, autoinstall)
if autoinstall:
pmb.chroot.initfs.build(flavor, Chroot(ChrootType.ROOTFS, device))
# 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
if args.action_flasher == "boot":
if boot:
logging.info("(native) boot " + flavor + " kernel")
pmb.flasher.run(args, "boot", flavor)
pmb.flasher.run(device, deviceinfo, "boot", flavor)
else:
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 "
"USB interface shortly.")
logging.info("Then you can connect to your device using ssh after pmOS has"
" 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"
" sure that Unl0kr has been properly configured for your"
" device")
def list_flavors(args: PmbArgs):
suffix = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud)
logging.info(f"({suffix}) installed kernel flavors:")
logging.info("* " + pmb.chroot.other.kernel_flavor_installed(suffix))
def list_flavors(device: str):
chroot = Chroot(ChrootType.ROOTFS, device)
logging.info(f"({chroot}) installed kernel flavors:")
logging.info("* " + pmb.chroot.other.kernel_flavor_installed(chroot))
def rootfs(args: PmbArgs):
method = args.flash_method or args.deviceinfo["flash_method"]
def rootfs(device: str, deviceinfo: Dict[str, str], method: str):
# Generate rootfs, install flasher
suffix = ".img"
if pmb.config.flashers.get(method, {}).get("split", False):
suffix = "-root.img"
img_path = Chroot.native() / "home/pmos/rootfs" / f"{args.devicesdhbfvhubsud}{suffix}"
img_path = Chroot.native() / "home/pmos/rootfs" / f"{device}{suffix}"
if not img_path.exists():
raise RuntimeError("The rootfs has not been generated yet, please run"
" 'pmbootstrap install' first.")
# Do not flash if using fastboot & image is too large
if method.startswith("fastboot") \
and args.deviceinfo["flash_fastboot_max_size"]:
and deviceinfo["flash_fastboot_max_size"]:
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:
raise RuntimeError("The rootfs is too large for fastboot to"
" flash.")
# Run the flasher
logging.info("(native) flash rootfs image")
pmb.flasher.run(args, "flash_rootfs")
pmb.flasher.run(device, deviceinfo, "flash_rootfs")
def flash_vbmeta(args: PmbArgs):
logging.info("(native) flash vbmeta.img with verity disabled flag")
pmb.flasher.run(args, "flash_vbmeta")
def list_devices(device, deviceinfo):
pmb.flasher.run(device, deviceinfo, "list_devices")
def flash_dtbo(args: PmbArgs):
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):
def sideload(device: str, deviceinfo: Dict[str, str]):
# Install depends
pmb.flasher.install_depends(args)
pmb.flasher.install_depends()
# Mount the buildroot
chroot = Chroot.buildroot(args.deviceinfo["arch"])
chroot = Chroot.buildroot(deviceinfo["arch"])
mountpoint = "/mnt/" / chroot
pmb.helpers.mount.bind(chroot.path,
Chroot.native().path / mountpoint)
# Missing recovery zip error
if not (Chroot.native() / mountpoint / "/var/lib/postmarketos-android-recovery-installer"
/ f"pmos-{args.devicesdhbfvhubsud}.zip").exists():
/ f"pmos-{device}.zip").exists():
raise RuntimeError("The recovery zip has not been generated yet,"
" please run 'pmbootstrap install' with the"
" '--android-recovery-zip' parameter first!")
pmb.flasher.run(args, "sideload")
pmb.flasher.run(device, deviceinfo, "sideload")
def flash_lk2nd(args: PmbArgs):
method = args.flash_method or args.deviceinfo["flash_method"]
def flash_lk2nd(device: str, deviceinfo: Dict[str, str], method: str):
if method == "fastboot":
# In the future this could be expanded to use "fastboot flash lk2nd $img"
# which reflashes/updates lk2nd from itself. For now let the user handle this
# manually since supporting the codepath with heimdall requires more effort.
pmb.flasher.init(args)
pmb.flasher.init(device)
logging.info("(native) checking current fastboot product")
output = pmb.chroot.root(["fastboot", "getvar", "product"],
output="interactive", output_return=True)
@ -125,7 +114,7 @@ def flash_lk2nd(args: PmbArgs):
" bootloader mode to re-flash lk2nd.")
# 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)
lk2nd_pkg = None
for dep in apkbuild["depends"]:
@ -136,16 +125,19 @@ def flash_lk2nd(args: PmbArgs):
if not lk2nd_pkg:
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)
logging.info("(native) flash lk2nd image")
pmb.flasher.run(args, "flash_lk2nd")
pmb.flasher.run(device, deviceinfo, "flash_lk2nd")
def frontend(args: PmbArgs):
context = get_context()
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",
"flash_lk2nd"]:
@ -153,18 +145,20 @@ def frontend(args: PmbArgs):
return
if action in ["boot", "flash_kernel"]:
kernel(args)
kernel(device, deviceinfo)
elif action == "flash_rootfs":
rootfs(args)
rootfs(device, deviceinfo, method)
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":
flash_dtbo(args)
logging.info("(native) flash dtbo image")
pmb.flasher.run(device, deviceinfo, "flash_dtbo")
elif action == "flash_lk2nd":
flash_lk2nd(args)
flash_lk2nd(device, deviceinfo, method)
elif action == "list_flavors":
list_flavors(args)
list_flavors(device)
elif action == "list_devices":
list_devices(args)
list_devices(device, deviceinfo)
elif action == "sideload":
sideload(args)
sideload(device, deviceinfo)

View file

@ -5,15 +5,18 @@ import pmb.config
import pmb.config.pmaports
from pmb.types import PmbArgs
import pmb.helpers.mount
import pmb.helpers.args
from pmb.helpers.mount import mount_device_rootfs
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'):
method = args.flash_method or args.deviceinfo["flash_method"]
else:
method = args.deviceinfo["flash_method"]
method = args.flash_method
if not method:
method = pmb.parse.deviceinfo()["flash_method"]
if method not in pmb.config.flashers:
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())
def init(args: PmbArgs):
install_depends(args)
def init(device: str):
install_depends()
# Mount folders from host system
for folder in pmb.config.flash_mount_bind:
pmb.helpers.mount.bind(folder, Chroot.native() / folder)
# 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
# SPDX-License-Identifier: GPL-3.0-or-later
from typing import Dict
from pmb.types import PmbArgs
import pmb.flasher
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_"):
return
name = args.deviceinfo["name"]
if value in args.deviceinfo["partition_blacklist"].split(","):
name = deviceinfo["name"]
if value in deviceinfo["partition_blacklist"].split(","):
raise RuntimeError("'" + value + "'" + " partition is blacklisted " +
"from being flashed! See the " + name + " device " +
"wiki page for more information.")
def run(args: PmbArgs, action, flavor=None):
pmb.flasher.init(args)
def run(device: str, deviceinfo: Dict[str, str], action, flavor=None):
pmb.flasher.init(device)
# FIXME: handle argparsing and pass in only the args we need.
args = pmb.helpers.args.please_i_really_need_args()
# Verify action
method = args.flash_method or args.deviceinfo["flash_method"]
method = args.flash_method or deviceinfo["flash_method"]
cfg = pmb.config.flashers[method]
if not isinstance(cfg["actions"], dict):
raise TypeError(f"Flashers misconfigured! {method} key 'actions' should be a dictionary")
@ -74,7 +79,7 @@ def run(args: PmbArgs, action, flavor=None):
" but the value for this variable"
" is None! Is that missing in your"
" deviceinfo?")
check_partition_blacklist(args, key, value)
check_partition_blacklist(args, deviceinfo, key, value)
command[i] = command[i].replace(key, value)
# Remove empty strings

View file

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

View file

@ -4,7 +4,7 @@ import os
from pathlib import Path
from typing import List, Sequence
import pmb.chroot.run
import pmb.chroot
import pmb.config.pmaports
from pmb.types import PathString, PmbArgs
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:
"""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
# source
skip = False

View file

@ -48,7 +48,7 @@ __args: PmbArgs = PmbArgs()
code as well.
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())
def add_deviceinfo(args: PmbArgs):
"""Add and verify the deviceinfo (only after initialization)"""
setattr(args, "deviceinfo", pmb.parse.deviceinfo())
def init(args: PmbArgs) -> PmbArgs:
global __args
# Basic initialization
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
context = Context(config)
@ -102,6 +102,8 @@ def init(args: PmbArgs) -> PmbArgs:
context.offline = args.offline
context.command = args.action
context.cross = args.cross
context.assume_yes = getattr(args, "assume_yes", False)
context.force = getattr(args, "force", False)
if args.mirrors_postmarketos:
context.config.mirrors_postmarketos = args.mirrors_postmarketos
if args.mirror_alpine:
@ -121,9 +123,9 @@ def init(args: PmbArgs) -> PmbArgs:
if args.action not in ["init", "checksum", "config", "bootimg_analyze", "log",
"pull", "shutdown", "zap"]:
pmb.config.pmaports.read_config()
add_deviceinfo(args)
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
delattr(args, "timeout")
@ -134,6 +136,10 @@ def init(args: PmbArgs) -> PmbArgs:
delattr(args, "aports")
delattr(args, "mirrors_postmarketos")
delattr(args, "mirror_alpine")
if hasattr(args, "force"):
delattr(args, "force")
if hasattr(args, "device"):
delattr(args, "device")
# args.work is deprecated!
delattr(args, "work")
@ -142,7 +148,7 @@ def init(args: PmbArgs) -> PmbArgs:
if not key.startswith("_") and not key == "from_argparse":
setattr(__args, key, value)
print(json.dumps(__args.__dict__))
#print(json.dumps(__args.__dict__))
#sys.exit(0)

View file

@ -90,7 +90,7 @@ def is_older_than(path, seconds):
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."""
if os.path.exists(link):
if (os.path.islink(link) and

View file

@ -1,7 +1,7 @@
# Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
import json
from typing import List, Sequence
from typing import List, Sequence, Tuple
from pmb.helpers import logging
import os
from pathlib import Path
@ -40,7 +40,7 @@ import pmb.sideload
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.
: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
# "postmarketos-<manufacturer>-<device/chip>", e.g.
# "postmarketos-qcom-sdm845"
chroot = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud)
chroot = Chroot(ChrootType.ROOTFS, device)
flavor = pmb.chroot.other.kernel_flavor_installed(
chroot, autoinstall)
@ -65,7 +65,7 @@ def _parse_suffix(args: PmbArgs) -> Chroot:
return Chroot(ChrootType.ROOTFS, get_context().config.device)
elif args.buildroot:
if args.buildroot == "device":
return Chroot.buildroot(args.deviceinfo["arch"])
return Chroot.buildroot(pmb.parse.deviceinfo()["arch"])
else:
return Chroot.buildroot(args.buildroot)
elif args.suffix:
@ -76,14 +76,14 @@ def _parse_suffix(args: PmbArgs) -> Chroot:
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"
dest = Chroot(ChrootType.INSTALLER, args.devicesdhbfvhubsud) / chroot_dest
dest = Chroot(ChrootType.INSTALLER, device) / chroot_dest
if dest.exists():
return
if args.ondev_cp:
for _, chroot_dest_cp in args.ondev_cp:
if ondev_cp:
for _, chroot_dest_cp in ondev_cp:
if chroot_dest_cp == chroot_dest:
return
@ -112,14 +112,14 @@ def build(args: PmbArgs):
# Set src and force
src = os.path.realpath(os.path.expanduser(args.src[0])) \
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):
raise RuntimeError("Invalid path specified for --src: " + src)
# Ensure repo_bootstrap is done for all arches we intend to build for
for package in args.packages:
arch_package = args.arch or pmb.build.autodetect.arch(args, package)
pmb.helpers.repo_bootstrap.require_bootstrap(args, arch_package,
pmb.helpers.repo_bootstrap.require_bootstrap(arch_package,
f"build {package} for {arch_package}")
context = get_context()
@ -134,8 +134,8 @@ def build(args: PmbArgs):
def build_init(args: PmbArgs):
suffix = _parse_suffix(args)
pmb.build.init(args, suffix)
chroot = _parse_suffix(args)
pmb.build.init(chroot)
def checksum(args: PmbArgs):
@ -148,7 +148,7 @@ def checksum(args: PmbArgs):
def sideload(args: PmbArgs):
arch = args.arch
user = args.user
user = get_context().config.user
host = args.host
pmb.sideload.sideload(args, user, host, args.port, arch, args.install_key,
args.packages)
@ -156,25 +156,27 @@ def sideload(args: PmbArgs):
def netboot(args: PmbArgs):
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):
# Suffix
suffix = _parse_suffix(args)
if (args.user and suffix != Chroot.native() and
not suffix.type == ChrootType.BUILDROOT):
chroot = _parse_suffix(args)
user = get_context().config.user
if (user and chroot != Chroot.native() and
not chroot.type == ChrootType.BUILDROOT):
raise RuntimeError("--user is only supported for native or"
" 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.")
# apk: check minimum version, install packages
pmb.chroot.apk.check_min_version(suffix)
pmb.chroot.apk.check_min_version(chroot)
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
env = {}
@ -196,14 +198,14 @@ def chroot(args: PmbArgs):
size_root, size_reserve)
# Run the command as user/root
if args.user:
logging.info(f"({suffix}) % su pmos -c '" +
if user:
logging.info(f"({chroot}) % su pmos -c '" +
" ".join(args.command) + "'")
pmb.chroot.user(args.command, suffix, output=args.output,
pmb.chroot.user(args.command, chroot, output=args.output,
env=env)
else:
logging.info(f"({suffix}) % " + " ".join(args.command))
pmb.chroot.root(args.command, suffix, output=args.output,
logging.info(f"({chroot}) % " + " ".join(args.command))
pmb.chroot.root(args.command, chroot, output=args.output,
env=env)
@ -219,7 +221,7 @@ def config(args: PmbArgs):
raise RuntimeError("config --reset requires a name to be given.")
def_value = getattr(Config(), args.name)
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)
elif args.value is not None:
setattr(config, args.name, args.value)
@ -258,6 +260,9 @@ def initfs(args: PmbArgs):
def install(args: PmbArgs):
config = get_context().config
device = config.device
deviceinfo = pmb.parse.deviceinfo(device)
if args.no_fde:
logging.warning("WARNING: --no-fde is deprecated,"
" as it is now the default.")
@ -271,8 +276,8 @@ def install(args: PmbArgs):
raise ValueError("Installation using rsync"
" is not currently supported on btrfs filesystem.")
pmb.helpers.repo_bootstrap.require_bootstrap(args, args.deviceinfo["arch"],
f"do 'pmbootstrap install' for {args.deviceinfo['arch']}"
pmb.helpers.repo_bootstrap.require_bootstrap(deviceinfo["arch"],
f"do 'pmbootstrap install' for {deviceinfo['arch']}"
" (deviceinfo_arch)")
# On-device installer checks
@ -296,7 +301,7 @@ def install(args: PmbArgs):
raise ValueError("--on-device-installer cannot be combined with"
" --filesystem")
if args.deviceinfo["cgpt_kpart"]:
if deviceinfo["cgpt_kpart"]:
raise ValueError("--on-device-installer cannot be used with"
" ChromeOS devices")
else:
@ -306,7 +311,7 @@ def install(args: PmbArgs):
raise ValueError("--no-rootfs can only be combined with --ondev."
" Do you mean --no-image?")
if args.ondev_no_rootfs:
_install_ondev_verify_no_rootfs(args)
_install_ondev_verify_no_rootfs(device, args.ondev_cp)
# On-device installer overrides
if args.on_device_installer:
@ -315,15 +320,15 @@ def install(args: PmbArgs):
# a password for the user, disable SSH password authentication,
# optionally add a new user for SSH that must not have the same
# username etc.)
if args.user != "user":
logging.warning(f"WARNING: custom username '{args.user}' will be"
if config.user != "user":
logging.warning(f"WARNING: custom username '{config.user}' will be"
" replaced with 'user' for the on-device"
" installer.")
args.user = "user"
config.user = "user"
if not args.disk and args.split is None:
# 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):
args.split = True
@ -345,10 +350,10 @@ def install(args: PmbArgs):
if not args.install_local_pkgs:
# Implies that we don't build outdated packages (overriding the answer
# 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
if (get_context().config.work / "packages").glob("*"):
if (config.work / "packages").glob("*"):
raise ValueError("--no-local-pkgs specified, but locally built"
" packages found. Consider 'pmbootstrap zap -p'"
" to delete them.")
@ -443,7 +448,7 @@ def kconfig(args: PmbArgs):
skipped = 0
packages.sort()
for package in packages:
if not args.force:
if not get_context().force:
pkgname = package if package.startswith("linux-") \
else "linux-" + package
aport = pmb.helpers.pmaports.find(pkgname)
@ -451,7 +456,7 @@ def kconfig(args: PmbArgs):
if "!pmb:kconfigcheck" in apkbuild["options"]:
skipped += 1
continue
if not pmb.parse.kconfig.check(args, package, components_list,
if not pmb.parse.kconfig.check(package, components_list,
details=details):
error = True
@ -467,7 +472,7 @@ def kconfig(args: PmbArgs):
if args.package:
pkgname = args.package if isinstance(args.package, str) else args.package[0]
else:
pkgname = args.deviceinfo["codename"]
pkgname = get_context().config.device
use_oldconfig = args.action_kconfig == "migrate"
pmb.build.menuconfig(args, pkgname, use_oldconfig)
@ -577,7 +582,7 @@ def zap(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"
for line in pmb.aportgen.device.\
generate_deviceinfo_fastboot_content(bootimg).split("\n"):
@ -667,4 +672,4 @@ def ci(args: PmbArgs):
if not scripts_selected:
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,
or an empty string if it's not a git repository.
"""
return pmb.helpers.run.user(["git", "rev-parse", "--show-toplevel"],
repo, output_return=True, check=False).rstrip()
res = pmb.helpers.run.user(["git", "rev-parse", "--show-toplevel"],
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):

View file

@ -1,6 +1,7 @@
# Copyright 2023 Danct12 <danct12@disroot.org>
# SPDX-License-Identifier: GPL-3.0-or-later
from pathlib import Path
from pmb.core.chroot import Chroot
from pmb.helpers import logging
import os
@ -17,12 +18,13 @@ def check(args: PmbArgs, pkgnames):
: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
# package folders
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
# root

View file

@ -10,7 +10,9 @@ See also:
"""
import copy
from typing import Any, Dict
from pmb.core.context import get_context
from pmb.helpers import logging
import pmb.build._package
from pmb.types import PmbArgs
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)
if pmaport:
ret = {"arch": pmaport["arch"],
"depends": pmb.build._package.get_depends(args, pmaport),
"depends": pmb.build._package.get_depends(get_context(), pmaport),
"pkgname": pmaport["pkgname"],
"provides": pmaport["provides"],
"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
# against) and packages for which no aport exists.
if (depend.startswith("so:") or
not pmb.helpers.pmaports.find_optional(args, depend)):
not pmb.helpers.pmaports.find_optional(depend)):
missing.append(depend)
# Increase pkgrel
@ -116,7 +116,7 @@ def auto(args: PmbArgs, dry=False):
logging.verbose(
f"{pkgname}: origin '{origin}' found again")
continue
aport_path = pmb.helpers.pmaports.find_optional(args, origin)
aport_path = pmb.helpers.pmaports.find_optional(origin)
if not aport_path:
logging.warning("{}: origin '{}' aport not found".format(
pkgname, origin))

View file

@ -58,6 +58,7 @@ def urls(user_repository=True, postmarketos_mirror=True, alpine=True):
"http://...", ...]
"""
ret: List[str] = []
context = get_context()
# Get mirrordirs from channels.cfg (postmarketOS mirrordir is the same as
# 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)
if user_repository:
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
if postmarketos_mirror:
for mirror in context.config.mirrors_postmarketos:

View file

@ -20,7 +20,7 @@ def get_arch(args: PmbArgs):
return args.arch
if args.build_default_device_arch:
return args.deviceinfo["arch"]
return pmb.parse.deviceinfo()["arch"]
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):
log_progress(f"building {package}")
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)
log_progress("bootstrap complete!")
@ -175,7 +175,7 @@ def require_bootstrap_error(repo, arch, trigger_str):
" 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.

View file

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

View file

@ -9,7 +9,7 @@ import pmb.helpers.package
import pmb.parse
def list_ui(args: PmbArgs, arch):
def list_ui(arch):
"""Get all UIs, for which aports are available with their description.
: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-*")):
apkbuild = pmb.parse.apkbuild(path)
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"]))
return ret

View file

@ -21,6 +21,7 @@ import pmb.helpers.devices
from pmb.helpers.mount import mount_device_rootfs
import pmb.helpers.run
import pmb.helpers.other
import pmb.helpers.package
import pmb.install.blockdevice
import pmb.install.recovery
import pmb.install.ui
@ -113,11 +114,11 @@ def copy_files_from_chroot(args: PmbArgs, chroot: Chroot):
"""
# Mount the device rootfs
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
# 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")
if os.path.exists(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)
def create_home_from_skel(args: PmbArgs):
def create_home_from_skel(filesystem: str, user: str):
"""
Create /home/{user} from /etc/skel
"""
rootfs = (Chroot.native() / "mnt/install")
# 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"])
home = (rootfs / "home" / args.user)
home = (rootfs / "home" / user)
if (rootfs / "etc/skel").exists():
pmb.helpers.run.root(["cp", "-a", (rootfs / "etc/skel"), home])
else:
@ -184,8 +185,7 @@ def configure_apk(args: PmbArgs):
pmb.helpers.run.root(["cp", key, rootfs / "etc/apk/keys/"])
# Copy over the corresponding APKINDEX files from cache
index_files = pmb.helpers.repo.apkindex_files(args,
arch=args.deviceinfo["arch"],
index_files = pmb.helpers.repo.apkindex_files(arch=pmb.parse.deviceinfo()["arch"],
user_repository=False)
for f in index_files:
pmb.helpers.run.root(["cp", f, rootfs / "var/cache/apk/"])
@ -221,7 +221,7 @@ def set_user(config: Config):
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
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
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"],
chroot)
@ -258,7 +258,7 @@ def is_root_locked(chroot: Chroot):
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
root login.
@ -268,13 +268,13 @@ def setup_login(args: PmbArgs, chroot: Chroot):
"""
if not args.on_device_installer:
# User password
logging.info(f" *** SET LOGIN PASSWORD FOR: '{args.user}' ***")
logging.info(f" *** SET LOGIN PASSWORD FOR: '{config.user}' ***")
if args.password:
setup_login_chpasswd_user_from_arg(args, chroot)
setup_login_chpasswd_user_from_arg(args, config.user, chroot)
else:
while True:
try:
pmb.chroot.root(["passwd", args.user], chroot,
pmb.chroot.root(["passwd", config.user], chroot,
output="interactive")
break
except RuntimeError:
@ -363,11 +363,9 @@ def setup_keymap(config: Config):
logging.info("NOTE: No valid keymap specified for device")
def setup_timezone(config: Config):
suffix = Chroot(ChrootType.ROOTFS, config.device)
arch = args.deviceinfo["arch"]
alpine_conf = pmb.helpers.package.get(args, "alpine-conf", arch)
def setup_timezone(chroot: Chroot, timezone: str):
# 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)
version = alpine_conf["version"].split("-r")[0]
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
# installed, make sure that setup-timezone makes use of it, since
# 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"]
if not pmb.parse.version.check_string(version, ">=3.14.0"):
setup_tz_cmd += ["-z"]
setup_tz_cmd += [args.timezone]
pmb.chroot.root(setup_tz_cmd, suffix)
setup_tz_cmd += [timezone]
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
"""
# Default to device name. If device name is not a valid hostname then
# default to a static default.
hostname = args.hostname
if not hostname:
hostname = args.devicesdhbfvhubsud
hostname = device
if not pmb.helpers.other.validate_hostname(hostname):
# A valid host name, see:
# 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"
" run 'pmbootstrap init' to configure it.")
suffix = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud)
suffix = Chroot(ChrootType.ROOTFS, device)
# Generate /etc/hostname
pmb.chroot.root(["sh", "-c", "echo " + shlex.quote(hostname) +
" > /etc/hostname"], suffix)
@ -412,37 +409,32 @@ def setup_hostname(args: PmbArgs):
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
update AppStream data on new installs
"""
suffix = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud)
installed_pkgs = pmb.chroot.apk.installed(args, suffix)
installed_pkgs = pmb.chroot.apk.installed(chroot)
if "alpine-appstream-downloader" not in installed_pkgs or args.offline:
if "alpine-appstream-downloader" not in installed_pkgs or offline:
return
if not pmb.chroot.root(["alpine-appstream-downloader",
"/mnt/appstream-data"], suffix, check=False):
pmb.chroot.root(["mkdir", "-p", "/var/lib/swcatalog"], suffix)
"/mnt/appstream-data"], chroot, check=False):
pmb.chroot.root(["mkdir", "-p", "/var/lib/swcatalog"], chroot)
pmb.chroot.root(["cp", "-r", "/mnt/appstream-data/icons",
"/mnt/appstream-data/xml",
"-t", "/var/lib/swcatalog"], suffix)
"-t", "/var/lib/swcatalog"], chroot)
def disable_sshd(args: PmbArgs):
if not args.no_sshd:
return
def disable_sshd(chroot: Chroot):
# 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,
check=False)
# Verify that it's gone
sshd_files = pmb.helpers.run.root(
args, ["find", "-name", "sshd"], output_return=True,
["find", "-name", "sshd"], output_return=True,
working_dir=chroot / "etc/runlevels")
if 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).")
else:
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.")
if args.on_device_installer:
@ -469,24 +461,20 @@ def print_sshd_info(args: PmbArgs):
logging.info("More info: https://postmarketos.org/ondev-debug")
def disable_firewall(args: PmbArgs):
if not args.no_firewall:
return
def disable_firewall(chroot: Chroot):
# 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,
check=False)
# Verify that it's gone
nftables_files = pmb.helpers.run.root(
args, ["find", "-name", "nftables"], output_return=True,
["find", "-name", "nftables"], output_return=True,
working_dir=chroot / "etc/runlevels")
if 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_ok = pmaports_cfg.get("supported_firewall", None) == "nftables"
@ -494,7 +482,6 @@ def print_firewall_info(args: PmbArgs):
apkbuild_found = False
apkbuild_has_opt = False
arch = args.deviceinfo["arch"]
kernel = get_kernel_package(get_context().config)
if kernel:
kernel_apkbuild = pmb.build._package.get_apkbuild(kernel[0], arch)
@ -510,7 +497,7 @@ def print_firewall_info(args: PmbArgs):
if not pmaports_ok:
logging.info("Firewall is not supported in checked out pmaports"
" branch.")
elif args.no_firewall:
elif disabled:
logging.info("Firewall is disabled (--no-firewall).")
elif not apkbuild_found:
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_list = []
binaries = args.deviceinfo["sd_embed_firmware"].split(",")
binaries = pmb.parse.deviceinfo()["sd_embed_firmware"].split(",")
for binary_offset in binaries:
binary, _offset = binary_offset.split(':')
@ -554,7 +541,7 @@ def generate_binary_list(args: PmbArgs, chroot: Chroot, step):
f"/usr/share/{binary}")
# Insure that embedding the firmware will not overrun the
# first partition
boot_part_start = 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)
binary_size = os.path.getsize(binary_path)
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
rootfs_{args.device} or installer_{args.device}
"""
if not args.deviceinfo["sd_embed_firmware"]:
if not pmb.parse.deviceinfo()["sd_embed_firmware"]:
return
step = 1024
if args.deviceinfo["sd_embed_firmware_step_size"]:
if pmb.parse.deviceinfo()["sd_embed_firmware_step_size"]:
try:
step = int(args.deviceinfo["sd_embed_firmware_step_size"])
step = int(pmb.parse.deviceinfo()["sd_embed_firmware_step_size"])
except ValueError:
raise RuntimeError("Value for "
"deviceinfo_sd_embed_firmware_step_size "
"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)
# 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 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
device_rootfs = mount_device_rootfs(args, suffix)
filename = f"{device_rootfs}{args.deviceinfo['cgpt_kpart']}"
device_rootfs = mount_device_rootfs(suffix)
filename = f"{device_rootfs}{pmb.parse.deviceinfo()['cgpt_kpart']}"
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():
@ -680,7 +667,7 @@ def sanity_check_disk_size(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)
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)}"
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"):
boot_filesystem = "vfat"
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 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
logging.info(f"*** ({step}/{steps}) PREPARE INSTALL BLOCKDEVICE ***")
pmb.chroot.shutdown(args, True)
(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)
if not args.rsync:
pmb.install.blockdevice.create(args, size_boot, size_root,
size_reserve, split, disk)
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(
args, layout, size_boot, size_reserve)
else:
pmb.install.partition(args, layout, size_boot, size_reserve)
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)
@ -860,9 +849,9 @@ def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, step
# Just copy all the files
logging.info(f"*** ({step + 1}/{steps}) FILL INSTALL BLOCKDEVICE ***")
copy_files_from_chroot(args, chroot)
create_home_from_skel(args)
create_home_from_skel(args.filesystem, config.user)
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
# 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
sparse = args.sparse
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:
workdir = Path("/home/pmos/rootfs")
logging.info("(native) make sparse rootfs")
pmb.chroot.apk.install(["android-tools"], Chroot.native())
sys_image = args.devicesdhbfvhubsud + ".img"
sys_image_sparse = args.devicesdhbfvhubsud + "-sparse.img"
sys_image = device + ".img"
sys_image_sparse = device + "-sparse.img"
pmb.chroot.user(["img2simg", sys_image, sys_image_sparse],
working_dir=workdir)
pmb.chroot.user(["mv", "-f", sys_image_sparse, sys_image],
working_dir=workdir)
# 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:
logging.info("(native) convert sparse image into Samsung's sparse image format")
pmb.chroot.apk.install(["sm-sparse-image-tool"], Chroot.native())
sys_image = f"{args.devicesdhbfvhubsud}.img"
sys_image_patched = f"{args.devicesdhbfvhubsud}-patched.img"
sys_image = f"{device}.img"
sys_image_patched = f"{device}-patched.img"
pmb.chroot.user(["sm_sparse_image_tool", "samsungify", "--strategy",
samsungify_strategy, sys_image, sys_image_patched],
working_dir=workdir)
@ -905,14 +894,14 @@ def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, step
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
pmbootstrap arguments. """
logging.info("") # make the note stand out
logging.info("*** FLASHING INFORMATION ***")
# System flash information
method = args.deviceinfo["flash_method"]
method = deviceinfo["flash_method"]
flasher = pmb.config.flashers.get(method, {})
flasher_actions = flasher.get("actions", {})
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"
" target device:")
if "flash_rootfs" in flasher_actions and not args.disk and \
bool(args.split) == requires_split:
if "flash_rootfs" in flasher_actions and not have_disk and \
bool(split) == requires_split:
logging.info("* pmbootstrap flasher flash_rootfs")
logging.info(" Flashes the generated rootfs image to your device:")
if args.split:
logging.info(f" {Chroot.native() / 'home/pmos/rootfs' / args.devicesdhbfvhubsud}-rootfs.img")
if split:
logging.info(f" {Chroot.native() / 'home/pmos/rootfs' / device}-rootfs.img")
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"
" contains /boot and / subpartitions. That way we"
" 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
# in deviceinfo
if "flash_vbmeta" in flasher_actions and \
(args.deviceinfo["flash_fastboot_partition_vbmeta"] or
args.deviceinfo["flash_heimdall_partition_vbmeta"]):
(deviceinfo["flash_fastboot_partition_vbmeta"] or
deviceinfo["flash_heimdall_partition_vbmeta"]):
logging.info("* pmbootstrap flasher flash_vbmeta")
logging.info(" Flashes vbmeta image with verification disabled flag.")
# if current flasher supports dtbo and partition is explicitly specified
# in deviceinfo
if "flash_dtbo" in flasher_actions and \
(args.deviceinfo["flash_fastboot_partition_dtbo"] or
args.deviceinfo["flash_heimdall_partition_dtbo"]):
(deviceinfo["flash_fastboot_partition_dtbo"] or
deviceinfo["flash_heimdall_partition_dtbo"]):
logging.info("* pmbootstrap flasher flash_dtbo")
logging.info(" Flashes dtbo image.")
@ -963,14 +952,14 @@ def print_flash_info(args: PmbArgs):
# works even when partitions are split or installing to disk. This is not
# possible if the flash method requires split partitions.
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(" Flashes the kernel + initramfs to your device:")
if requires_split:
logging.info(f" {Chroot.native()}/home/pmos/rootfs/"
f"{args.devicesdhbfvhubsud}-boot.img")
f"{device}-boot.img")
else:
logging.info(f" {Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud)}/boot")
logging.info(f" {Chroot(ChrootType.ROOTFS, device)}/boot")
if "boot" in flasher_actions:
logging.info(" (NOTE: " + method + " also supports booting"
@ -978,7 +967,7 @@ def print_flash_info(args: PmbArgs):
" Use 'pmbootstrap flasher boot' to do that.)")
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"
" flashing lk2nd. You should flash it before"
" flashing anything else. Use 'pmbootstrap flasher"
@ -990,11 +979,11 @@ def print_flash_info(args: PmbArgs):
" 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 ***")
suffix = "buildroot_" + args.deviceinfo["arch"]
mount_device_rootfs(args, Chroot.rootfs(args.devicesdhbfvhubsud))
pmb.install.recovery.create_zip(args, suffix)
chroot = Chroot(ChrootType.BUILDROOT, arch)
mount_device_rootfs(Chroot.rootfs(device))
pmb.install.recovery.create_zip(args, chroot, device)
# Flash 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
# specified, otherwise the separate boot image is not needed)
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}")
pmb.chroot.root(["rm", f"/home/pmos/rootfs/{img_boot}"])
# Disable root login
setup_login(args, chroot_installer)
setup_login(args, config, chroot_installer)
# Generate installer image
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}")'
' ***')
suffix = Chroot(ChrootType.ROOTFS, device)
pmb.chroot.init(suffix)
chroot = Chroot(ChrootType.ROOTFS, device)
pmb.chroot.init(chroot)
# Create user before installing packages, so post-install scripts of
# pmaports can figure out the username (legacy reasons: pmaports#820)
set_user(context.config)
@ -1215,7 +1204,7 @@ def create_device_rootfs(args: PmbArgs, step, steps):
install_packages += context.config.extra_packages.split(",")
if args.add:
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:
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
# selected for installation
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:
install_packages += [unlocker["pkgname"]]
else:
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_packages += get_recommends(args, install_packages)
# Explicitly call build on the install packages, to re-build them or any
# dependency, in case the version increased
if args.build_pkgs_on_install:
if config.build_pkgs_on_install:
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,
# because that doesn't always happen automatically yet, e.g. when the user
# installed a hook without pmbootstrap - see #69 for more info)
pmb.chroot.apk.install(install_packages, suffix)
flavor = pmb.chroot.other.kernel_flavor_installed(suffix)
pmb.chroot.initfs.build(args, flavor, suffix)
pmb.chroot.apk.install(install_packages, chroot)
flavor = pmb.chroot.other.kernel_flavor_installed(chroot)
pmb.chroot.initfs.build(flavor, chroot)
# Set the user password
setup_login(args, suffix)
setup_login(args, config, chroot)
# Set the keymap if the device requires it
setup_keymap(config)
# Set timezone
setup_timezone(config)
setup_timezone(chroot, config.timezone)
# Set locale
if locale_is_set:
# 10locale-pmos.sh gets sourced before 20locale.sh from
# alpine-baselayout by /etc/profile. Since they don't override the
# 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)}"
" > /etc/profile.d/10locale-pmos.sh"], suffix)
" > /etc/profile.d/10locale-pmos.sh"], chroot)
# 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)
disable_firewall(args)
if args.no_sshd:
disable_sshd(chroot)
if args.no_firewall:
disable_firewall(chroot)
def install(args: PmbArgs):
device = get_context().config.device
chroot = Chroot(ChrootType.ROOTFS, device)
deviceinfo = pmb.parse.deviceinfo()
# Sanity checks
sanity_check_boot_size()
if not args.android_recovery_zip and args.disk:
@ -1319,18 +1313,18 @@ def install(args: PmbArgs):
if args.no_image:
return
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:
# Runs install_system_image twice
install_on_device_installer(args, step, steps)
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)
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_firewall_info(args)
print_firewall_info(args.no_firewall, deviceinfo["arch"])
# Leave space before 'chroot still active' note
logging.info("")

View file

@ -23,7 +23,7 @@ def format_and_mount_boot(args: PmbArgs, device, boot_label):
ondev-prepare-internal-storage.sh in postmarketos-ondev.git!
"""
mountpoint = "/mnt/install/boot"
filesystem = args.deviceinfo["boot_filesystem"] or "ext2"
filesystem = pmb.parse.deviceinfo()["boot_filesystem"] or "ext2"
install_fsprogs(filesystem)
logging.info(f"(native) format {device} (boot, {filesystem}), mount to"
f" {mountpoint}")
@ -72,7 +72,7 @@ def format_luks_root(args: PmbArgs, device):
def get_root_filesystem(args: PmbArgs):
ret = args.filesystem or args.deviceinfo["root_filesystem"] or "ext4"
ret = args.filesystem or pmb.parse.deviceinfo()["root_filesystem"] or "ext4"
pmaports_cfg = pmb.config.pmaports.read_config()
supported = pmaports_cfg.get("supported_root_filesystems", "ext4")

View file

@ -42,7 +42,7 @@ def mount(args: PmbArgs, img_path: Path):
init()
losetup_cmd: List[PathString] = ["losetup", "-f", img_path]
sector_size = args.deviceinfo["rootfs_image_sector_size"]
sector_size = pmb.parse.deviceinfo()["rootfs_image_sector_size"]
if sector_size:
losetup_cmd += ["-b", str(int(sector_size))]

View file

@ -14,15 +14,15 @@ from pmb.core import Chroot
# FIXME (#2324): this function drops disk to a string because it's easier
# 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
:param layout: partition layout from get_partition_layout()
:param disk: path to disk block device (e.g. /dev/mmcblk0) or None
"""
if not disk:
img_path = Path("/home/pmos/rootfs") / f"{args.devicesdhbfvhubsud}.img"
disk = pmb.install.losetup.device_by_back_file(args, img_path)
img_path = Path("/home/pmos/rootfs") / f"{device}.img"
disk = pmb.install.losetup.device_by_back_file(img_path)
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},"
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
# sometimes "fails to inform the kernel". In case it really failed with
# partitioning, the follow-up mounting/formatting will not work, so it
# will stop there (see #463).
boot_part_start = 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 = [
["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)
"""
pmb.chroot.apk.install(["cgpt"], build=False)
pmb.chroot.apk.install(["cgpt"], Chroot.native(), build=False)
cgpt = {
'kpart_start': args.deviceinfo["cgpt_kpart_start"],
'kpart_size': args.deviceinfo["cgpt_kpart_size"],
'kpart_start': pmb.parse.deviceinfo()["cgpt_kpart_start"],
'kpart_size': pmb.parse.deviceinfo()["cgpt_kpart_size"],
}
# Convert to MB and print info
@ -178,7 +178,7 @@ def partition_cgpt(args: PmbArgs, layout, size_boot, size_reserve):
]
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)
root_size = str(int(dev_size) - int(s_root_start) - 33)

View file

@ -1,38 +1,40 @@
# Copyright 2023 Attila Szollosi
# SPDX-License-Identifier: GPL-3.0-or-later
from pathlib import Path
from pmb.core.chroot import Chroot
from pmb.helpers import logging
import pmb.chroot
import pmb.chroot.apk
import pmb.config.pmaports
from pmb.types import PmbArgs
import pmb.flasher
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.
"""
zip_root = Path("/var/lib/postmarketos-android-recovery-installer/")
rootfs = "/mnt/rootfs_" + args.devicesdhbfvhubsud
flavor = pmb.helpers.frontend._parse_flavor(args)
method = args.deviceinfo["flash_method"]
rootfs = "/mnt/rootfs_" + device
flavor = pmb.helpers.frontend._parse_flavor(device)
deviceinfo = pmb.parse.deviceinfo()
method = deviceinfo["flash_method"]
vars = pmb.flasher.variables(args, flavor, method)
# Install recovery installer package in buildroot
pmb.chroot.apk.install(args,
["postmarketos-android-recovery-installer"],
suffix)
pmb.chroot.apk.install(["postmarketos-android-recovery-installer"],
chroot)
logging.info(f"({suffix}) create recovery zip")
logging.info(f"({chroot}) create recovery zip")
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
options = {
"DEVICE": args.devicesdhbfvhubsud,
"DEVICE": device,
"FLASH_KERNEL": args.recovery_flash_kernel,
"ISOREC": method == "heimdall-isorec",
"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 "-"
# Write to a temporary file
config_temp = suffix / "tmp/install_options"
config_temp = chroot / "tmp/install_options"
with config_temp.open("w") as handle:
for key, value in options.items():
if isinstance(value, bool):
@ -69,6 +71,6 @@ def create_zip(args: PmbArgs, suffix):
["tar", "-prf", "rootfs.tar", "-C", "/", "./etc/apk/keys"],
# Compress with -1 for speed improvement
["gzip", "-f1", "rootfs.tar"],
["build-recovery-zip", args.devicesdhbfvhubsud]]
["build-recovery-zip", device]]
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
# SPDX-License-Identifier: GPL-3.0-or-later
from pmb.core.context import get_context
from pmb.helpers import logging
from pathlib import Path
import socket
import time
import pmb.chroot.run
import pmb.chroot
from pmb.types import PmbArgs
import pmb.helpers.run
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.
:param ip: IP address to serve nbd server for
:param port: port of nbd server
"""
pmb.chroot.apk.install(['nbd'])
pmb.chroot.apk.install(['nbd'], Chroot.native())
chroot = Chroot.native()
rootfs_path = Path("/mnt/pmbootstrap/netboot") / f"{args.devicesdhbfvhubsud}.img"
if not (chroot / rootfs_path).exists() or args.replace:
rootfs_path2 = Path("/home/pmos/rootfs") / f"{args.devicesdhbfvhubsud}.img"
rootfs_path = Path("/mnt/pmbootstrap/netboot") / f"{device}.img"
if not (chroot / rootfs_path).exists() or replace:
rootfs_path2 = Path("/home/pmos/rootfs") / f"{device}.img"
if not (chroot / rootfs_path2).exists():
raise RuntimeError("The rootfs has not been generated yet, please "
"run 'pmbootstrap install' first.")
if args.replace and not \
if replace and not \
pmb.helpers.cli.confirm(f"Are you sure you want to "
f"replace the rootfs for "
f"{args.devicesdhbfvhubsud}?"):
f"{device}?"):
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}"
f"/images_netboot/. The image will persist \"pmbootstrap "
f"zap\" for your convenience. Use \"pmbootstrap netboot "
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:
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
logging.info("Found postmarketOS device, serving image...")
pmb.chroot.run(
args, ["nbd-server", f"{ip}@{port}", rootfs_path, "-d"],
pmb.chroot.root(["nbd-server", f"{ip}@{port}", rootfs_path, "-d"],
check=False, disable_timeout=True)
logging.info("nbd-server quit. Connection lost?")
# On a reboot nbd-server will quit, but the IP address sticks around

View file

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

View file

@ -594,11 +594,8 @@ def arguments_ci(subparser):
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(
package for package in pmb.helpers.pmaports.get_list(args)
package for package in pmb.helpers.pmaports.get_list()
if package.startswith(prefix))
return packages

View file

@ -1,11 +1,12 @@
# Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
import os
from pmb.core.context import get_context
from pmb.helpers import logging
from pathlib import Path
from pmb.types import PmbArgs
import pmb.helpers.run
import pmb.chroot.run
import pmb.chroot
import pmb.chroot.other
import pmb.chroot.apk
from pmb.core import Chroot
@ -72,7 +73,7 @@ def get_qcdt_type(path):
return None
def bootimg(args: PmbArgs, path: Path):
def bootimg(path: Path):
if not path.exists():
raise RuntimeError(f"Could not find file '{path}'")
@ -93,7 +94,7 @@ def bootimg(args: PmbArgs, path: Path):
working_dir=temp_path,
output_return=True).rstrip()
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"
" proceeding anyway (-f specified)")
else:
@ -166,6 +167,6 @@ def bootimg(args: PmbArgs, path: Path):
output["cmdline"] = f.read().replace('\n', '')
# Cleanup
pmb.chroot.run.user(["rm", "-r", temp_path])
pmb.chroot.user(["rm", "-r", temp_path])
return output

View file

@ -230,7 +230,7 @@ def check_config(config_path, config_arch, pkgver, components_list=[],
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.

View file

@ -2,6 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
import subprocess
from typing import Sequence
from pmb.core.context import get_context
from pmb.helpers import logging
import os
from pathlib import Path
@ -24,12 +25,12 @@ import pmb.parse.cpuinfo
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
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():
logging.debug(f"Could not find rootfs: {path}")
raise RuntimeError("The rootfs has not been generated yet, please "
@ -37,14 +38,14 @@ def system_image(args: PmbArgs):
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.
: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(["chmod", "a+w", 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
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
"""
cmdline = args.deviceinfo["kernel_cmdline"]
cmdline = pmb.parse.deviceinfo()["kernel_cmdline"]
if 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)
chroot = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud)
chroot = Chroot(ChrootType.ROOTFS, device)
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}"
# Backwards compatibility with old mkinitfs (pma#660)
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"]
# 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"):
command += ["-enable-kvm"]
command += ["-cpu", "host"]
@ -315,7 +316,7 @@ def install_depends(args: PmbArgs, arch):
]
# 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":
depends.remove("qemu-hw-display-virtio-gpu")
depends.remove("qemu-hw-display-virtio-gpu-pci")
@ -332,22 +333,23 @@ def run(args: PmbArgs):
"""
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 "
"the QEMU device packages. Run 'pmbootstrap init' "
"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
if args.second_storage:
img_path_2nd = create_second_storage(args)
img_path_2nd = create_second_storage(args, device)
if not args.host_qemu:
install_depends(args, 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
# rootfs, which is owned by root

View file

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