pmb.config/install: add flexible provider selection for "pmbootstrap init" (MR 2132)
The provider selection for "pmbootstrap init" added in this commit is a flexible way to offer UI/device-specific configuration options in "pmbootstrap init", without hardcoding them in pmbootstrap. Instead, the options are defined entirely in pmaports using APK's virtual package provider mechanism. The code in pmbootstrap searches for available providers and displays them together with their pkgdesc. There are many possible use cases for this but I have tested two so far: 1. Selecting root provider (sudo vs doas). This can be defined entirely in postmarketos-base, without having to handle this specifically in pmbootstrap. $ pmbootstrap init [...] Available providers for postmarketos-root (2): * sudo: Use sudo to run root commands (**default**) * doas: Use doas (minimal replacement for sudo) to run root commands (Note: Does not support all functionality of sudo) Provider [default]: doas 2. Device-specific options. My main motivation for working on this feature is a new configuration option for the MSM8916-based devices. It allows more control about which firmware to enable: $ pmbootstrap init [...] Available providers for soc-qcom-msm8916-rproc (3): * all: Enable all remote processors (audio goes through modem) (default) * no-modem: Disable only modem (audio bypasses modem, ~80 MiB more RAM) * none: Disable all remote processors (no WiFi/BT/modem, ~90 MiB more RAM) Provider [default]: no-modem The configuration prompts show up dynamically by defining _pmb_select="<virtual packages>" in postmarketos-base, a UI PKGBUILD or the device APKBUILD. Selecting "default" (just pressing enter) means that no provider is selected. This allows APK to choose it automatically based on the "provider_priority". It also provides compatibility with existing installation; APK will just choose the default provider when upgrading. The selection can still be changed after installation by installing another provider using "apk". Note that at the end this is just a more convenient interface for the already existing "extra packages" prompt. When using pmbootstrap in automated scripts the providers (e.g. "postmarketos-root-doas") can be simply selected through the existing "extra_packages" option.
This commit is contained in:
parent
47645f41b1
commit
8ace36113c
|
@ -572,6 +572,11 @@ apkbuild_package_attributes = {
|
|||
# UI meta-packages can specify groups to which the user must be added
|
||||
# to access specific hardware such as LED indicators.
|
||||
"_pmb_groups": {"array": True},
|
||||
|
||||
# postmarketos-base, UI and device packages can use _pmb_select to provide
|
||||
# additional configuration options in "pmbootstrap init" that allow
|
||||
# selecting alternative providers for a virtual APK package.
|
||||
"_pmb_select": {"array": True},
|
||||
}
|
||||
|
||||
# Variables in APKBUILD files that get parsed
|
||||
|
|
|
@ -14,6 +14,7 @@ import pmb.helpers.devices
|
|||
import pmb.helpers.http
|
||||
import pmb.helpers.logging
|
||||
import pmb.helpers.other
|
||||
import pmb.helpers.pmaports
|
||||
import pmb.helpers.run
|
||||
import pmb.helpers.ui
|
||||
import pmb.chroot.zap
|
||||
|
@ -206,6 +207,80 @@ def ask_for_timezone(args):
|
|||
return "GMT"
|
||||
|
||||
|
||||
def ask_for_provider_select(args, apkbuild, providers_cfg):
|
||||
"""
|
||||
Ask for selectable providers that are specified using "_pmb_select"
|
||||
in a APKBUILD.
|
||||
|
||||
:param apkbuild: the APKBUILD with the _pmb_select
|
||||
:param providers_cfg: the configuration section with previously selected
|
||||
providers. Updated with new providers after selection
|
||||
"""
|
||||
for select in apkbuild["_pmb_select"]:
|
||||
providers = pmb.helpers.pmaports.find_providers(args, select)
|
||||
logging.info(f"Available providers for {select} ({len(providers)}):")
|
||||
|
||||
has_default = False
|
||||
providers_short = {}
|
||||
last_selected = providers_cfg.get(select, 'default')
|
||||
|
||||
for pkgname, pkg in providers:
|
||||
# Strip provider prefix if possible
|
||||
short = pkgname
|
||||
if short.startswith(f'{select}-'):
|
||||
short = short[len(f"{select}-"):]
|
||||
|
||||
# Allow selecting the package using both short and long name
|
||||
providers_short[pkgname] = pkgname
|
||||
providers_short[short] = pkgname
|
||||
|
||||
if pkgname == last_selected:
|
||||
last_selected = short
|
||||
|
||||
if not has_default and pkg.get('provider_priority', 0) != 0:
|
||||
# Display as default provider
|
||||
styles = pmb.config.styles
|
||||
logging.info(f"* {short}: {pkg['pkgdesc']} "
|
||||
f"{styles['BOLD']}(default){styles['END']}")
|
||||
has_default = True
|
||||
else:
|
||||
logging.info(f"* {short}: {pkg['pkgdesc']}")
|
||||
|
||||
while True:
|
||||
ret = pmb.helpers.cli.ask("Provider", None, last_selected, True,
|
||||
complete=providers_short.keys())
|
||||
|
||||
if has_default and ret == 'default':
|
||||
# Selecting default means to not select any provider explicitly
|
||||
# In other words, apk chooses it automatically based on
|
||||
# "provider_priority"
|
||||
if select in providers_cfg:
|
||||
del providers_cfg[select]
|
||||
break
|
||||
if ret in providers_short:
|
||||
providers_cfg[select] = providers_short[ret]
|
||||
break
|
||||
logging.fatal("ERROR: Invalid provider specified, please type in"
|
||||
" one from the list above.")
|
||||
|
||||
|
||||
def ask_for_provider_select_pkg(args, pkgname, providers_cfg):
|
||||
"""
|
||||
Look up the APKBUILD for the specified pkgname and ask for selectable
|
||||
providers that are specified using "_pmb_select".
|
||||
|
||||
:param pkgname: name of the package to search APKBUILD for
|
||||
:param providers_cfg: the configuration section with previously selected
|
||||
providers. Updated with new providers after selection
|
||||
"""
|
||||
apkbuild = pmb.helpers.pmaports.get(args, pkgname,
|
||||
subpackages=False, must_exist=False)
|
||||
if not apkbuild:
|
||||
return
|
||||
|
||||
ask_for_provider_select(args, apkbuild, providers_cfg)
|
||||
|
||||
|
||||
def ask_for_device_kernel(args, device):
|
||||
"""
|
||||
Ask for the kernel that should be used with the device.
|
||||
|
@ -567,6 +642,10 @@ def frontend(args):
|
|||
cfg["pmbootstrap"]["nonfree_userland"] = str(nonfree["userland"])
|
||||
|
||||
info = pmb.parse.deviceinfo(args, device)
|
||||
apkbuild_path = pmb.helpers.devices.find_path(args, device, 'APKBUILD')
|
||||
if apkbuild_path:
|
||||
apkbuild = pmb.parse.apkbuild(args, apkbuild_path)
|
||||
ask_for_provider_select(args, apkbuild, cfg["providers"])
|
||||
|
||||
# Device keymap
|
||||
if device_exists:
|
||||
|
@ -576,10 +655,15 @@ def frontend(args):
|
|||
cfg["pmbootstrap"]["user"] = pmb.helpers.cli.ask("Username", None,
|
||||
args.user, False,
|
||||
"[a-z_][a-z0-9_-]*")
|
||||
|
||||
ask_for_provider_select_pkg(args, "postmarketos-base", cfg["providers"])
|
||||
|
||||
# UI and various build options
|
||||
ui = ask_for_ui(args, info)
|
||||
cfg["pmbootstrap"]["ui"] = ui
|
||||
cfg["pmbootstrap"]["ui_extras"] = str(ask_for_ui_extras(args, ui))
|
||||
ask_for_provider_select_pkg(args, f"postmarketos-ui-{ui}",
|
||||
cfg["providers"])
|
||||
ask_for_additional_options(args, cfg)
|
||||
|
||||
# Extra packages to be installed to rootfs
|
||||
|
|
|
@ -13,6 +13,8 @@ def load(args):
|
|||
|
||||
if "pmbootstrap" not in cfg:
|
||||
cfg["pmbootstrap"] = {}
|
||||
if "providers" not in cfg:
|
||||
cfg["providers"] = {}
|
||||
|
||||
for key in pmb.config.defaults:
|
||||
if key in pmb.config.config_keys and key not in cfg["pmbootstrap"]:
|
||||
|
|
|
@ -29,6 +29,7 @@ def merge_with_args(args):
|
|||
if isinstance(default, bool):
|
||||
value = (value.lower() == "true")
|
||||
setattr(args, key, value)
|
||||
setattr(args, 'selected_providers', cfg['providers'])
|
||||
|
||||
# Use defaults from pmb.config.defaults
|
||||
for key, value in pmb.config.defaults.items():
|
||||
|
|
|
@ -219,6 +219,30 @@ def get(args, pkgname, must_exist=True, subpackages=True):
|
|||
return None
|
||||
|
||||
|
||||
def find_providers(args, provide):
|
||||
"""
|
||||
Search for providers of the specified (virtual) package in pmaports.
|
||||
Note: Currently only providers from a single APKBUILD are returned.
|
||||
|
||||
:param provide: the (virtual) package to search providers for
|
||||
:returns: tuple list (pkgname, apkbuild_pkg) with providers, sorted by
|
||||
provider_priority. The provider with the highest priority
|
||||
(which would be selected by default) comes first.
|
||||
"""
|
||||
|
||||
providers = {}
|
||||
|
||||
apkbuild = get(args, provide)
|
||||
for subpkgname, subpkg in apkbuild["subpackages"].items():
|
||||
for provides in subpkg["provides"]:
|
||||
# Strip provides version (=$pkgver-r$pkgrel)
|
||||
if provides.split("=", 1)[0] == provide:
|
||||
providers[subpkgname] = subpkg
|
||||
|
||||
return sorted(providers.items(), reverse=True,
|
||||
key=lambda p: p[1].get('provider_priority', 0))
|
||||
|
||||
|
||||
def get_repo(args, pkgname, must_exist=True):
|
||||
""" Get the repository folder of an aport.
|
||||
|
||||
|
|
|
@ -789,6 +789,24 @@ def install_on_device_installer(args, step, steps):
|
|||
boot_label, "pmOS_install", args.split, args.sdcard)
|
||||
|
||||
|
||||
def get_selected_providers(args, packages):
|
||||
"""
|
||||
Look through the specified packages and see which providers were selected
|
||||
in "pmbootstrap init". Install those as extra packages to select them
|
||||
instead of the default provider.
|
||||
|
||||
:param packages: the packages that have selectable providers (_pmb_select)
|
||||
:return: additional provider packages to install
|
||||
"""
|
||||
providers = []
|
||||
for p in packages:
|
||||
apkbuild = pmb.helpers.pmaports.get(args, p, subpackages=False)
|
||||
for select in apkbuild['_pmb_select']:
|
||||
if select in args.selected_providers:
|
||||
providers.append(args.selected_providers[select])
|
||||
return providers
|
||||
|
||||
|
||||
def create_device_rootfs(args, step, steps):
|
||||
# List all packages to be installed (including the ones specified by --add)
|
||||
# and upgrade the installed packages/apkindexes
|
||||
|
@ -802,14 +820,19 @@ def create_device_rootfs(args, step, steps):
|
|||
|
||||
# Fill install_packages
|
||||
install_packages = (pmb.config.install_device_packages +
|
||||
["device-" + args.device] +
|
||||
get_kernel_package(args, args.device) +
|
||||
get_nonfree_packages(args, args.device))
|
||||
["device-" + args.device])
|
||||
if not args.install_base:
|
||||
install_packages = [p for p in install_packages
|
||||
if p != "postmarketos-base"]
|
||||
if args.ui.lower() != "none":
|
||||
install_packages += ["postmarketos-ui-" + args.ui]
|
||||
|
||||
# Add additional providers of base/device/UI package
|
||||
install_packages += get_selected_providers(args, install_packages)
|
||||
|
||||
install_packages += get_kernel_package(args, args.device)
|
||||
install_packages += get_nonfree_packages(args, args.device)
|
||||
if args.ui.lower() != "none":
|
||||
if args.ui_extras:
|
||||
install_packages += ["postmarketos-ui-" + args.ui + "-extras"]
|
||||
if args.install_recommends:
|
||||
|
|
Loading…
Reference in New Issue