Compare commits

...

27 Commits

Author SHA1 Message Date
Caleb Connolly 4c4bd77c87
install: support password as cmdline arg (MR 2125)
So I can finally run `pmbootstrap install --password 147147` and go and
make a cup of tea.

Based on MR 1919.

Co-Authored-By: Oliver Smith <ollieparanoid@postmarketos.org>
2021-11-06 22:45:00 +01:00
Oliver Smith 498738abcc
Prepare 1.39.0 release 2021-11-06 21:06:53 +01:00
Ultracoolguy 54afa35d70
Add 'flasher flash_lk2nd' command (MR 2089)
Many Qualcomm devices need this second bootloader for booting a mainline
kernel. Add an option to flash one if it exists.
2021-11-06 20:51:58 +01:00
Luca Weiss f4990cfc7a
aportupgrade: support gitlab.gnome.org (MR 2119)
Also sort the gitlab hosts alphabetically
2021-11-06 20:31:25 +01:00
Luca Weiss f996f3ed5a
aportupgrade: prefer explicit package mapping (MR 2119)
This solves some edge cases with packages like 'date', 'fmt' and others.
2021-11-06 20:31:19 +01:00
Oliver Smith 6557e6892a
kconfig argcomplete: support full pkgname (MR 2134)
Support full linux-* package names in argument completion for
"pmbootstrap kconfig ..." command-lines and get rid of related PROTIP
messages:
  PROTIP: You can simply do 'pmbootstrap kconfig check postmarketos-allwinner'

This improves consistency, as in other places we expect the user to
supply full package names as well (e.g. pmbootstrap build).
2021-11-06 15:23:37 +01:00
Oliver Smith 152bd6753c
arguments: fix compat with argcomplete 1.8 (MR 2134)
When running pmbootstrap on debian bullseye with the distro's
python3-argcomplete 1.8 from 2017, tab completion was broken. After
disabling stderr redirect to /dev/null, the error appeared:
  TypeError: package_completer() missing 1 required positional argument: 'parser'

Support this ancient version of argcomplete too by setting None as
default value for parser (we don't use it anyway).

Related: https://wiki.postmarketos.org/wiki/Pmbootstrap_development_guide#Debugging_tab_completion_.28argparse.29
2021-11-06 15:23:31 +01:00
Minecrell 8ace36113c
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.
2021-11-06 15:04:34 +01:00
Minecrell 47645f41b1
pmb.parse._apkbuild: parse provider_priority as int (MR 2132) 2021-11-06 15:04:25 +01:00
Minecrell 19b232969d
pmb.helpers.pmaports: search for provides in both root and subpackages (MR 2132)
At the moment, "provides" are only checked in the root package and not
in subpackages of APKBUILDs. Fix this by looking through the subpackages
as well.
2021-11-06 15:04:25 +01:00
Minecrell 5ed807c064
pmb.helpers.pmaports: parse guessed APKBUILD first for speed up (MR 2132)
At the moment we have to parse all APKBUILDs to find subpackages,
even if they are guessed easily shortly after. To speed this up,
let's guess first but verify the guess by only parsing that particular
APKBUILD. If the subpackage/provides is in there we seem to have found it.
2021-11-06 15:04:25 +01:00
Minecrell eb3e38d15c
pmb.helpers.pmaports: separate finding pkg in APKBUILD to function (MR 2132)
Make it possible to call this in a different place as well by placing
it in a separate function.
2021-11-06 15:04:25 +01:00
Minecrell 7246c32539
pmb.install: move install_recommends check into _install.py (MR 2132)
The current install code looks a bit confusing, there is an existing
if statement for the ui and ui-extras package but the recommended
packages are already installed before with a check in a completely
different file. Make this a bit more clear by moving this to the
ui if statement instead.
2021-11-06 15:04:25 +01:00
Minecrell 9d724d5d3d
pmb.config.init: parse deviceinfo only once (MR 2132)
Instead of parsing the deviceinfo over and over again, parse it once
and pass it to all the functions that make use of it.
2021-11-06 15:04:20 +01:00
Oliver Smith 05f257295d
pmb.config: add comment: kconfig check value types (MR 2133) 2021-11-06 14:28:54 +01:00
Oliver Smith aead36d5ac
pmbootstrap kconfig check: add iwd check (MR 2133)
iwd seems like a promising alternative to wpa_supplicant. It uses crypto
implementations from the kernel, so let's make kconfig check aware of
the options it needs.
2021-11-06 14:28:53 +01:00
Oliver Smith d856e21673
pmb.config.necessary_kconfig_*: tweak comments (MR 2133)
Improve the comments for consistency and to give a little more info than
just repeating the variable name.
2021-11-06 14:28:53 +01:00
Oliver Smith 93e7a1d876
pmbootstrap kconfig check: add apparmor check (MR 2133)
SECURITY_APPARMOR_BOOTPARAM_VALUE was required to enable it by default
until 5.1 where the option was removed.

Related: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=0102fb83f90050b86ce37aec810ea17bb4448e0c
Related: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/security/Kconfig?id=fe91c4725aeed35023ba4f7a1e1adfebb6878c23#n285
2021-11-06 14:28:53 +01:00
Oliver Smith dcedc4bc1f
kconfig check: support strings too (MR 2133)
Extend the kconfig check code to not only support booleans and arrays,
but also strings. This will be used for CONFIG_LSM with apparmor where
it's important that "apparmor" has a certain spot in the list.
2021-11-06 14:28:53 +01:00
Oliver Smith 6b8fa93d37
pmb.parse.kconfig.check: git friendly arguments (MR 2133)
Put each component argument into a separate line, as these get changed
whenever adding a new kernel config check. I'm about to add two new
ones.
2021-11-06 14:28:49 +01:00
Alexey Min 0664a38190
pmb.qemu.run: workaround for new mkinitfs requirements (MR 2138)
It's been 3 months since we switched to new mkinitfs
and we are still fixing consequences.

linux-lts and linux-virt used by qemu-amd64 device package
install vmlinuz-lts (vmlinuz-virt). And qemu run cmdline
passed -kernel vmlinuz which makes it impossible to run
qemu without this change.

With this change proper kernel arg is passed to qemu.

Co-Authored-By: Oliver Smith <ollieparanoid@postmarketos.org>
Signed-off-by: Alexey Min <alexeymin@postmarketos.org>
2021-11-06 13:36:34 +01:00
博麗霊夢 7541a0ca36
helper/envkernel.sh: fix for running envkernel on aarch64 host (MR 2135)
aarch64 is not recongised as arm64, and envkernel thinks that
cross-compiler is needed, while it shouldn't be.

Fixes #2075
2021-11-02 16:23:33 +03:00
Jami Kettunen 931675450d
pmb/config: check CRYPTO_AES and INPUT_EVDEV configs required for FDE (MR 2137)
AES is needed on top of XTS to unlock a disk with FDE enabled and evdev
is used in osk-sdl to input the passphrase.
2021-11-01 23:07:05 +03:00
bo41 caf7973e24
args.arch_native: remove (MR 2130)
Replace "args.arch_native" with the direct function call in order to
avoid passing "args" to all functions. This is a step to get rid of this
args-passed-to-all-functions pattern in pmbootstrap.
2021-10-24 14:34:30 +02:00
Oliver Smith f2966e62ae
pmb.parse.arch.alpine_native: remove unused ret (MR 2130) 2021-10-24 14:34:30 +02:00
bo41 a8d425554c
remove unused args argument (MR 2130) 2021-10-24 14:34:26 +02:00
Martijn Braam 896879e89a
pmb: data: keys: sync with Alpine (MR 2131)
Alpine has new keys for the riscv64 builder and new 4096 bit
builder keys. This fixes CI again.
2021-10-19 01:44:35 +02:00
61 changed files with 723 additions and 220 deletions

View File

@ -120,7 +120,8 @@ initialize_chroot() {
# shellcheck disable=SC3057
arch_substr="${host_arch:0:3}"
if [ "$arch" = "$host_arch" ] || \
{ [ "$arch_substr" = "arm" ] && [ "$arch_substr" = "$arch" ]; }; then
{ [ "$arch_substr" = "arm" ] && [ "$arch_substr" = "$arch" ]; } || \
{ [ "$arch" = "arm64" ] && [ "$host_arch" = "aarch64" ]; }; then
need_cross_compiler=0
fi

View File

@ -24,7 +24,7 @@ def main():
os.umask(0o22)
# Sanity checks
other.check_grsec(args)
other.check_grsec()
if not args.as_root and os.geteuid() == 0:
raise RuntimeError("Do not run pmbootstrap as root!")

View File

@ -15,7 +15,7 @@ def generate(args, pkgname):
fields = {
"pkgname": pkgname,
"pkgdesc": f"Tools necessary to build programs for {arch} targets",
"arch": args.arch_native,
"arch": pmb.config.arch_native,
"makedepends_build": "",
"makedepends_host": "",
"makedepends": "gettext libtool autoconf automake bison texinfo",

View File

@ -47,7 +47,7 @@ def generate(args, pkgname):
url="http://busybox.net"
license="GPL2"
arch="{args.arch_native}"
arch="{pmb.config.arch_native}"
options="!check !strip"
pkgdesc="Statically linked Busybox for $_arch"
_target="$(arch_to_hostspec $_arch)"

View File

@ -28,7 +28,7 @@ def generate(args, pkgname):
fields = {
"pkgname": pkgname,
"pkgdesc": f"Stage2 cross-compiler for {arch}",
"arch": args.arch_native,
"arch": pmb.config.arch_native,
"depends": f"isl binutils-{arch} mpc1",
"makedepends_build": "gcc g++ bison flex texinfo gawk zip"
" gmp-dev mpfr-dev mpc1-dev zlib-dev",

View File

@ -43,7 +43,7 @@ def generate(args, pkgname):
pkgdesc="GRUB $_arch EFI files for every architecture"
url="https://www.gnu.org/software/grub/"
license="GPL-3.0-or-later"
arch="{args.arch_native}"
arch="{pmb.config.arch_native}"
source="grub-efi-$pkgver-r$pkgrel-$_arch-{mirrordir}.apk::$_mirror/{mirrordir}/main/$_arch/grub-efi-$pkgver-r$pkgrel.apk"
package() {{

View File

@ -42,7 +42,7 @@ def generate(args, pkgname):
pkgname={pkgname}
pkgver={pkgver}
pkgrel={pkgrel}
arch="{args.arch_native}"
arch="{pmb.config.arch_native}"
subpackages="musl-dev-{arch}:package_dev"
_arch="{arch}"

View File

@ -196,7 +196,7 @@ def init_buildenv(args, apkbuild, arch, strict=False, force=False, cross=None,
depends_arch = arch
if cross == "native":
depends_arch = args.arch_native
depends_arch = pmb.config.arch_native
# Build dependencies
depends, built = build_depends(args, apkbuild, depends_arch, strict)
@ -236,7 +236,7 @@ def get_gcc_version(args, arch):
:returns: a string like "6.4.0-r5"
"""
return pmb.parse.apkindex.package(args, "gcc-" + arch,
args.arch_native)["version"]
pmb.config.arch_native)["version"]
def get_pkgver(original_pkgver, original_source=False, now=None):
@ -493,7 +493,7 @@ def package(args, pkgname, arch=None, force=False, strict=False,
output path relative to the packages folder ("armhf/ab-1-r2.apk")
"""
# Once per session is enough
arch = arch or args.arch_native
arch = arch or pmb.config.arch_native
if skip_already_built(args, pkgname, arch):
return
@ -505,7 +505,7 @@ def package(args, pkgname, arch=None, force=False, strict=False,
# Detect the build environment (skip unnecessary builds)
if not check_build_for_arch(args, pkgname, arch):
return
suffix = pmb.build.autodetect.suffix(args, apkbuild, arch)
suffix = pmb.build.autodetect.suffix(apkbuild, arch)
cross = pmb.build.autodetect.crosscompile(args, apkbuild, arch, suffix)
if not init_buildenv(args, apkbuild, arch, strict, force, cross, suffix,
skip_init_buildenv, src):

View File

@ -50,8 +50,10 @@ def arch(args, pkgname):
apkbuild = pmb.parse.apkbuild(args, aport + "/APKBUILD")
arches = apkbuild["arch"]
if "noarch" in arches or "all" in arches or args.arch_native in arches:
return args.arch_native
if ("noarch" in arches or
"all" in arches or
pmb.config.arch_native in arches):
return pmb.config.arch_native
arch_device = args.deviceinfo["arch"]
if arch_device in arches:
@ -63,8 +65,8 @@ def arch(args, pkgname):
return None
def suffix(args, apkbuild, arch):
if arch == args.arch_native:
def suffix(apkbuild, arch):
if arch == pmb.config.arch_native:
return "native"
if "pmb:cross-native" in apkbuild["options"]:
@ -79,7 +81,7 @@ def crosscompile(args, apkbuild, arch, suffix):
"""
if not args.cross:
return None
if not pmb.parse.arch.cpu_emulation_required(args, arch):
if not pmb.parse.arch.cpu_emulation_required(arch):
return None
if suffix == "native":
return "native"

View File

@ -12,7 +12,7 @@ import pmb.helpers.pmaports
import pmb.parse
def match_kbuild_out(args, word):
def match_kbuild_out(word):
"""
Look for paths in the following formats:
"<prefix>/<kbuild_out>/arch/<arch>/boot"
@ -47,7 +47,7 @@ def match_kbuild_out(args, word):
return "" if out_dir is None else out_dir.strip("/")
def find_kbuild_output_dir(args, function_body):
def find_kbuild_output_dir(function_body):
"""
Guess what the kernel build output directory is. Parses each line of the
function word by word, looking for paths which contain the kbuild output
@ -61,7 +61,7 @@ def find_kbuild_output_dir(args, function_body):
guesses = []
for line in function_body:
for item in line.split():
kbuild_out = match_kbuild_out(args, item)
kbuild_out = match_kbuild_out(item)
if kbuild_out is not None:
guesses.append(kbuild_out)
break
@ -142,7 +142,7 @@ def run_abuild(args, pkgname, arch, apkbuild_path, kbuild_out):
# Create the apk package
env = {"CARCH": arch,
"CHOST": arch,
"CBUILD": args.arch_native,
"CBUILD": pmb.config.arch_native,
"SUDO_APK": "abuild-apk --no-progress"}
cmd = ["abuild", "rootpkg"]
pmb.chroot.user(args, cmd, working_dir=build_path, env=env)
@ -176,12 +176,12 @@ def package_kernel(args):
kbuild_out = apkbuild["_outdir"]
else:
function_body = pmb.parse.function_body(aport + "/APKBUILD", "package")
kbuild_out = find_kbuild_output_dir(args, function_body)
suffix = pmb.build.autodetect.suffix(args, apkbuild, arch)
kbuild_out = find_kbuild_output_dir(function_body)
suffix = pmb.build.autodetect.suffix(apkbuild, arch)
# Install package dependencies
depends, _ = pmb.build._package.build_depends(
args, apkbuild, args.arch_native, strict=False)
args, apkbuild, pmb.config.arch_native, strict=False)
pmb.build.init(args, suffix)
pmb.chroot.apk.install(args, depends, suffix)

View File

@ -14,7 +14,7 @@ import pmb.helpers.run
import pmb.parse
def get_arch(args, apkbuild):
def get_arch(apkbuild):
"""
Take the architecture from the APKBUILD or complain if it's ambiguous. This
function only gets called if --arch is not set.
@ -81,17 +81,15 @@ def get_outputdir(args, pkgname, apkbuild):
def menuconfig(args, pkgname, use_oldconfig):
# Pkgname: allow omitting "linux-" prefix
if pkgname.startswith("linux-"):
pkgname_ = pkgname.split("linux-")[1]
logging.info(f"PROTIP: You can simply do 'pmbootstrap kconfig "
f"{args.action_kconfig} {pkgname_}'")
pkgname = pkgname.split("linux-")[1]
else:
pkgname = "linux-" + pkgname
# Read apkbuild
aport = pmb.helpers.pmaports.find(args, pkgname)
apkbuild = pmb.parse.apkbuild(args, aport + "/APKBUILD")
arch = args.arch or get_arch(args, apkbuild)
suffix = pmb.build.autodetect.suffix(args, apkbuild, arch)
arch = args.arch or get_arch(apkbuild)
suffix = pmb.build.autodetect.suffix(apkbuild, arch)
cross = pmb.build.autodetect.crosscompile(args, apkbuild, arch, suffix)
hostspec = pmb.parse.arch.alpine_to_hostspec(arch)

View File

@ -141,7 +141,7 @@ def download(args, file):
"""
channel_cfg = pmb.config.pmaports.read_config_channel(args)
mirrordir = channel_cfg["mirrordir_alpine"]
base_url = f"{args.mirror_alpine}{mirrordir}/main/{args.arch_native}"
base_url = f"{args.mirror_alpine}{mirrordir}/main/{pmb.config.arch_native}"
return pmb.helpers.http.download(args, f"{base_url}/{file}", file)

View File

@ -33,7 +33,7 @@ def register(args, arch):
if is_registered(arch_qemu):
return
info = pmb.parse.binfmt_info(args, arch_qemu)
info = pmb.parse.binfmt_info(arch_qemu)
# Build registration string
# https://en.wikipedia.org/wiki/Binfmt_misc

View File

@ -43,7 +43,7 @@ def mark_in_chroot(args, suffix="native"):
def setup_qemu_emulation(args, suffix):
arch = pmb.parse.arch.from_chroot_suffix(args, suffix)
if not pmb.parse.arch.cpu_emulation_required(args, arch):
if not pmb.parse.arch.cpu_emulation_required(arch):
return
chroot = f"{args.work}/chroot_{suffix}"

View File

@ -90,6 +90,6 @@ def shutdown(args, only_install_related=False):
# Clean up the rest
for arch in pmb.config.build_device_architectures:
if pmb.parse.arch.cpu_emulation_required(args, arch):
if pmb.parse.arch.cpu_emulation_required(arch):
pmb.chroot.binfmt.unregister(args, arch)
logging.debug("Shutdown complete")

View File

@ -106,7 +106,7 @@ def zap_pkgs_local_mismatch(args, confirm=True, dry=False):
pattern = f"{args.work}/packages/{channel}/*/APKINDEX.tar.gz"
for apkindex_path in glob.glob(pattern):
# Delete packages without same version in aports
blocks = pmb.parse.apkindex.parse_blocks(args, apkindex_path)
blocks = pmb.parse.apkindex.parse_blocks(apkindex_path)
for block in blocks:
pkgname = block["pkgname"]
origin = block["origin"]
@ -158,7 +158,9 @@ def zap_pkgs_online_mismatch(args, confirm=True, dry=False):
# Iterate over existing apk caches
for path in paths:
arch = os.path.basename(path).split("_", 2)[2]
suffix = "native" if arch == args.arch_native else f"buildroot_{arch}"
suffix = f"buildroot_{arch}"
if arch == pmb.config.arch_native:
suffix = "native"
# Clean the cache with apk
logging.info(f"({suffix}) apk -v cache clean")

View File

@ -2,6 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
import multiprocessing
import os
import pmb.parse.arch
import sys
#
@ -15,9 +16,10 @@ from pmb.config.merge_with_args import merge_with_args
#
# Exported variables (internal configuration)
#
version = "1.38.0"
version = "1.39.0"
pmb_src = os.path.normpath(os.path.realpath(__file__) + "/../../..")
apk_keys_path = pmb_src + "/pmb/data/keys"
arch_native = pmb.parse.arch.alpine_native()
# apk-tools minimum version
# https://pkgs.alpinelinux.org/packages?name=apk-tools&branch=edge
@ -256,6 +258,21 @@ build_device_architectures = ["armhf", "armv7", "aarch64", "x86_64", "x86"]
# for the first time
build_packages = ["abuild", "build-base", "ccache", "git"]
#
# KCONFIG CHECK
#
# Implemented value types:
# - boolean (e.g. '"ANDROID_PARANOID_NETWORK": False'):
# - False: disabled
# - True: enabled, either as module or built-in
# - array (e.g. '"ANDROID_BINDER_DEVICES": ["binder", "hwbinder"]'):
# - each element of the array must be contained in the kernel config string,
# in any order. The example above would accept the following in the config:
# CONFIG_ANDROID_BINDER_DEVICES="hwbinder,vndbinder,binder"
# - string (e.g. '"LSM": "lockdown,yama,loadpin,safesetid,integrity"'):
# - the value in the kernel config must be the same as the given string. Use
# this e.g. if the order of the elements is important.
# Necessary kernel config options
necessary_kconfig_options = {
">=0.0.0": { # all versions
@ -263,9 +280,11 @@ necessary_kconfig_options = {
"ANDROID_PARANOID_NETWORK": False,
"BLK_DEV_INITRD": True,
"CGROUPS": True,
"CRYPTO_AES": True,
"CRYPTO_XTS": True,
"DEVTMPFS": True,
"DM_CRYPT": True,
"INPUT_EVDEV": True,
"EXT4_FS": True,
"KINETO_GAN": False,
"PFT": False,
@ -296,7 +315,7 @@ necessary_kconfig_options = {
}
}
# Necessary anbox kernel config options
# Necessary anbox/waydroid kernel config options (android app support)
necessary_kconfig_options_anbox = {
">=0.0.0": { # all versions
"all": { # all arches
@ -328,7 +347,55 @@ necessary_kconfig_options_anbox = {
}
}
# Necessary nftables kernel config options
# Necessary apparmor kernel config options (mandatory access control)
# LSM: the value that "config LSM" sets in security/Kconfig, if
# DEFAULT_SECURITY_APPARMOR is set (and other DEFAULT_SECURITY_* are unset).
necessary_kconfig_options_apparmor = {
">=0.0.0": { # all versions
"all": { # all arches
"AUDIT": True,
"DEFAULT_SECURITY_APPARMOR": True,
"LSM": "landlock,lockdown,yama,loadpin,safesetid,integrity,"
"apparmor,selinux,smack,tomoyo,bpf",
"SECURITY_APPARMOR": True,
},
},
"<5.1": {
"all": {
"SECURITY_APPARMOR_BOOTPARAM_VALUE": True,
},
},
}
# Necessary iwd kernel config options (inet wireless daemon)
# Obtained from 'grep ADD_MISSING src/main.c' in iwd.git
necessary_kconfig_options_iwd = {
">=0.0.0": { # all versions
"all": { # all arches
"ASYMMETRIC_KEY_TYPE": True,
"ASYMMETRIC_PUBLIC_KEY_SUBTYPE": True,
"CRYPTO_AES": True,
"CRYPTO_CBC": True,
"CRYPTO_CMAC": True,
"CRYPTO_DES": True,
"CRYPTO_ECB": True,
"CRYPTO_HMAC": True,
"CRYPTO_MD5": True,
"CRYPTO_SHA1": True,
"CRYPTO_SHA256": True,
"CRYPTO_SHA512": True,
"CRYPTO_USER_API_HASH": True,
"CRYPTO_USER_API_SKCIPHER": True,
"KEYS": True,
"KEY_DH_OPERATIONS": True,
"PKCS7_MESSAGE_PARSER": True,
"PKCS8_PRIVATE_KEY_PARSER": True,
"X509_CERTIFICATE_PARSER": True,
},
},
}
# Necessary nftables kernel config options (firewall)
necessary_kconfig_options_nftables = {
">=3.13.0": { # nftables support introduced here
"all": { # all arches
@ -470,6 +537,7 @@ necessary_kconfig_options_containers = {
},
}
# Necessary zram kernel config options (RAM disk with on-the-fly compression)
necessary_kconfig_options_zram = {
">=3.14.0": { # zram support introduced here
"all": { # all arches
@ -491,6 +559,7 @@ apkbuild_package_attributes = {
"pkgdesc": {},
"depends": {"array": True},
"provides": {"array": True},
"provider_priority": {"int": True},
"install": {"array": True},
# UI meta-packages can specify apps in "_pmb_recommends" to be explicitly
@ -503,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
@ -714,6 +788,8 @@ flashers = {
"$BOOT/dtbo.img"]],
"boot": [["fastboot", "--cmdline", "$KERNEL_CMDLINE",
"boot", "$BOOT/boot.img$FLAVOR"]],
"flash_lk2nd": [["fastboot", "flash", "$PARTITION_KERNEL",
"$BOOT/lk2nd.img"]]
},
},
# Some devices provide Fastboot but using Android boot images is not

View File

@ -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
@ -111,8 +112,7 @@ def ask_for_channel(args):
" from the list above.")
def ask_for_ui(args, device):
info = pmb.parse.deviceinfo(args, device)
def ask_for_ui(args, info):
ui_list = pmb.helpers.ui.list(args, info["arch"])
hidden_ui_count = 0
device_is_accelerated = info.get("gpu_accelerated") == "true"
@ -163,8 +163,7 @@ def ask_for_ui_extras(args, ui):
default=args.ui_extras)
def ask_for_keymaps(args, device):
info = pmb.parse.deviceinfo(args, device)
def ask_for_keymaps(args, info):
if "keymaps" not in info or info["keymaps"].strip() == "":
return ""
options = info["keymaps"].split(' ')
@ -208,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.
@ -568,18 +641,29 @@ def frontend(args):
cfg["pmbootstrap"]["nonfree_firmware"] = str(nonfree["firmware"])
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:
cfg["pmbootstrap"]["keymap"] = ask_for_keymaps(args, device)
cfg["pmbootstrap"]["keymap"] = ask_for_keymaps(args, info)
# Username
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, device)
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
@ -619,7 +703,7 @@ def frontend(args):
pmb.helpers.cli.confirm(
args, "Zap existing chroots to apply configuration?",
default=True)):
setattr(args, "deviceinfo", pmb.parse.deviceinfo(args, device=device))
setattr(args, "deviceinfo", info)
# Do not zap any existing packages or cache_http directories
pmb.chroot.zap(args, confirm=False)

View File

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

View File

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

View File

@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXEJ8uVwJPODshTkf2BH
pH5fVVDppOa974+IQJsZDmGd3Ny0dcd+WwYUhNFUW3bAfc3/egaMWCaprfaHn+oS
4ddbOFgbX8JCHdru/QMAAU0aEWSMybfJGA569c38fNUF/puX6XK/y0lD2SS3YQ/a
oJ5jb5eNrQGR1HHMAd0G9WC4JeZ6WkVTkrcOw55F00aUPGEjejreXBerhTyFdabo
dSfc1TILWIYD742Lkm82UBOPsOSdSfOdsMOOkSXxhdCJuCQQ70DHkw7Epy9r+X33
ybI4r1cARcV75OviyhD8CFhAlapLKaYnRFqFxlA515e6h8i8ih/v3MSEW17cCK0b
QwIDAQAB
-----END PUBLIC KEY-----

View File

@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwR4uJVtJOnOFGchnMW5Y
j5/waBdG1u5BTMlH+iQMcV5+VgWhmpZHJCBz3ocD+0IGk2I68S5TDOHec/GSC0lv
6R9o6F7h429GmgPgVKQsc8mPTPtbjJMuLLs4xKc+viCplXc0Nc0ZoHmCH4da6fCV
tdpHQjVe6F9zjdquZ4RjV6R6JTiN9v924dGMAkbW/xXmamtz51FzondKC52Gh8Mo
/oA0/T0KsCMCi7tb4QNQUYrf+Xcha9uus4ww1kWNZyfXJB87a2kORLiWMfs2IBBJ
TmZ2Fnk0JnHDb8Oknxd9PvJPT0mvyT8DA+KIAPqNvOjUXP4bnjEHJcoCP9S5HkGC
IQIDAQAB
-----END PUBLIC KEY-----

View File

@ -0,0 +1,14 @@
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAutQkua2CAig4VFSJ7v54
ALyu/J1WB3oni7qwCZD3veURw7HxpNAj9hR+S5N/pNeZgubQvJWyaPuQDm7PTs1+
tFGiYNfAsiibX6Rv0wci3M+z2XEVAeR9Vzg6v4qoofDyoTbovn2LztaNEjTkB+oK
tlvpNhg1zhou0jDVYFniEXvzjckxswHVb8cT0OMTKHALyLPrPOJzVtM9C1ew2Nnc
3848xLiApMu3NBk0JqfcS3Bo5Y2b1FRVBvdt+2gFoKZix1MnZdAEZ8xQzL/a0YS5
Hd0wj5+EEKHfOd3A75uPa/WQmA+o0cBFfrzm69QDcSJSwGpzWrD1ScH3AK8nWvoj
v7e9gukK/9yl1b4fQQ00vttwJPSgm9EnfPHLAtgXkRloI27H6/PuLoNvSAMQwuCD
hQRlyGLPBETKkHeodfLoULjhDi1K2gKJTMhtbnUcAA7nEphkMhPWkBpgFdrH+5z4
Lxy+3ek0cqcI7K68EtrffU8jtUj9LFTUC8dERaIBs7NgQ/LfDbDfGh9g6qVj1hZl
k9aaIPTm/xsi8v3u+0qaq7KzIBc9s59JOoA8TlpOaYdVgSQhHHLBaahOuAigH+VI
isbC9vmqsThF2QdDtQt37keuqoda2E6sL7PUvIyVXDRfwX7uMDjlzTxHTymvq2Ck
htBqojBnThmjJQFgZXocHG8CAwEAAQ==
-----END PUBLIC KEY-----

View File

@ -0,0 +1,14 @@
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlEyxkHggKCXC2Wf5Mzx4
nZLFZvU2bgcA3exfNPO/g1YunKfQY+Jg4fr6tJUUTZ3XZUrhmLNWvpvSwDS19ZmC
IXOu0+V94aNgnhMsk9rr59I8qcbsQGIBoHzuAl8NzZCgdbEXkiY90w1skUw8J57z
qCsMBydAueMXuWqF5nGtYbi5vHwK42PffpiZ7G5Kjwn8nYMW5IZdL6ZnMEVJUWC9
I4waeKg0yskczYDmZUEAtrn3laX9677ToCpiKrvmZYjlGl0BaGp3cxggP2xaDbUq
qfFxWNgvUAb3pXD09JM6Mt6HSIJaFc9vQbrKB9KT515y763j5CC2KUsilszKi3mB
HYe5PoebdjS7D1Oh+tRqfegU2IImzSwW3iwA7PJvefFuc/kNIijfS/gH/cAqAK6z
bhdOtE/zc7TtqW2Wn5Y03jIZdtm12CxSxwgtCF1NPyEWyIxAQUX9ACb3M0FAZ61n
fpPrvwTaIIxxZ01L3IzPLpbc44x/DhJIEU+iDt6IMTrHOphD9MCG4631eIdB0H1b
6zbNX1CXTsafqHRFV9XmYYIeOMggmd90s3xIbEujA6HKNP/gwzO6CDJ+nHFDEqoF
SkxRdTkEqjTjVKieURW7Swv7zpfu5PrsrrkyGnsRrBJJzXlm2FOOxnbI2iSL1B5F
rO5kbUxFeZUIDq+7Yv4kLWcCAwEAAQ==
-----END PUBLIC KEY-----

View File

@ -0,0 +1,14 @@
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnC+bR4bHf/L6QdU4puhQ
gl1MHePszRC38bzvVFDUJsmCaMCL2suCs2A2yxAgGb9pu9AJYLAmxQC4mM3jNqhg
/E7yuaBbek3O02zN/ctvflJ250wZCy+z0ZGIp1ak6pu1j14IwHokl9j36zNfGtfv
ADVOcdpWITFFlPqwq1qt/H3UsKVmtiF3BNWWTeUEQwKvlU8ymxgS99yn0+4OPyNT
L3EUeS+NQJtDS01unau0t7LnjUXn+XIneWny8bIYOQCuVR6s/gpIGuhBaUqwaJOw
7jkJZYF2Ij7uPb4b5/R3vX2FfxxqEHqssFSg8FFUNTZz3qNZs0CRVyfA972g9WkJ
hPfn31pQYil4QGRibCMIeU27YAEjXoqfJKEPh4UWMQsQLrEfdGfb8VgwrPbniGfU
L3jKJR3VAafL9330iawzVQDlIlwGl6u77gEXMl9K0pfazunYhAp+BMP+9ot5ckK+
osmrqj11qMESsAj083GeFdfV3pXEIwUytaB0AKEht9DbqUfiE/oeZ/LAXgySMtVC
sbC4ESmgVeY2xSBIJdDyUap7FR49GGrw0W49NUv9gRgQtGGaNVQQO9oGL2PBC41P
iWF9GLoX30HIz1P8PF/cZvicSSPkQf2Z6TV+t0ebdGNS5DjapdnCrq8m9Z0pyKsQ
uxAL2a7zX8l5i1CZh1ycUGsCAwEAAQ==
-----END PUBLIC KEY-----

View File

@ -0,0 +1,14 @@
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0MfCDrhODRCIxR9Dep1s
eXafh5CE5BrF4WbCgCsevyPIdvTeyIaW4vmO3bbG4VzhogDZju+R3IQYFuhoXP5v
Y+zYJGnwrgz3r5wYAvPnLEs1+dtDKYOgJXQj+wLJBW1mzRDL8FoRXOe5iRmn1EFS
wZ1DoUvyu7/J5r0itKicZp3QKED6YoilXed+1vnS4Sk0mzN4smuMR9eO1mMCqNp9
9KTfRDHTbakIHwasECCXCp50uXdoW6ig/xUAFanpm9LtK6jctNDbXDhQmgvAaLXZ
LvFqoaYJ/CvWkyYCgL6qxvMvVmPoRv7OPcyni4xR/WgWa0MSaEWjgPx3+yj9fiMA
1S02pFWFDOr5OUF/O4YhFJvUCOtVsUPPfA/Lj6faL0h5QI9mQhy5Zb9TTaS9jB6p
Lw7u0dJlrjFedk8KTJdFCcaGYHP6kNPnOxMylcB/5WcztXZVQD5WpCicGNBxCGMm
W64SgrV7M07gQfL/32QLsdqPUf0i8hoVD8wfQ3EpbQzv6Fk1Cn90bZqZafg8XWGY
wddhkXk7egrr23Djv37V2okjzdqoyLBYBxMz63qQzFoAVv5VoY2NDTbXYUYytOvG
GJ1afYDRVWrExCech1mX5ZVUB1br6WM+psFLJFoBFl6mDmiYt0vMYBddKISsvwLl
IJQkzDwtXzT2cSjoj3T5QekCAwEAAQ==
-----END PUBLIC KEY-----

View File

@ -0,0 +1,14 @@
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvaaoSLab+IluixwKV5Od
0gib2YurjPatGIbn5Ov2DLUFYiebj2oJINXJSwUOO+4WcuHFEqiL/1rya+k5hLZt
hnPL1tn6QD4rESznvGSasRCQNT2vS/oyZbTYJRyAtFkEYLlq0t3S3xBxxHWuvIf0
qVxVNYpQWyM3N9RIeYBR/euXKJXileSHk/uq1I5wTC0XBIHWcthczGN0m9wBEiWS
0m3cnPk4q0Ea8mUJ91Rqob19qETz6VbSPYYpZk3qOycjKosuwcuzoMpwU8KRiMFd
5LHtX0Hx85ghGsWDVtS0c0+aJa4lOMGvJCAOvDfqvODv7gKlCXUpgumGpLdTmaZ8
1RwqspAe3IqBcdKTqRD4m2mSg23nVx2FAY3cjFvZQtfooT7q1ItRV5RgH6FhQSl7
+6YIMJ1Bf8AAlLdRLpg+doOUGcEn+pkDiHFgI8ylH1LKyFKw+eXaAml/7DaWZk1d
dqggwhXOhc/UUZFQuQQ8A8zpA13PcbC05XxN2hyP93tCEtyynMLVPtrRwDnHxFKa
qKzs3rMDXPSXRn3ZZTdKH3069ApkEjQdpcwUh+EmJ1Ve/5cdtzT6kKWCjKBFZP/s
91MlRrX2BTRdHaU5QJkUheUtakwxuHrdah2F94lRmsnQlpPr2YseJu6sIE+Dnx4M
CfhdVbQL2w54R645nlnohu8CAwEAAQ==
-----END PUBLIC KEY-----

View File

@ -0,0 +1,14 @@
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq0BFD1D4lIxQcsqEpQzU
pNCYM3aP1V/fxxVdT4DWvSI53JHTwHQamKdMWtEXetWVbP5zSROniYKFXd/xrD9X
0jiGHey3lEtylXRIPxe5s+wXoCmNLcJVnvTcDtwx/ne2NLHxp76lyc25At+6RgE6
ADjLVuoD7M4IFDkAsd8UQ8zM0Dww9SylIk/wgV3ZkifecvgUQRagrNUdUjR56EBZ
raQrev4hhzOgwelT0kXCu3snbUuNY/lU53CoTzfBJ5UfEJ5pMw1ij6X0r5S9IVsy
KLWH1hiO0NzU2c8ViUYCly4Fe9xMTFc6u2dy/dxf6FwERfGzETQxqZvSfrRX+GLj
/QZAXiPg5178hT/m0Y3z5IGenIC/80Z9NCi+byF1WuJlzKjDcF/TU72zk0+PNM/H
Kuppf3JT4DyjiVzNC5YoWJT2QRMS9KLP5iKCSThwVceEEg5HfhQBRT9M6KIcFLSs
mFjx9kNEEmc1E8hl5IR3+3Ry8G5/bTIIruz14jgeY9u5jhL8Vyyvo41jgt9sLHR1
/J1TxKfkgksYev7PoX6/ZzJ1ksWKZY5NFoDXTNYUgzFUTOoEaOg3BAQKadb3Qbbq
XIrxmPBdgrn9QI7NCgfnAY3Tb4EEjs3ON/BNyEhUENcXOH6I1NbcuBQ7g9P73kE4
VORdoc8MdJ5eoKBpO8Ww8HECAwEAAQ==
-----END PUBLIC KEY-----

View File

@ -0,0 +1,14 @@
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyduVzi1mWm+lYo2Tqt/0
XkCIWrDNP1QBMVPrE0/ZlU2bCGSoo2Z9FHQKz/mTyMRlhNqTfhJ5qU3U9XlyGOPJ
piM+b91g26pnpXJ2Q2kOypSgOMOPA4cQ42PkHBEqhuzssfj9t7x47ppS94bboh46
xLSDRff/NAbtwTpvhStV3URYkxFG++cKGGa5MPXBrxIp+iZf9GnuxVdST5PGiVGP
ODL/b69sPJQNbJHVquqUTOh5Ry8uuD2WZuXfKf7/C0jC/ie9m2+0CttNu9tMciGM
EyKG1/Xhk5iIWO43m4SrrT2WkFlcZ1z2JSf9Pjm4C2+HovYpihwwdM/OdP8Xmsnr
DzVB4YvQiW+IHBjStHVuyiZWc+JsgEPJzisNY0Wyc/kNyNtqVKpX6dRhMLanLmy+
f53cCSI05KPQAcGj6tdL+D60uKDkt+FsDa0BTAobZ31OsFVid0vCXtsbplNhW1IF
HwsGXBTVcfXg44RLyL8Lk/2dQxDHNHzAUslJXzPxaHBLmt++2COa2EI1iWlvtznk
Ok9WP8SOAIj+xdqoiHcC4j72BOVVgiITIJNHrbppZCq6qPR+fgXmXa+sDcGh30m6
9Wpbr28kLMSHiENCWTdsFij+NQTd5S47H7XTROHnalYDuF1RpS+DpQidT5tUimaT
JZDr++FjKrnnijbyNF8b98UCAwEAAQ==
-----END PUBLIC KEY-----

View File

@ -0,0 +1,14 @@
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnpUpyWDWjlUk3smlWeA0
lIMW+oJ38t92CRLHH3IqRhyECBRW0d0aRGtq7TY8PmxjjvBZrxTNDpJT6KUk4LRm
a6A6IuAI7QnNK8SJqM0DLzlpygd7GJf8ZL9SoHSH+gFsYF67Cpooz/YDqWrlN7Vw
tO00s0B+eXy+PCXYU7VSfuWFGK8TGEv6HfGMALLjhqMManyvfp8hz3ubN1rK3c8C
US/ilRh1qckdbtPvoDPhSbTDmfU1g/EfRSIEXBrIMLg9ka/XB9PvWRrekrppnQzP
hP9YE3x/wbFc5QqQWiRCYyQl/rgIMOXvIxhkfe8H5n1Et4VAorkpEAXdsfN8KSVv
LSMazVlLp9GYq5SUpqYX3KnxdWBgN7BJoZ4sltsTpHQ/34SXWfu3UmyUveWj7wp0
x9hwsPirVI00EEea9AbP7NM2rAyu6ukcm4m6ATd2DZJIViq2es6m60AE6SMCmrQF
wmk4H/kdQgeAELVfGOm2VyJ3z69fQuywz7xu27S6zTKi05Qlnohxol4wVb6OB7qG
LPRtK9ObgzRo/OPumyXqlzAi/Yvyd1ZQk8labZps3e16bQp8+pVPiumWioMFJDWV
GZjCmyMSU8V6MB6njbgLHoyg2LCukCAeSjbPGGGYhnKLm1AKSoJh3IpZuqcKCk5C
8CM1S15HxV78s9dFntEqIokCAwEAAQ==
-----END PUBLIC KEY-----

View File

@ -37,6 +37,7 @@ def symlinks(args, flavor, folder):
f"{args.device}-boot.img": "Boot partition image",
f"{args.device}-root.img": "Root partition image",
f"pmos-{args.device}.zip": "Android recovery flashable zip",
"lk2nd.img": "Secondary Android bootloader",
}
# Generate a list of patterns
@ -53,7 +54,8 @@ def symlinks(args, flavor, folder):
f"{path_native}/home/pmos/rootfs/{args.device}-boot.img",
f"{path_native}/home/pmos/rootfs/{args.device}-root.img",
f"{path_buildroot}/var/lib/postmarketos-android-recovery-" +
f"installer/pmos-{args.device}.zip"]
f"installer/pmos-{args.device}.zip",
f"{path_boot}/lk2nd.img"]
# Generate a list of files from the patterns
files = []

View File

@ -112,6 +112,19 @@ def sideload(args):
pmb.flasher.run(args, "sideload")
def flash_lk2nd(args):
chroot_path = args.work + "/chroot_rootfs_" + args.device
lk2nd_path = "/boot/lk2nd.img"
if not os.path.exists(chroot_path + lk2nd_path):
raise RuntimeError(f"{chroot_path+lk2nd_path} doesn't exist. Your"
" device may not support lk2nd.")
logging.info(lk2nd_path)
logging.info("It's normal if fastboot warns"
" \"Image not signed or corrupt\" or a similar warning")
pmb.flasher.run(args, "flash_lk2nd")
def frontend(args):
action = args.action_flasher
method = args.flash_method or args.deviceinfo["flash_method"]
@ -120,7 +133,8 @@ def frontend(args):
if action == "flash_system":
action = "flash_rootfs"
if method == "none" and action in ["boot", "flash_kernel", "flash_rootfs"]:
if method == "none" and action in ["boot", "flash_kernel", "flash_rootfs",
"flash_lk2nd"]:
logging.info("This device doesn't support any flash method.")
return
@ -138,3 +152,5 @@ def frontend(args):
list_devices(args)
if action == "sideload":
sideload(args)
if action in ["flash_lk2nd"]:
flash_lk2nd(args)

View File

@ -19,9 +19,10 @@ ANITYA_API_BASE = "https://release-monitoring.org/api/v2"
GITHUB_API_BASE = "https://api.github.com"
GITLAB_HOSTS = [
"https://gitlab.com",
"https://gitlab.freedesktop.org",
"https://gitlab.gnome.org",
"https://invent.kde.org",
"https://source.puri.sm",
"https://gitlab.freedesktop.org",
]
@ -169,38 +170,52 @@ def upgrade_stable_package(args, pkgname: str, package) -> bool:
:param package: a dict containing package information
:returns: if something (would have) been changed
"""
projects = pmb.helpers.http.retrieve_json(
f"{ANITYA_API_BASE}/projects/?name={pkgname}", headers=req_headers)
if projects["total_items"] < 1:
# There is no Anitya project with the package name.
# Looking up if there's a custom mapping from postmarketOS package name
# to Anitya project name.
mappings = pmb.helpers.http.retrieve_json(
f"{ANITYA_API_BASE}/packages/?distribution=postmarketOS"
f"&name={pkgname}", headers=req_headers)
if mappings["total_items"] < 1:
logging.warning("{}: failed to get Anitya project".format(pkgname))
return False
project_name = mappings["items"][0]["project"]
# Looking up if there's a custom mapping from postmarketOS package name
# to Anitya project name.
mappings = pmb.helpers.http.retrieve_json(
f"{ANITYA_API_BASE}/packages/?distribution=postmarketOS"
f"&name={pkgname}", headers=req_headers)
if mappings["total_items"] < 1:
projects = pmb.helpers.http.retrieve_json(
f"{ANITYA_API_BASE}/projects/?name={project_name}",
f"{ANITYA_API_BASE}/projects/?name={pkgname}", headers=req_headers)
if projects["total_items"] < 1:
logging.warning(f"{pkgname}: failed to get Anitya project")
return False
else:
project_name = mappings["items"][0]["project"]
ecosystem = mappings["items"][0]["ecosystem"]
projects = pmb.helpers.http.retrieve_json(
f"{ANITYA_API_BASE}/projects/?name={project_name}&"
f"ecosystem={ecosystem}",
headers=req_headers)
if projects["total_items"] < 1:
logging.warning(f"{pkgname}: didn't find any projects, can't upgrade!")
return False
if projects["total_items"] > 1:
logging.warning(f"{pkgname}: found more than one project, can't "
f"upgrade! Please create an explicit mapping of "
f"\"project\" to the package name.")
return False
# Get the first, best-matching item
project = projects["items"][0]
# Check that we got a version number
if project["version"] is None:
if len(project["stable_versions"]) < 1:
logging.warning("{}: got no version number, ignoring".format(pkgname))
return False
version = project["stable_versions"][0]
# Compare the pmaports version with the project version
if package["pkgver"] == project["version"]:
if package["pkgver"] == version:
logging.info("{}: up-to-date".format(pkgname))
return False
pkgver = package["pkgver"]
pkgver_new = project["version"]
pkgver_new = version
pkgrel = package["pkgrel"]
pkgrel_new = 0

View File

@ -31,15 +31,7 @@ import pmb.helpers.git
args.device ("samsung-i9100", "qemu-amd64" etc.)
args.work ("/home/user/.local/var/pmbootstrap", override with --work)
3. Shortcuts
Long variables or function calls that always return the same information
may have a shortcut defined, to make the code more readable (see
add_shortcuts() below).
Example:
args.arch_native ("x86_64" etc.)
4. Cache
3. Cache
pmbootstrap uses this dictionary to save the result of expensive
results, so they work a lot faster the next time they are needed in the
same session. Usually the cache is written to and read from in the same
@ -54,7 +46,7 @@ import pmb.helpers.git
See add_cache() below for details.
5. Parsed configs
4. Parsed configs
Similar to the cache above, specific config files get parsed and added
to args, so they can get accessed quickly (without parsing the configs
over and over). These configs are not only used in one specific
@ -116,11 +108,6 @@ def replace_placeholders(args):
setattr(args, key, os.path.expanduser(getattr(args, key)))
def add_shortcuts(args):
""" Add convenience shortcuts """
setattr(args, "arch_native", pmb.parse.arch.alpine_native())
def add_cache(args):
""" Add a caching dict (caches parsing of files etc. for the current
session) """
@ -142,7 +129,7 @@ def add_deviceinfo(args):
""" Add and verify the deviceinfo (only after initialization) """
setattr(args, "deviceinfo", pmb.parse.deviceinfo(args))
arch = args.deviceinfo["arch"]
if (arch != args.arch_native and
if (arch != pmb.config.arch_native and
arch not in pmb.config.build_device_architectures):
raise ValueError("Arch '" + arch + "' is not available in"
" postmarketOS. If you would like to add it, see:"
@ -154,7 +141,6 @@ def init(args):
fix_mirrors_postmarketos(args)
pmb.config.merge_with_args(args)
replace_placeholders(args)
add_shortcuts(args)
add_cache(args)
# Initialize logs (we could raise errors below)

View File

@ -368,7 +368,7 @@ def kconfig(args):
if args.action_kconfig == "check":
# Handle passing a file directly
if args.file:
if pmb.parse.kconfig.check_file(args, args.package,
if pmb.parse.kconfig.check_file(args.package,
anbox=args.anbox,
nftables=args.nftables,
containers=args.containers,
@ -403,6 +403,8 @@ def kconfig(args):
if not pmb.parse.kconfig.check(
args, package,
force_anbox_check=args.anbox,
force_apparmor_check=args.apparmor,
force_iwd_check=args.iwd,
force_nftables_check=args.nftables,
force_containers_check=args.containers,
force_zram_check=args.zram,
@ -507,7 +509,7 @@ def shutdown(args):
def stats(args):
# Chroot suffix
suffix = "native"
if args.arch != args.arch_native:
if args.arch != pmb.config.arch_native:
suffix = "buildroot_" + args.arch
# Install ccache and display stats

View File

@ -229,7 +229,7 @@ def pull(args, name_repo):
return 0
def is_outdated(args, path):
def is_outdated(path):
# FETCH_HEAD always exists in repositories cloned by pmbootstrap.
# Usually it does not (before first git fetch/pull), but there is no good
# fallback. For exampe, getting the _creation_ date of .git/HEAD is non-

View File

@ -29,7 +29,7 @@ def folder_size(args, path):
return ret
def check_grsec(args):
def check_grsec():
"""
Check if the current kernel is based on the grsec patchset, and if
the chroot_deny_chmod option is enabled. Raise an exception in that

View File

@ -100,6 +100,41 @@ def guess_main(args, subpkgname):
return os.path.dirname(path)
def _find_package_in_apkbuild(args, package, path):
"""
Look through subpackages and all provides to see if the APKBUILD at the
specified path contains (or provides) the specified package.
:param package: The package to search for
:param path: The path to the apkbuild
:return: True if the APKBUILD contains or provides the package
"""
apkbuild = pmb.parse.apkbuild(args, path)
# Subpackages
if package in apkbuild["subpackages"]:
return True
# Search for provides in both package and subpackages
apkbuild_pkgs = [apkbuild, *apkbuild["subpackages"].values()]
for apkbuild_pkg in apkbuild_pkgs:
if not apkbuild_pkg:
continue
# Provides (cut off before equals sign for entries like
# "mkbootimg=0.0.1")
for provides_i in apkbuild_pkg["provides"]:
# Ignore provides without version, they shall never be
# automatically selected
if "=" not in provides_i:
continue
if package == provides_i.split("=", 1)[0]:
return True
return False
def find(args, package, must_exist=True):
"""
Find the aport path that provides a certain subpackage.
@ -123,36 +158,23 @@ def find(args, package, must_exist=True):
if path:
ret = os.path.dirname(path)
# Try to guess based on the subpackage name
guess = guess_main(args, package)
if guess:
# ... but see if we were right
if _find_package_in_apkbuild(args, package, f'{guess}/APKBUILD'):
ret = guess
# Search in subpackages and provides
if not ret:
for path_current in _find_apkbuilds(args).values():
apkbuild = pmb.parse.apkbuild(args, path_current)
found = False
# Subpackages
if package in apkbuild["subpackages"]:
found = True
# Provides (cut off before equals sign for entries like
# "mkbootimg=0.0.1")
if not found:
for provides_i in apkbuild["provides"]:
# Ignore provides without version, they shall never be
# automatically selected
if "=" not in provides_i:
continue
if package == provides_i.split("=", 1)[0]:
found = True
break
if found:
if _find_package_in_apkbuild(args, package, path_current):
ret = os.path.dirname(path_current)
break
# Guess a main package
# Use the guess otherwise
if not ret:
ret = guess_main(args, package)
ret = guess
# Crash when necessary
if ret is None and must_exist:
@ -197,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.

View File

@ -96,7 +96,7 @@ def apkindex_files(args, arch=None, user_repository=True, pmos=True,
:returns: list of absolute APKINDEX.tar.gz file paths
"""
if not arch:
arch = args.arch_native
arch = pmb.config.arch_native
ret = []
# Local user repository (for packages compiled with pmbootstrap)
@ -209,7 +209,7 @@ def alpine_apkindex_path(args, repo="main", arch=None):
raise RuntimeError("Invalid Alpine repository: " + repo)
# Download the file
arch = arch or args.arch_native
arch = arch or pmb.config.arch_native
update(args, arch)
# Find it on disk

View File

@ -92,7 +92,7 @@ def print_checks_git_repo(args, repo, details=True):
log_ok("up to date with remote branch")
# Outdated remote information
if pmb.helpers.git.is_outdated(args, path):
if pmb.helpers.git.is_outdated(path):
return log_nok_ret(-5, "outdated remote information",
"update with 'pmbootstrap pull'")
log_ok("remote information updated recently (via git fetch/pull)")

View File

@ -204,6 +204,30 @@ def set_user(args):
pmb.chroot.root(args, ["addgroup", args.user, group], suffix)
def setup_login_chpasswd_user_from_arg(args, suffix):
"""
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
a temp file, instead of "echo user:$pass | chpasswd". The user should of
course only use this with a test password anyway, but let's be nice and try
to have the user protected from accidentally posting their password in
any case.
:param suffix: of the chroot, where passwd will be execute (either the
f"rootfs_{args.device}", or f"installer_{args.device}")
"""
path = "/tmp/pmbootstrap_chpasswd_in"
path_outside = f"{args.work}/chroot_{suffix}{path}"
with open(path_outside, "w", encoding="utf-8") as handle:
handle.write(f"{args.user}:{args.password}")
pmb.chroot.root(args, ["sh", "-c", f"cat {shlex.quote(path)} | chpasswd"],
suffix)
os.unlink(path_outside)
def setup_login(args, suffix):
"""
Loop until the password for user has been set successfully, and disable
@ -214,16 +238,19 @@ def setup_login(args, suffix):
"""
if not args.on_device_installer:
# User password
logging.info(" *** SET LOGIN PASSWORD FOR: '" + args.user + "' ***")
while True:
try:
pmb.chroot.root(args, ["passwd", args.user], suffix,
output="interactive")
break
except RuntimeError:
logging.info("WARNING: Failed to set the password. Try it"
" one more time.")
pass
logging.info(f" *** SET LOGIN PASSWORD FOR: '{args.user}' ***")
if args.password:
setup_login_chpasswd_user_from_arg(args, suffix)
else:
while True:
try:
pmb.chroot.root(args, ["passwd", args.user], suffix,
output="interactive")
break
except RuntimeError:
logging.info("WARNING: Failed to set the password. Try it"
" one more time.")
pass
# Disable root login
pmb.chroot.root(args, ["passwd", "-l", "root"], suffix)
@ -698,6 +725,14 @@ def print_flash_info(args):
" the kernel/initramfs directly without flashing."
" Use 'pmbootstrap flasher boot' to do that.)")
if "flash_lk2nd" in flasher_actions and \
os.path.exists(args.work + "/chroot_rootfs_" + args.device +
"/boot/lk2nd.img"):
logging.info(" * Your device supports and may even require"
" flashing lk2nd. You should flash it before"
" flashing anything else. Use 'pmbootstrap flasher"
" flash_lk2nd' to do that.")
# Export information
logging.info("* If the above steps do not work, you can also create"
" symlinks to the generated files with 'pmbootstrap export'"
@ -789,6 +824,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,17 +855,23 @@ 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) +
pmb.install.ui.get_recommends(args))
["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:
install_packages += pmb.install.ui.get_recommends(args)
if args.extra_packages.lower() != "none":
install_packages += args.extra_packages.split(",")
if args.add:

View File

@ -209,6 +209,11 @@ def _parse_attributes(path, lines, apkbuild_attributes, ret):
if options.get("array", False):
# Split up arrays, delete empty strings inside the list
ret[attribute] = list(filter(None, ret[attribute].split(" ")))
if options.get("int", False):
if ret[attribute]:
ret[attribute] = int(ret[attribute])
else:
ret[attribute] = 0
def _parse_subpackage(path, lines, apkbuild, subpackages, subpkg):

View File

@ -9,7 +9,7 @@ import pmb.helpers.repo
import pmb.parse.version
def parse_next_block(args, path, lines, start):
def parse_next_block(path, lines, start):
"""
Parse the next block in an APKINDEX.
@ -200,7 +200,7 @@ def parse(args, path, multiple_providers=True):
ret = collections.OrderedDict()
start = [0]
while True:
block = parse_next_block(args, path, lines, start)
block = parse_next_block(path, lines, start)
if not block:
break
@ -223,7 +223,7 @@ def parse(args, path, multiple_providers=True):
return ret
def parse_blocks(args, path):
def parse_blocks(path):
"""
Read all blocks from an APKINDEX.tar.gz into a list.
@ -244,7 +244,7 @@ def parse_blocks(args, path):
ret = []
start = [0]
while True:
block = pmb.parse.apkindex.parse_next_block(args, path, lines, start)
block = pmb.parse.apkindex.parse_next_block(path, lines, start)
if not block:
return ret
ret.append(block)
@ -282,7 +282,7 @@ def providers(args, package, arch=None, must_exist=True, indexes=None):
"""
if not indexes:
arch = arch or args.arch_native
arch = arch or pmb.config.arch_native
indexes = pmb.helpers.repo.apkindex_files(args, arch)
for operator in [">", ">=", "=", "<=", "<", "~"]:

View File

@ -1,12 +1,12 @@
# Copyright 2021 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
import platform
import fnmatch
import platform
import pmb.parse.arch
def alpine_native():
machine = platform.machine()
ret = ""
mapping = {
"i686": "x86",
@ -19,12 +19,11 @@ def alpine_native():
return mapping[machine]
raise ValueError("Can not map platform.machine '" + machine + "'"
" to the right Alpine Linux architecture")
return ret
def from_chroot_suffix(args, suffix):
if suffix == "native":
return args.arch_native
return pmb.config.arch_native
if suffix in [f"rootfs_{args.device}", f"installer_{args.device}"]:
return args.deviceinfo["arch"]
if suffix.startswith("buildroot_"):
@ -97,9 +96,9 @@ def alpine_to_hostspec(arch):
" to the right hostspec value")
def cpu_emulation_required(args, arch):
def cpu_emulation_required(arch):
# Obvious case: host arch is target arch
if args.arch_native == arch:
if pmb.config.arch_native == arch:
return False
# Other cases: host arch on the left, target archs on the right
@ -108,8 +107,8 @@ def cpu_emulation_required(args, arch):
"armv7": ["armel", "armhf"],
"aarch64": ["armel", "armhf", "armv7"],
}
if args.arch_native in not_required:
if arch in not_required[args.arch_native]:
if pmb.config.arch_native in not_required:
if arch in not_required[pmb.config.arch_native]:
return False
# No match: then it's required

View File

@ -55,6 +55,10 @@ def arguments_install(subparser):
help="do not enable the SSH daemon by default")
ret.add_argument("--no-firewall", action="store_true",
help="do not enable the firewall by default")
ret.add_argument("--password", help="dummy password for automating the"
" installation - will be handled in PLAIN TEXT during"
" install and may be logged to the logfile, do not use an"
" important password!")
# Image type
group_desc = ret.add_argument_group(
@ -217,6 +221,14 @@ def arguments_flasher(subparser):
help="partition to flash the kernel to (defaults"
" to deviceinfo_flash_*_partition_kernel)")
# Flash lk2nd
flash_lk2nd = sub.add_parser("flash_lk2nd",
help="flash lk2nd, a secondary bootloader"
" needed for various Android devices")
flash_lk2nd.add_argument("--partition", default=None,
help="partition to flash lk2nd to (defaults to"
" default boot image partition ")
# Flash rootfs
flash_rootfs = sub.add_parser("flash_rootfs", aliases=["flash_system"],
help="flash the rootfs to a partition on the"
@ -416,7 +428,7 @@ def arguments_newapkbuild(subparser):
def arguments_kconfig(subparser):
# Allowed architectures
arch_native = pmb.parse.arch.alpine_native()
arch_native = pmb.config.arch_native
arch_choices = set(pmb.config.build_device_architectures + [arch_native])
# Kconfig subparser
@ -434,15 +446,17 @@ def arguments_kconfig(subparser):
" directly instead of a config in a package")
check.add_argument("--anbox", action="store_true", help="check"
" options needed for anbox too")
check.add_argument("--apparmor", action="store_true", help="check"
" options needed for apparmor too")
check.add_argument("--iwd", action="store_true", help="check"
" options needed for iwd too")
check.add_argument("--nftables", action="store_true", help="check"
" options needed for nftables too")
check.add_argument("--containers", action="store_true",
help="check options needed for containers too")
check.add_argument("--zram", action="store_true", help="check"
" options needed for zram support too")
check_package = check.add_argument("package", default="", nargs='?')
if argcomplete:
check_package.completer = kernel_completer
add_kernel_arg(check)
# "pmbootstrap kconfig edit"
edit = sub.add_parser("edit", help="edit kernel aport config")
@ -453,9 +467,7 @@ def arguments_kconfig(subparser):
edit.add_argument("-n", dest="nconfig", action="store_true",
help="use nconfig rather than menuconfig for kernel"
" configuration")
edit_package = edit.add_argument("package", nargs='?')
if argcomplete:
edit_package.completer = kernel_completer
add_kernel_arg(edit)
# "pmbootstrap kconfig migrate"
migrate = sub.add_parser("migrate",
@ -464,9 +476,7 @@ def arguments_kconfig(subparser):
"which asks question for every new kernel "
"config option.")
migrate.add_argument("--arch", choices=arch_choices, dest="arch")
migrate_package = migrate.add_argument("package", nargs='?')
if argcomplete:
migrate_package.completer = kernel_completer
add_kernel_arg(migrate)
def arguments_repo_missing(subparser):
@ -476,7 +486,7 @@ def arguments_repo_missing(subparser):
if argcomplete:
package.completer = package_completer
ret.add_argument("--arch", choices=pmb.config.build_device_architectures,
default=pmb.parse.arch.alpine_native())
default=pmb.config.arch_native)
ret.add_argument("--built", action="store_true",
help="include packages which exist in the binary repos")
ret.add_argument("--overview", action="store_true",
@ -498,7 +508,7 @@ def arguments_status(subparser):
return ret
def package_completer(prefix, action, parser, parsed_args):
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)
@ -509,10 +519,21 @@ def package_completer(prefix, action, parser, parsed_args):
return packages
def kernel_completer(prefix, action, parser, parsed_args):
packages = package_completer("linux-" + prefix, action, parser,
def kernel_completer(prefix, action, parser=None, parsed_args=None):
""" :returns: matched linux-* packages, with linux-* prefix and without """
ret = []
# Full package name, starting with "linux-"
if (len("linux-") < len(prefix) and prefix.startswith("linux-") or
"linux-".startswith(prefix)):
ret += package_completer(prefix, action, parser, parsed_args)
# Kernel name without "linux-"
packages = package_completer(f"linux-{prefix}", action, parser,
parsed_args)
return [package.replace("linux-", "", 1) for package in packages]
ret += [package.replace("linux-", "", 1) for package in packages]
return ret
def add_packages_arg(subparser, name="packages", *args, **kwargs):
@ -521,9 +542,16 @@ def add_packages_arg(subparser, name="packages", *args, **kwargs):
arg.completer = package_completer
def add_kernel_arg(subparser, name="package", *args, **kwargs):
arg = subparser.add_argument("package", nargs='?', help="kernel package"
" (e.g. linux-postmarketos-allwinner)")
if argcomplete:
arg.completer = kernel_completer
def arguments():
parser = argparse.ArgumentParser(prog="pmbootstrap")
arch_native = pmb.parse.arch.alpine_native()
arch_native = pmb.config.arch_native
arch_choices = set(pmb.config.build_device_architectures + [arch_native])
mirrors_pmos_default = pmb.config.defaults["mirrors_postmarketos"]

View File

@ -7,7 +7,7 @@ import pmb.config
# Return: {magic: ..., mask: ...}
def binfmt_info(args, arch_qemu):
def binfmt_info(arch_qemu):
# Parse the info file
full = {}
info = pmb.config.pmb_src + "/pmb/data/qemu-user-binfmt.txt"

View File

@ -19,6 +19,17 @@ def is_set(config, option):
return re.search("^CONFIG_" + option + "=[ym]$", config, re.M) is not None
def is_set_str(config, option, string):
"""
Check, whether a config option contains a string as value.
"""
match = re.search("^CONFIG_" + option + "=\"(.*)\"$", config, re.M)
if match:
return string == match.group(1)
else:
return False
def is_in_array(config, option, string):
"""
Check, whether a config option contains string as an array element
@ -47,6 +58,15 @@ def check_option(component, details, config, config_path_pretty, option,
else:
logging.warning(warning_no_details)
return False
elif isinstance(option_value, str):
if not is_set_str(config, option, option_value):
if details:
logging.info(f"WARNING: {config_path_pretty}: CONFIG_{option}"
f' should be set to "{option_value}".'
f" See <{link}> for details.")
else:
logging.warning(warning_no_details)
return False
elif option_value in [True, False]:
if option_value != is_set(config, option):
if details:
@ -57,15 +77,20 @@ def check_option(component, details, config, config_path_pretty, option,
logging.warning(warning_no_details)
return False
else:
raise RuntimeError("kconfig check code can only handle True/False and"
" arrays now, given value '" + str(option_value) +
"' is not supported. If you need this, please open"
" an issue.")
raise RuntimeError("kconfig check code can only handle booleans,"
f" strings and arrays. Given value {option_value}"
" is not supported. If you need this, please patch"
" pmbootstrap or open an issue.")
return True
def check_config(config_path, config_path_pretty, config_arch, pkgver,
anbox=False, nftables=False, containers=False, zram=False,
anbox=False,
apparmor=False,
iwd=False,
nftables=False,
containers=False,
zram=False,
details=False):
logging.debug(f"Check kconfig: {config_path}")
with open(config_path) as handle:
@ -74,6 +99,10 @@ def check_config(config_path, config_path_pretty, config_arch, pkgver,
components = {"postmarketOS": pmb.config.necessary_kconfig_options}
if anbox:
components["anbox"] = pmb.config.necessary_kconfig_options_anbox
if apparmor:
components["apparmor"] = pmb.config.necessary_kconfig_options_apparmor
if iwd:
components["iwd"] = pmb.config.necessary_kconfig_options_iwd
if nftables:
components["nftables"] = pmb.config.necessary_kconfig_options_nftables
if containers:
@ -123,8 +152,14 @@ def check_config_options_set(config, config_path_pretty, config_arch, options,
return ret
def check(args, pkgname, force_anbox_check=False, force_nftables_check=False,
force_containers_check=False, force_zram_check=False, details=False):
def check(args, pkgname,
force_anbox_check=False,
force_apparmor_check=False,
force_iwd_check=False,
force_nftables_check=False,
force_containers_check=False,
force_zram_check=False,
details=False):
"""
Check for necessary kernel config options in a package.
@ -133,8 +168,6 @@ def check(args, pkgname, force_anbox_check=False, force_nftables_check=False,
# Pkgname: allow omitting "linux-" prefix
if pkgname.startswith("linux-"):
flavor = pkgname.split("linux-")[1]
logging.info("PROTIP: You can simply do 'pmbootstrap kconfig check " +
flavor + "'")
else:
flavor = pkgname
@ -145,6 +178,10 @@ def check(args, pkgname, force_anbox_check=False, force_nftables_check=False,
pkgver = apkbuild["pkgver"]
check_anbox = force_anbox_check or (
"pmb:kconfigcheck-anbox" in apkbuild["options"])
check_apparmor = force_apparmor_check or (
"pmb:kconfigcheck-apparmor" in apkbuild["options"])
check_iwd = force_iwd_check or (
"pmb:kconfigcheck-iwd" in apkbuild["options"])
check_nftables = force_nftables_check or (
"pmb:kconfigcheck-nftables" in apkbuild["options"])
check_containers = force_containers_check or (
@ -159,6 +196,8 @@ def check(args, pkgname, force_anbox_check=False, force_nftables_check=False,
ret &= check_config(config_path, config_path_pretty, config_arch,
pkgver,
anbox=check_anbox,
apparmor=check_apparmor,
iwd=check_iwd,
nftables=check_nftables,
containers=check_containers,
zram=check_zram,
@ -198,7 +237,7 @@ def extract_version(config_file):
return "unknown"
def check_file(args, config_file, anbox=False, nftables=False,
def check_file(config_file, anbox=False, nftables=False,
containers=False, zram=False, details=False):
"""
Check for necessary kernel config options in a kconfig file.

View File

@ -104,11 +104,18 @@ def command_qemu(args, arch, img_path, img_path_2nd=None):
if pmaports_cfg.get("supported_mkinitfs_without_flavors", False):
flavor_suffix = ""
# Alpine kernels always have the flavor appended to /boot/vmlinuz
kernel = f"{rootfs}/boot/vmlinuz{flavor_suffix}"
if not os.path.exists(kernel):
kernel = f"{kernel}-{flavor}"
if not os.path.exists(kernel):
raise RuntimeError("failed to find the proper vmlinuz path")
ncpus = os.cpu_count()
# QEMU mach-virt's max CPU count is 8, limit it so it will work correctly
# on systems with more than 8 CPUs
if arch != args.arch_native and ncpus > 8:
if arch != pmb.config.arch_native and ncpus > 8:
ncpus = 8
if args.host_qemu:
@ -130,7 +137,7 @@ def command_qemu(args, arch, img_path, img_path_2nd=None):
rootfs_native + "/usr/share"})
command = []
if args.arch_native in ["aarch64", "armv7"]:
if pmb.config.arch_native in ["aarch64", "armv7"]:
# Workaround for QEMU failing on aarch64 asymetric multiprocessor
# arch (big/little architecture
# https://en.wikipedia.org/wiki/ARM_big.LITTLE) see
@ -141,12 +148,12 @@ def command_qemu(args, arch, img_path, img_path_2nd=None):
logging.info("QEMU will run on big/little architecture on the"
f" first {ncpus} cores (from /proc/cpuinfo)")
command += [rootfs_native + "/lib/ld-musl-" +
args.arch_native + ".so.1"]
pmb.config.arch_native + ".so.1"]
command += [rootfs_native + "/usr/bin/taskset"]
command += ["-c", "0-" + str(ncpus - 1)]
command += [rootfs_native + "/lib/ld-musl-" +
args.arch_native + ".so.1"]
pmb.config.arch_native + ".so.1"]
command += ["--library-path=" + rootfs_native + "/lib:" +
rootfs_native + "/usr/lib:" +
rootfs_native + "/usr/lib/pulseaudio"]
@ -154,7 +161,7 @@ def command_qemu(args, arch, img_path, img_path_2nd=None):
command += ["-L", rootfs_native + "/usr/share/qemu/"]
command += ["-nodefaults"]
command += ["-kernel", rootfs + "/boot/vmlinuz" + flavor_suffix]
command += ["-kernel", kernel]
command += ["-initrd", rootfs + "/boot/initramfs" + flavor_suffix]
command += ["-append", shlex.quote(cmdline)]
@ -189,7 +196,7 @@ def command_qemu(args, arch, img_path, img_path_2nd=None):
" yet.")
# Kernel Virtual Machine (KVM) support
native = args.arch_native == args.deviceinfo["arch"]
native = pmb.config.arch_native == args.deviceinfo["arch"]
if args.qemu_kvm and native and os.path.exists("/dev/kvm"):
command += ["-enable-kvm"]
command += ["-cpu", "host"]

View File

@ -315,7 +315,7 @@ def test_finish(args, monkeypatch):
assert "Package not found" in str(e.value)
# Existing output path
func(args, apkbuild, args.arch_native, output)
func(args, apkbuild, pmb.config.arch_native, output)
def test_package(args):
@ -362,7 +362,7 @@ def test_build_depends_high_level(args, monkeypatch):
# Remove hello-world
pmb.helpers.run.root(args, ["rm", output_hello_outside])
pmb.build.index_repo(args, args.arch_native)
pmb.build.index_repo(args, pmb.config.arch_native)
args.cache["built"] = {}
# Ask to build the wrapper. It should not build the wrapper (it exists, not
@ -414,7 +414,7 @@ def test_build_local_source_high_level(args, tmpdir):
# Test native arch and foreign arch chroot
channel = pmb.config.pmaports.read_config(args)["channel"]
for arch in [args.arch_native, "armhf"]:
for arch in [pmb.config.arch_native, "armhf"]:
# Delete all hello-world --src packages
pattern = f"{args.work}/packages/{channel}/{arch}/hello-world-*_p*.apk"
for path in glob.glob(pattern):
@ -432,5 +432,5 @@ def test_build_local_source_high_level(args, tmpdir):
pmb.helpers.run.root(args, ["rm", paths[0]])
# Clean up: update index, delete temp folder
pmb.build.index_repo(args, args.arch_native)
pmb.build.index_repo(args, pmb.config.arch_native)
pmb.helpers.run.root(args, ["rm", "-r", tmpdir])

View File

@ -32,13 +32,12 @@ def test_package_kernel_args(args):
str(e.value)
def test_find_kbuild_output_dir(args):
def test_find_kbuild_output_dir():
# Test parsing an APKBUILD
pkgname = "linux-envkernel-test"
path = pmb_test.const.testdata + "/apkbuild/APKBUILD." + pkgname
function_body = pmb.parse.function_body(path, "package")
kbuild_out = pmb.build.envkernel.find_kbuild_output_dir(args,
function_body)
kbuild_out = pmb.build.envkernel.find_kbuild_output_dir(function_body)
assert kbuild_out == "build"
# Test full function body
@ -59,8 +58,7 @@ def test_find_kbuild_output_dir(args):
" KBUILD_BUILD_VERSION=\"$((pkgrel + 1))-Alpine\" ",
" INSTALL_MOD_PATH=\"$pkgdir\" modules_install",
]
kbuild_out = pmb.build.envkernel.find_kbuild_output_dir(args,
function_body)
kbuild_out = pmb.build.envkernel.find_kbuild_output_dir(function_body)
assert kbuild_out == "build"
# Test no kbuild out dir
@ -70,8 +68,7 @@ def test_find_kbuild_output_dir(args):
" install -D \"$srcdir\"/include/config/kernel.release ",
" \"$pkgdir\"/usr/share/kernel/$_flavor/kernel.release",
]
kbuild_out = pmb.build.envkernel.find_kbuild_output_dir(args,
function_body)
kbuild_out = pmb.build.envkernel.find_kbuild_output_dir(function_body)
assert kbuild_out == ""
# Test curly brackets around srcdir
@ -81,8 +78,7 @@ def test_find_kbuild_output_dir(args):
" install -D \"${srcdir}\"/build/include/config/kernel.release ",
" \"$pkgdir\"/usr/share/kernel/$_flavor/kernel.release",
]
kbuild_out = pmb.build.envkernel.find_kbuild_output_dir(args,
function_body)
kbuild_out = pmb.build.envkernel.find_kbuild_output_dir(function_body)
assert kbuild_out == "build"
# Test multiple sub directories
@ -92,8 +88,7 @@ def test_find_kbuild_output_dir(args):
" install -D \"${srcdir}\"/sub/dir/include/config/kernel.release ",
" \"$pkgdir\"/usr/share/kernel/$_flavor/kernel.release",
]
kbuild_out = pmb.build.envkernel.find_kbuild_output_dir(args,
function_body)
kbuild_out = pmb.build.envkernel.find_kbuild_output_dir(function_body)
assert kbuild_out == "sub/dir"
# Test no kbuild out dir found
@ -104,8 +99,7 @@ def test_find_kbuild_output_dir(args):
" \"$pkgdir\"/usr/share/kernel/$_flavor/kernel.release",
]
with pytest.raises(RuntimeError) as e:
kbuild_out = pmb.build.envkernel.find_kbuild_output_dir(args,
function_body)
kbuild_out = pmb.build.envkernel.find_kbuild_output_dir(function_body)
assert ("Couldn't find a kbuild out directory. Is your APKBUILD messed up?"
" If not, then consider adjusting the patterns in "
"pmb/build/envkernel.py to work with your APKBUILD, or submit an "
@ -119,8 +113,7 @@ def test_find_kbuild_output_dir(args):
" \"$pkgdir\"/usr/share/kernel/$_flavor/kernel.release",
]
with pytest.raises(RuntimeError) as e:
kbuild_out = pmb.build.envkernel.find_kbuild_output_dir(args,
function_body)
kbuild_out = pmb.build.envkernel.find_kbuild_output_dir(function_body)
assert ("Multiple kbuild out directories found. Can you modify your "
"APKBUILD so it only has one output path? If you can't resolve it,"
" please open an issue.") in str(e.value)

View File

@ -168,7 +168,7 @@ def test_pull(args, monkeypatch, tmpdir):
assert func(args, name_repo) == 0
def test_is_outdated(args, tmpdir, monkeypatch):
def test_is_outdated(tmpdir, monkeypatch):
func = pmb.helpers.git.is_outdated
# Override time.time(): now is "100"
@ -187,8 +187,8 @@ def test_is_outdated(args, tmpdir, monkeypatch):
# Outdated (date_outdated: 90)
monkeypatch.setattr(pmb.config, "git_repo_outdated", 10)
assert func(args, path) is True
assert func(path) is True
# Not outdated (date_outdated: 89)
monkeypatch.setattr(pmb.config, "git_repo_outdated", 11)
assert func(args, path) is False
assert func(path) is False

View File

@ -85,7 +85,7 @@ def test_print_checks_git_repo(args, monkeypatch, tmpdir):
assert status == 0
# Outdated remote information
def is_outdated(args, path):
def is_outdated(path):
return True
monkeypatch.setattr(pmb.helpers.git, "is_outdated", is_outdated)
status, _ = func(args, name_repo)

View File

@ -23,20 +23,20 @@ def args(tmpdir, request):
def test_kconfig_check(args):
# basic checks, from easiers to hard-ish
dir = f"{pmb_test.const.testdata}/kconfig_check/"
assert not pmb.parse.kconfig.check_file(args, dir +
assert not pmb.parse.kconfig.check_file(dir +
"bad-missing-required-option")
assert pmb.parse.kconfig.check_file(args, dir + "good")
assert not pmb.parse.kconfig.check_file(args, dir + "bad-wrong-option-set")
assert pmb.parse.kconfig.check_file(args, dir + "good-anbox",
assert pmb.parse.kconfig.check_file(dir + "good")
assert not pmb.parse.kconfig.check_file(dir + "bad-wrong-option-set")
assert pmb.parse.kconfig.check_file(dir + "good-anbox",
anbox=True)
assert not pmb.parse.kconfig.check_file(args, dir +
assert not pmb.parse.kconfig.check_file(dir +
"bad-array-missing-some-options",
anbox=True)
assert pmb.parse.kconfig.check_file(args, dir + "good-nftables",
assert pmb.parse.kconfig.check_file(dir + "good-nftables",
nftables=True)
assert not pmb.parse.kconfig.check_file(args, dir + "bad-nftables",
assert not pmb.parse.kconfig.check_file(dir + "bad-nftables",
nftables=True)
assert pmb.parse.kconfig.check_file(args, dir + "good-zram",
assert pmb.parse.kconfig.check_file(dir + "good-zram",
zram=True)
# tests on real devices

View File

@ -27,8 +27,8 @@ def test_keys(args):
# Get the alpine-keys apk filename
pmb.chroot.init(args)
version = pmb.parse.apkindex.package(args, "alpine-keys")["version"]
pattern = (args.work + "/cache_apk_" + args.arch_native + "/alpine-keys-" +
version + ".*.apk")
pattern = (args.work + "/cache_apk_" + pmb.config.arch_native +
"/alpine-keys-" + version + ".*.apk")
filename = os.path.basename(glob.glob(pattern)[0])
# Extract it to a temporary folder

View File

@ -23,7 +23,7 @@ def args(tmpdir, request):
return args
def test_parse_next_block_exceptions(args):
def test_parse_next_block_exceptions():
# Mapping of input files (inside the /test/testdata/apkindex) to
# error message substrings
mapping = {"key_twice": "specified twice",
@ -37,11 +37,11 @@ def test_parse_next_block_exceptions(args):
lines = handle.readlines()
with pytest.raises(RuntimeError) as e:
pmb.parse.apkindex.parse_next_block(args, path, lines, [0])
pmb.parse.apkindex.parse_next_block(path, lines, [0])
assert error_substr in str(e.value)
def test_parse_next_block_no_error(args):
def test_parse_next_block_no_error():
# Read the file
func = pmb.parse.apkindex.parse_next_block
path = pmb.config.pmb_src + "/test/testdata/apkindex/no_error"
@ -57,7 +57,7 @@ def test_parse_next_block_no_error(args):
'provides': ['so:libc.musl-x86_64.so.1'],
'timestamp': '1515217616',
'version': '1.1.18-r5'}
assert func(args, path, lines, start) == block
assert func(path, lines, start) == block
assert start == [24]
# Second block
@ -71,15 +71,15 @@ def test_parse_next_block_no_error(args):
'provides': ['cmd:curl'],
'timestamp': '1512030418',
'version': '7.57.0-r0'}
assert func(args, path, lines, start) == block
assert func(path, lines, start) == block
assert start == [45]
# No more blocks
assert func(args, path, lines, start) is None
assert func(path, lines, start) is None
assert start == [45]
def test_parse_next_block_virtual(args):
def test_parse_next_block_virtual():
"""
Test parsing a virtual package from an APKINDEX.
"""
@ -98,7 +98,7 @@ def test_parse_next_block_virtual(args):
'provides': ['cmd:hello-world'],
'timestamp': '1500000000',
'version': '2-r0'}
assert func(args, path, lines, start) == block
assert func(path, lines, start) == block
assert start == [20]
# Second block: virtual package
@ -107,11 +107,11 @@ def test_parse_next_block_virtual(args):
'pkgname': '.pmbootstrap',
'provides': [],
'version': '0'}
assert func(args, path, lines, start) == block
assert func(path, lines, start) == block
assert start == [31]
# No more blocks
assert func(args, path, lines, start) is None
assert func(path, lines, start) is None
assert start == [31]

View File

@ -142,7 +142,7 @@ def test_pkgrel_bump_high_level(args, tmpdir):
# Delete package with previous soname (--auto-dry exits with >0 now)
channel = pmb.config.pmaports.read_config(args)["channel"]
arch = args.arch_native
arch = pmb.config.arch_native
apk_path = f"{tmpdir}/packages/{channel}/{arch}/testlib-1.0-r0.apk"
pmb.helpers.run.root(args, ["rm", apk_path])
pmbootstrap(args, tmpdir, ["index"])

View File

@ -11,6 +11,7 @@ import pmb.aportgen.device
import pmb.config
import pmb.config.init
import pmb.helpers.logging
import pmb.parse.deviceinfo
@pytest.fixture
@ -211,19 +212,20 @@ def test_questions_flash_methods(args, monkeypatch):
def test_questions_keymaps(args, monkeypatch):
func = pmb.config.init.ask_for_keymaps
fake_answers(monkeypatch, ["invalid_keymap", "us/rx51_us"])
assert func(args, "nokia-n900") == "us/rx51_us"
assert func(args, "lg-mako") == ""
assert func(args, pmb.parse.deviceinfo(args, "nokia-n900")) == "us/rx51_us"
assert func(args, pmb.parse.deviceinfo(args, "lg-mako")) == ""
def test_questions_ui(args, monkeypatch):
args.aports = pmb_test.const.testdata + "/init_questions_device/aports"
device = "lg-mako"
info = pmb.parse.deviceinfo(args, device)
fake_answers(monkeypatch, ["none"])
assert pmb.config.init.ask_for_ui(args, device) == "none"
assert pmb.config.init.ask_for_ui(args, info) == "none"
fake_answers(monkeypatch, ["invalid_UI", "weston"])
assert pmb.config.init.ask_for_ui(args, device) == "weston"
assert pmb.config.init.ask_for_ui(args, info) == "weston"
def test_questions_ui_extras(args, monkeypatch):

View File

@ -25,10 +25,12 @@ CONFIG_BLK_DEV_INITRD=y
CONFIG_CGROUPS=y
CONFIG_DEVTMPFS=y
CONFIG_DM_CRYPT=y
CONFIG_INPUT_EVDEV=y
CONFIG_SYSVIPC=y
CONFIG_VT=y
CONFIG_UEVENT_HELPER=y
CONFIG_LBDAF=y
CONFIG_CRYPTO_AES=y
CONFIG_CRYPTO_XTS=y
CONFIG_TMPFS_POSIX_ACL=y
### here's one explicitely disabled:

View File

@ -25,10 +25,12 @@ CONFIG_BLK_DEV_INITRD=y
CONFIG_CGROUPS=y
CONFIG_DEVTMPFS=y
CONFIG_DM_CRYPT=y
CONFIG_INPUT_EVDEV=y
CONFIG_SYSVIPC=y
CONFIG_VT=y
CONFIG_UEVENT_HELPER=y
CONFIG_LBDAF=y
CONFIG_CRYPTO_AES=y
CONFIG_CRYPTO_XTS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_EXT4_FS=y

View File

@ -8,10 +8,12 @@ CONFIG_BLK_DEV_INITRD=y
CONFIG_CGROUPS=y
CONFIG_DEVTMPFS=y
CONFIG_DM_CRYPT=y
CONFIG_INPUT_EVDEV=y
CONFIG_SYSVIPC=y
CONFIG_VT=y
CONFIG_UEVENT_HELPER=y
CONFIG_LBDAF=y
CONFIG_CRYPTO_AES=y
CONFIG_CRYPTO_XTS=y
CONFIG_TMPFS_POSIX_ACL=y
### here's one explicitely disabled:

View File

@ -31,10 +31,12 @@ CONFIG_BLK_DEV_INITRD=y
CONFIG_CGROUPS=y
CONFIG_DEVTMPFS=y
CONFIG_DM_CRYPT=y
CONFIG_INPUT_EVDEV=y
CONFIG_SYSVIPC=y
CONFIG_VT=y
CONFIG_UEVENT_HELPER=y
CONFIG_LBDAF=y
CONFIG_CRYPTO_AES=y
CONFIG_CRYPTO_XTS=y
CONFIG_TMPFS_POSIX_ACL=y
### here's one explicitely disabled: