WIP: start ripping out args (MR 2252)

Cease merging pmbootstrap.cfg into args, implement a Context type to let
us pull globals out of thin air (as an intermediate workaround) and rip
args out of a lot of the codebase.

This is just a first pass, after this we can split all the state that
leaked over into Context into types with narrower scopes (like a
BuildContext(), etc).

Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
This commit is contained in:
Caleb Connolly 2024-05-25 03:59:04 +02:00 committed by Oliver Smith
parent bfea00e03a
commit 34dd9d42ba
No known key found for this signature in database
GPG key ID: 5AE7F5513E0885CB
129 changed files with 1393 additions and 1300 deletions

View file

@ -63,13 +63,13 @@ they contain untrusted input):
```py ```py
# Does not work, the command does not run in a shell! # Does not work, the command does not run in a shell!
pmb.chroot.root(args, ["echo", "test", ">", "/tmp/test"]) pmb.chroot.root(["echo", "test", ">", "/tmp/test"])
# Use this instead (assuming untrusted input for text, dest) # Use this instead (assuming untrusted input for text, dest)
text = "test" text = "test"
dest = "/tmp/test" dest = "/tmp/test"
shell_cmd = f"echo {shutil.quote(text)} > {shutil.quote(dest)}" shell_cmd = f"echo {shutil.quote(text)} > {shutil.quote(dest)}"
pmb.chroot.root(args, ["sh", "-c", shell_cmd]) pmb.chroot.root(["sh", "-c", shell_cmd])
``` ```
If you need to run many commands in a shell at once, write them into a If you need to run many commands in a shell at once, write them into a
@ -83,7 +83,7 @@ the chroot. Use one of the following methods.
##### Short files ##### Short files
```py ```py
pmb.chroot.user(args, ["sh", "-c", f"echo {shlex.quote(hostname)}" pmb.chroot.user(["sh", "-c", f"echo {shlex.quote(hostname)}"
" > /etc/hostname"], suffix) " > /etc/hostname"], suffix)
``` ```
@ -95,8 +95,8 @@ with open("tmp/somefile", "w") as handle:
handle.write("Some long file") handle.write("Some long file")
handle.write("with multiple") handle.write("with multiple")
handle.write("lines here") handle.write("lines here")
pmb.chroot.root(args, ["mv", "/tmp/somefile", "/etc/somefile"]) pmb.chroot.root(["mv", "/tmp/somefile", "/etc/somefile"])
pmb.chroot.root(args, ["chown", "root:root", "/etc/somefile"], suffix) pmb.chroot.root(["chown", "root:root", "/etc/somefile"], suffix)
``` ```
### Manual testing ### Manual testing

View file

@ -10,6 +10,7 @@ from pmb.helpers.exceptions import BuildFailedError, NonBugError
from . import config from . import config
from . import parse from . import parse
from . import types
from .config import init as config_init from .config import init as config_init
from .helpers import frontend from .helpers import frontend
from .helpers import logging from .helpers import logging
@ -31,8 +32,8 @@ if version < (3, 9):
def print_log_hint() -> None: def print_log_hint() -> None:
context = get_context() context = get_context(allow_failure=True)
log = context.log log = context.log if context else types.Config().work / "log.txt"
# Hints about the log file (print to stdout only) # Hints about the log file (print to stdout only)
log_hint = "Run 'pmbootstrap log' for details." log_hint = "Run 'pmbootstrap log' for details."
if not os.path.exists(log): if not os.path.exists(log):
@ -50,6 +51,7 @@ def main() -> int:
try: try:
# Parse arguments, set up logging # Parse arguments, set up logging
args = parse.arguments() args = parse.arguments()
context = get_context()
os.umask(0o22) os.umask(0o22)
# Store script invocation command # Store script invocation command
@ -66,7 +68,7 @@ def main() -> int:
elif not os.path.exists(args.config): elif not os.path.exists(args.config):
raise RuntimeError("Please specify a config file, or run" raise RuntimeError("Please specify a config file, or run"
" 'pmbootstrap init' to generate one.") " 'pmbootstrap init' to generate one.")
elif not os.path.exists(config.work): elif not os.path.exists(context.config.work):
raise RuntimeError("Work path not found, please run 'pmbootstrap" raise RuntimeError("Work path not found, please run 'pmbootstrap"
" init' to create it.") " init' to create it.")

View file

@ -1,6 +1,7 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import os import os
from pmb.core import get_context
from pmb.helpers import logging from pmb.helpers import logging
import pmb.aportgen.busybox_static import pmb.aportgen.busybox_static
import pmb.aportgen.device import pmb.aportgen.device
@ -9,7 +10,7 @@ import pmb.aportgen.linux
import pmb.aportgen.musl import pmb.aportgen.musl
import pmb.aportgen.grub_efi import pmb.aportgen.grub_efi
import pmb.config import pmb.config
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.cli import pmb.helpers.cli
@ -58,7 +59,8 @@ def generate(args: PmbArgs, pkgname):
{"confirm_overwrite": True}) {"confirm_overwrite": True})
else: else:
prefix, folder, options = properties(pkgname) prefix, folder, options = properties(pkgname)
path_target = args.aports / folder / pkgname config = get_context().config
path_target = config.aports / folder / pkgname
# Confirm overwrite # Confirm overwrite
if options["confirm_overwrite"] and os.path.exists(path_target): if options["confirm_overwrite"] and os.path.exists(path_target):
@ -67,7 +69,7 @@ def generate(args: PmbArgs, pkgname):
if not pmb.helpers.cli.confirm(args, "Continue and overwrite?"): if not pmb.helpers.cli.confirm(args, "Continue and overwrite?"):
raise RuntimeError("Aborted.") raise RuntimeError("Aborted.")
aportgen = pmb.config.work / "aportgen" aportgen = config.work / "aportgen"
if os.path.exists(aportgen): if os.path.exists(aportgen):
pmb.helpers.run.user(["rm", "-r", aportgen]) pmb.helpers.run.user(["rm", "-r", aportgen])
@ -85,6 +87,6 @@ def generate(args: PmbArgs, pkgname):
if os.path.exists(path_target): if os.path.exists(path_target):
pmb.helpers.run.user(["rm", "-r", path_target]) pmb.helpers.run.user(["rm", "-r", path_target])
pmb.helpers.run.user( pmb.helpers.run.user(
args, ["mv", aportgen, path_target]) ["mv", aportgen, path_target])
logging.info("*** pmaport generated: " + path_target) logging.info("*** pmaport generated: " + path_target)

View file

@ -5,30 +5,31 @@ import pmb.aportgen.core
import pmb.build import pmb.build
import pmb.chroot.apk import pmb.chroot.apk
import pmb.chroot.apk_static import pmb.chroot.apk_static
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.run import pmb.helpers.run
import pmb.parse.apkindex import pmb.parse.apkindex
from pmb.core import Chroot from pmb.core import Chroot, get_context
def generate(args: PmbArgs, pkgname): def generate(args: PmbArgs, pkgname):
arch = pkgname.split("-")[2] arch = pkgname.split("-")[2]
context = get_context()
# Parse version from APKINDEX # Parse version from APKINDEX
package_data = pmb.parse.apkindex.package(args, "busybox") package_data = pmb.parse.apkindex.package("busybox")
version = package_data["version"] version = package_data["version"]
pkgver = version.split("-r")[0] pkgver = version.split("-r")[0]
pkgrel = version.split("-r")[1] pkgrel = version.split("-r")[1]
# Prepare aportgen tempdir inside and outside of chroot # Prepare aportgen tempdir inside and outside of chroot
tempdir = Path("/tmp/aportgen") tempdir = Path("/tmp/aportgen")
aportgen = pmb.config.work / "aportgen" aportgen = context.config.work / "aportgen"
pmb.chroot.root(args, ["rm", "-rf", tempdir]) pmb.chroot.root(["rm", "-rf", tempdir])
pmb.helpers.run.user(["mkdir", "-p", aportgen, pmb.helpers.run.user(["mkdir", "-p", aportgen,
Chroot.native() / tempdir]) Chroot.native() / tempdir])
# Write the APKBUILD # Write the APKBUILD
channel_cfg = pmb.config.pmaports.read_config_channel(args) channel_cfg = pmb.config.pmaports.read_config_channel()
mirrordir = channel_cfg["mirrordir_alpine"] mirrordir = channel_cfg["mirrordir_alpine"]
apkbuild_path = Chroot.native() / tempdir / "APKBUILD" apkbuild_path = Chroot.native() / tempdir / "APKBUILD"
apk_name = f"busybox-static-$pkgver-r$pkgrel-$_arch-{mirrordir}.apk" apk_name = f"busybox-static-$pkgver-r$pkgrel-$_arch-{mirrordir}.apk"
@ -71,7 +72,7 @@ def generate(args: PmbArgs, pkgname):
handle.write(line[12:].replace(" " * 4, "\t") + "\n") handle.write(line[12:].replace(" " * 4, "\t") + "\n")
# Generate checksums # Generate checksums
pmb.build.init_abuild_minimal(args) pmb.build.init_abuild_minimal()
pmb.chroot.root(args, ["chown", "-R", "pmos:pmos", tempdir]) pmb.chroot.root(["chown", "-R", "pmos:pmos", tempdir])
pmb.chroot.user(args, ["abuild", "checksum"], working_dir=tempdir) pmb.chroot.user(["abuild", "checksum"], working_dir=tempdir)
pmb.helpers.run.user(["cp", apkbuild_path, aportgen]) pmb.helpers.run.user(["cp", apkbuild_path, aportgen])

View file

@ -3,9 +3,10 @@
import fnmatch import fnmatch
from pmb.helpers import logging from pmb.helpers import logging
import re import re
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.git import pmb.helpers.git
import pmb.helpers.run import pmb.helpers.run
from pmb.core import get_context
def indent_size(line): def indent_size(line):
@ -48,7 +49,7 @@ def format_function(name, body, remove_indent=4):
return name + "() {\n" + ret + "}\n" return name + "() {\n" + ret + "}\n"
def rewrite(args: PmbArgs, pkgname, path_original="", fields={}, replace_pkgname=None, def rewrite(pkgname, path_original="", fields={}, replace_pkgname=None,
replace_functions={}, replace_simple={}, below_header="", replace_functions={}, replace_simple={}, below_header="",
remove_indent=4): remove_indent=4):
""" """
@ -93,7 +94,7 @@ def rewrite(args: PmbArgs, pkgname, path_original="", fields={}, replace_pkgname
lines_new += line.rstrip() + "\n" lines_new += line.rstrip() + "\n"
# Copy/modify lines, skip Maintainer/Contributor # Copy/modify lines, skip Maintainer/Contributor
path = pmb.config.work / "aportgen/APKBUILD" path = get_context().config.work / "aportgen/APKBUILD"
with open(path, "r+", encoding="utf-8") as handle: with open(path, "r+", encoding="utf-8") as handle:
skip_in_func = False skip_in_func = False
for line in handle.readlines(): for line in handle.readlines():
@ -165,14 +166,14 @@ def get_upstream_aport(args: PmbArgs, pkgname, arch=None):
""" """
# APKBUILD # APKBUILD
pmb.helpers.git.clone("aports_upstream") pmb.helpers.git.clone("aports_upstream")
aports_upstream_path = pmb.config.work / "cache_git/aports_upstream" aports_upstream_path = get_context().config.work / "cache_git/aports_upstream"
if getattr(args, "fork_alpine_retain_branch", False): if getattr(args, "fork_alpine_retain_branch", False):
logging.info("Not changing aports branch as --fork-alpine-retain-branch was " logging.info("Not changing aports branch as --fork-alpine-retain-branch was "
"used.") "used.")
else: else:
# Checkout branch # Checkout branch
channel_cfg = pmb.config.pmaports.read_config_channel(args) channel_cfg = pmb.config.pmaports.read_config_channel()
branch = channel_cfg["branch_aports"] branch = channel_cfg["branch_aports"]
logging.info(f"Checkout aports.git branch: {branch}") logging.info(f"Checkout aports.git branch: {branch}")
if pmb.helpers.run.user(["git", "checkout", branch], if pmb.helpers.run.user(["git", "checkout", branch],
@ -202,8 +203,8 @@ def get_upstream_aport(args: PmbArgs, pkgname, arch=None):
split = aport_path.parts split = aport_path.parts
repo = split[-2] repo = split[-2]
pkgname = split[-1] pkgname = split[-1]
index_path = pmb.helpers.repo.alpine_apkindex_path(args, repo, arch) index_path = pmb.helpers.repo.alpine_apkindex_path(repo, arch)
package = pmb.parse.apkindex.package(args, pkgname, indexes=[index_path]) package = pmb.parse.apkindex.package(pkgname, indexes=[index_path])
# Compare version (return when equal) # Compare version (return when equal)
compare = pmb.parse.version.compare(apkbuild_version, package["version"]) compare = pmb.parse.version.compare(apkbuild_version, package["version"])

View file

@ -1,8 +1,9 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pmb.core import get_context
from pmb.helpers import logging from pmb.helpers import logging
import os import os
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.cli import pmb.helpers.cli
import pmb.helpers.run import pmb.helpers.run
import pmb.aportgen.core import pmb.aportgen.core
@ -237,8 +238,9 @@ def generate_deviceinfo(args: PmbArgs, pkgname, name, manufacturer, year, arch,
content += content_uuu content += content_uuu
# Write to file # Write to file
pmb.helpers.run.user(["mkdir", "-p", pmb.config.work / "aportgen"]) work = get_context().config.work
path = pmb.config.work / "aportgen/deviceinfo" pmb.helpers.run.user(["mkdir", "-p", work / "aportgen"])
path = work / "aportgen/deviceinfo"
with open(path, "w", encoding="utf-8") as handle: with open(path, "w", encoding="utf-8") as handle:
for line in content.rstrip().split("\n"): for line in content.rstrip().split("\n"):
handle.write(line.lstrip() + "\n") handle.write(line.lstrip() + "\n")
@ -258,8 +260,9 @@ def generate_modules_initfs(args: PmbArgs):
""" """
# Write to file # Write to file
pmb.helpers.run.user(["mkdir", "-p", pmb.config.work / "aportgen"]) work = get_context().config.work
path = pmb.config.work / "aportgen/modules-initfs" pmb.helpers.run.user(["mkdir", "-p", work / "aportgen"])
path = work / "aportgen/modules-initfs"
with open(path, "w", encoding="utf-8") as handle: with open(path, "w", encoding="utf-8") as handle:
for line in content.rstrip().split("\n"): for line in content.rstrip().split("\n"):
handle.write(line.lstrip() + "\n") handle.write(line.lstrip() + "\n")
@ -308,8 +311,9 @@ def generate_apkbuild(args: PmbArgs, pkgname, name, arch, flash_method):
""" """
# Write the file # Write the file
pmb.helpers.run.user(["mkdir", "-p", pmb.config.work / "aportgen"]) work = get_context().config.work
path = pmb.config.work / "aportgen/APKBUILD" pmb.helpers.run.user(["mkdir", "-p", work / "aportgen"])
path = work / "aportgen/APKBUILD"
with open(path, "w", encoding="utf-8") as handle: with open(path, "w", encoding="utf-8") as handle:
for line in content.rstrip().split("\n"): for line in content.rstrip().split("\n"):
handle.write(line[8:].replace(" " * 4, "\t") + "\n") handle.write(line[8:].replace(" " * 4, "\t") + "\n")

View file

@ -1,7 +1,8 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import pmb.aportgen.core import pmb.aportgen.core
from pmb.core.types import PmbArgs from pmb.core import get_context
from pmb.types import PmbArgs
import pmb.helpers.git import pmb.helpers.git
import pmb.helpers.run import pmb.helpers.run
@ -10,19 +11,20 @@ def generate(args: PmbArgs, pkgname):
# Copy original aport # Copy original aport
prefix = pkgname.split("-")[0] prefix = pkgname.split("-")[0]
arch = pkgname.split("-")[1] arch = pkgname.split("-")[1]
context = get_context()
if prefix == "gcc": if prefix == "gcc":
upstream = pmb.aportgen.core.get_upstream_aport(args, "gcc", arch) upstream = pmb.aportgen.core.get_upstream_aport(args, "gcc", arch)
based_on = "main/gcc (from Alpine)" based_on = "main/gcc (from Alpine)"
elif prefix == "gcc4": elif prefix == "gcc4":
upstream = f"{args.aports}/main/gcc4" upstream = f"{context.config.aports}/main/gcc4"
based_on = "main/gcc4 (from postmarketOS)" based_on = "main/gcc4 (from postmarketOS)"
elif prefix == "gcc6": elif prefix == "gcc6":
upstream = f"{args.aports}/main/gcc6" upstream = f"{context.config.aports}/main/gcc6"
based_on = "main/gcc6 (from postmarketOS)" based_on = "main/gcc6 (from postmarketOS)"
else: else:
raise ValueError(f"Invalid prefix '{prefix}', expected gcc, gcc4 or" raise ValueError(f"Invalid prefix '{prefix}', expected gcc, gcc4 or"
" gcc6.") " gcc6.")
pmb.helpers.run.user(["cp", "-r", upstream, pmb.config.work / "aportgen"]) pmb.helpers.run.user(["cp", "-r", upstream, context.config.work / "aportgen"])
# Rewrite APKBUILD # Rewrite APKBUILD
fields = { fields = {
@ -88,6 +90,6 @@ def generate(args: PmbArgs, pkgname):
'_libgcc=true*': '_libgcc=false', '_libgcc=true*': '_libgcc=false',
} }
pmb.aportgen.core.rewrite(args, pkgname, based_on, fields, pmb.aportgen.core.rewrite(pkgname, based_on, fields,
replace_simple=replace_simple, replace_simple=replace_simple,
below_header=below_header) below_header=below_header)

View file

@ -5,30 +5,31 @@ import pmb.aportgen.core
import pmb.build import pmb.build
import pmb.chroot.apk import pmb.chroot.apk
import pmb.chroot.apk_static import pmb.chroot.apk_static
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.run import pmb.helpers.run
import pmb.parse.apkindex import pmb.parse.apkindex
from pmb.core import Chroot from pmb.core import Chroot, get_context
def generate(args: PmbArgs, pkgname): def generate(args: PmbArgs, pkgname):
arch = "x86" arch = "x86"
if pkgname != "grub-efi-x86": if pkgname != "grub-efi-x86":
raise RuntimeError("only grub-efi-x86 is available") raise RuntimeError("only grub-efi-x86 is available")
package_data = pmb.parse.apkindex.package(args, "grub") package_data = pmb.parse.apkindex.package("grub")
version = package_data["version"] version = package_data["version"]
pkgver = version.split("-r")[0] pkgver = version.split("-r")[0]
pkgrel = version.split("-r")[1] pkgrel = version.split("-r")[1]
# Prepare aportgen tempdir inside and outside of chroot # Prepare aportgen tempdir inside and outside of chroot
context = get_context()
tempdir = Path("/tmp/aportgen") tempdir = Path("/tmp/aportgen")
aportgen = pmb.config.work / "aportgen" aportgen = context.config.work / "aportgen"
pmb.chroot.root(args, ["rm", "-rf", tempdir]) pmb.chroot.root(["rm", "-rf", tempdir])
pmb.helpers.run.user(["mkdir", "-p", aportgen, pmb.helpers.run.user(["mkdir", "-p", aportgen,
Chroot.native() / tempdir]) Chroot.native() / tempdir])
# Write the APKBUILD # Write the APKBUILD
channel_cfg = pmb.config.pmaports.read_config_channel(args) channel_cfg = pmb.config.pmaports.read_config_channel()
mirrordir = channel_cfg["mirrordir_alpine"] mirrordir = channel_cfg["mirrordir_alpine"]
apkbuild_path = Chroot.native() / tempdir / "APKBUILD" apkbuild_path = Chroot.native() / tempdir / "APKBUILD"
apk_name = f'"$srcdir/grub-efi-$pkgver-r$pkgrel-$_arch-{mirrordir}.apk"' apk_name = f'"$srcdir/grub-efi-$pkgver-r$pkgrel-$_arch-{mirrordir}.apk"'
@ -61,7 +62,7 @@ def generate(args: PmbArgs, pkgname):
handle.write(line[12:].replace(" " * 4, "\t") + "\n") handle.write(line[12:].replace(" " * 4, "\t") + "\n")
# Generate checksums # Generate checksums
pmb.build.init_abuild_minimal(args) pmb.build.init_abuild_minimal()
pmb.chroot.root(args, ["chown", "-R", "pmos:pmos", tempdir]) pmb.chroot.root(["chown", "-R", "pmos:pmos", tempdir])
pmb.chroot.user(args, ["abuild", "checksum"], working_dir=tempdir) pmb.chroot.user(["abuild", "checksum"], working_dir=tempdir)
pmb.helpers.run.user(["cp", apkbuild_path, aportgen]) pmb.helpers.run.user(["cp", apkbuild_path, aportgen])

View file

@ -1,6 +1,7 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pmb.core.types import PmbArgs from pmb.core import get_context
from pmb.types import PmbArgs
import pmb.helpers.run import pmb.helpers.run
import pmb.aportgen.core import pmb.aportgen.core
import pmb.parse.apkindex import pmb.parse.apkindex
@ -105,17 +106,18 @@ def generate_apkbuild(args: PmbArgs, pkgname, deviceinfo, patches):
""" """
# Write the file # Write the file
with (pmb.config.work / "aportgen/APKBUILD").open("w", encoding="utf-8") as hndl: with (get_context().config.work / "aportgen/APKBUILD").open("w", encoding="utf-8") as hndl:
for line in content.rstrip().split("\n"): for line in content.rstrip().split("\n"):
hndl.write(line[8:].replace(" " * 4, "\t") + "\n") hndl.write(line[8:].replace(" " * 4, "\t") + "\n")
def generate(args: PmbArgs, pkgname): def generate(args: PmbArgs, pkgname):
device = "-".join(pkgname.split("-")[1:]) device = "-".join(pkgname.split("-")[1:])
deviceinfo = pmb.parse.deviceinfo(args, device) deviceinfo = pmb.parse.deviceinfo(device)
work = get_context().config.work
# Symlink commonly used patches # Symlink commonly used patches
pmb.helpers.run.user(["mkdir", "-p", pmb.config.work / "aportgen"]) pmb.helpers.run.user(["mkdir", "-p", work / "aportgen"])
patches = [ patches = [
"gcc7-give-up-on-ilog2-const-optimizations.patch", "gcc7-give-up-on-ilog2-const-optimizations.patch",
"gcc8-fix-put-user.patch", "gcc8-fix-put-user.patch",
@ -125,6 +127,6 @@ def generate(args: PmbArgs, pkgname):
for patch in patches: for patch in patches:
pmb.helpers.run.user(["ln", "-s", pmb.helpers.run.user(["ln", "-s",
"../../.shared-patches/linux/" + patch, "../../.shared-patches/linux/" + patch,
(pmb.config.work / "aportgen" / patch)]) (work / "aportgen" / patch)])
generate_apkbuild(args, pkgname, deviceinfo, patches) generate_apkbuild(args, pkgname, deviceinfo, patches)

View file

@ -5,30 +5,31 @@ import pmb.aportgen.core
import pmb.build import pmb.build
import pmb.chroot.apk import pmb.chroot.apk
import pmb.chroot.apk_static import pmb.chroot.apk_static
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.run import pmb.helpers.run
import pmb.parse.apkindex import pmb.parse.apkindex
from pmb.core import Chroot from pmb.core import Chroot, get_context
def generate(args: PmbArgs, pkgname): def generate(args: PmbArgs, pkgname):
arch = pkgname.split("-")[1] arch = pkgname.split("-")[1]
# Parse musl version from APKINDEX # Parse musl version from APKINDEX
package_data = pmb.parse.apkindex.package(args, "musl") package_data = pmb.parse.apkindex.package("musl")
version = package_data["version"] version = package_data["version"]
pkgver = version.split("-r")[0] pkgver = version.split("-r")[0]
pkgrel = version.split("-r")[1] pkgrel = version.split("-r")[1]
# Prepare aportgen tempdir inside and outside of chroot # Prepare aportgen tempdir inside and outside of chroot
work = get_context().config.work
tempdir = Path("/tmp/aportgen") tempdir = Path("/tmp/aportgen")
aportgen = pmb.config.work / "aportgen" aportgen = work / "aportgen"
pmb.chroot.root(args, ["rm", "-rf", tempdir]) pmb.chroot.root(["rm", "-rf", tempdir])
pmb.helpers.run.user(["mkdir", "-p", aportgen, pmb.helpers.run.user(["mkdir", "-p", aportgen,
Chroot.native() / tempdir]) Chroot.native() / tempdir])
# Write the APKBUILD # Write the APKBUILD
channel_cfg = pmb.config.pmaports.read_config_channel(args) channel_cfg = pmb.config.pmaports.read_config_channel()
mirrordir = channel_cfg["mirrordir_alpine"] mirrordir = channel_cfg["mirrordir_alpine"]
apkbuild_path = Chroot.native() / tempdir / "APKBUILD" apkbuild_path = Chroot.native() / tempdir / "APKBUILD"
apk_name = f"$srcdir/musl-$pkgver-r$pkgrel-$_arch-{mirrordir}.apk" apk_name = f"$srcdir/musl-$pkgver-r$pkgrel-$_arch-{mirrordir}.apk"
@ -97,7 +98,7 @@ def generate(args: PmbArgs, pkgname):
handle.write(line[12:].replace(" " * 4, "\t") + "\n") handle.write(line[12:].replace(" " * 4, "\t") + "\n")
# Generate checksums # Generate checksums
pmb.build.init_abuild_minimal(args) pmb.build.init_abuild_minimal()
pmb.chroot.root(args, ["chown", "-R", "pmos:pmos", tempdir]) pmb.chroot.root(["chown", "-R", "pmos:pmos", tempdir])
pmb.chroot.user(args, ["abuild", "checksum"], working_dir=tempdir) pmb.chroot.user(["abuild", "checksum"], working_dir=tempdir)
pmb.helpers.run.user(["cp", apkbuild_path, aportgen]) pmb.helpers.run.user(["cp", apkbuild_path, aportgen])

View file

@ -2,6 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import datetime import datetime
import enum import enum
from pmb.core.context import Context
from pmb.helpers import logging from pmb.helpers import logging
from pathlib import Path from pathlib import Path
@ -9,7 +10,7 @@ import pmb.build
import pmb.build.autodetect import pmb.build.autodetect
import pmb.chroot import pmb.chroot
import pmb.chroot.apk import pmb.chroot.apk
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.pmaports import pmb.helpers.pmaports
import pmb.helpers.repo import pmb.helpers.repo
import pmb.helpers.mount import pmb.helpers.mount
@ -18,7 +19,7 @@ import pmb.parse.arch
import pmb.parse.apkindex import pmb.parse.apkindex
from pmb.helpers.exceptions import BuildFailedError from pmb.helpers.exceptions import BuildFailedError
from pmb.core import Chroot from pmb.core import Chroot, get_context
class BootstrapStage(enum.IntEnum): class BootstrapStage(enum.IntEnum):
""" """
@ -48,7 +49,7 @@ def skip_already_built(pkgname, arch):
return False return False
def get_apkbuild(args: PmbArgs, pkgname, arch): def get_apkbuild(pkgname, arch):
"""Parse the APKBUILD path for pkgname. """Parse the APKBUILD path for pkgname.
When there is none, try to find it in the binary package APKINDEX files or raise an exception. When there is none, try to find it in the binary package APKINDEX files or raise an exception.
@ -57,19 +58,19 @@ def get_apkbuild(args: PmbArgs, pkgname, arch):
:returns: None or parsed APKBUILD :returns: None or parsed APKBUILD
""" """
# Get existing binary package indexes # Get existing binary package indexes
pmb.helpers.repo.update(args, arch) pmb.helpers.repo.update(arch)
# Get pmaport, skip upstream only packages # Get pmaport, skip upstream only packages
pmaport = pmb.helpers.pmaports.get(args, pkgname, False) pmaport = pmb.helpers.pmaports.get(pkgname, False)
if pmaport: if pmaport:
return pmaport return pmaport
if pmb.parse.apkindex.providers(args, pkgname, arch, False): if pmb.parse.apkindex.providers(pkgname, arch, False):
return None return None
raise RuntimeError("Package '" + pkgname + "': Could not find aport, and" raise RuntimeError("Package '" + pkgname + "': Could not find aport, and"
" could not find this package in any APKINDEX!") " could not find this package in any APKINDEX!")
def check_build_for_arch(args: PmbArgs, pkgname, arch): def check_build_for_arch(pkgname, arch):
"""Check if pmaport can be built or exists as binary for a specific arch. """Check if pmaport can be built or exists as binary for a specific arch.
:returns: * True when it can be built :returns: * True when it can be built
@ -78,14 +79,15 @@ def check_build_for_arch(args: PmbArgs, pkgname, arch):
:raises: RuntimeError if the package can't be built for the given arch and :raises: RuntimeError if the package can't be built for the given arch and
does not exist as binary package. does not exist as binary package.
""" """
context = get_context()
# Check for pmaport with arch # Check for pmaport with arch
if pmb.helpers.package.check_arch(args, pkgname, arch, False): if pmb.helpers.package.check_arch(pkgname, arch, False):
return True return True
# Check for binary package # Check for binary package
binary = pmb.parse.apkindex.package(args, pkgname, arch, False) binary = pmb.parse.apkindex.package(pkgname, arch, False)
if binary: if binary:
pmaport = pmb.helpers.pmaports.get(args, pkgname) pmaport = pmb.helpers.pmaports.get(pkgname)
pmaport_version = pmaport["pkgver"] + "-r" + pmaport["pkgrel"] pmaport_version = pmaport["pkgver"] + "-r" + pmaport["pkgrel"]
logging.debug(pkgname + ": found pmaport (" + pmaport_version + ") and" logging.debug(pkgname + ": found pmaport (" + pmaport_version + ") and"
" binary package (" + binary["version"] + ", from" " binary package (" + binary["version"] + ", from"
@ -95,7 +97,7 @@ def check_build_for_arch(args: PmbArgs, pkgname, arch):
# No binary package exists and can't build it # No binary package exists and can't build it
logging.info("NOTE: You can edit the 'arch=' line inside the APKBUILD") logging.info("NOTE: You can edit the 'arch=' line inside the APKBUILD")
if args.action == "build": if context.command == "build":
logging.info("NOTE: Alternatively, use --arch to build for another" logging.info("NOTE: Alternatively, use --arch to build for another"
" architecture ('pmbootstrap build --arch=armhf " + " architecture ('pmbootstrap build --arch=armhf " +
pkgname + "')") pkgname + "')")
@ -103,7 +105,7 @@ def check_build_for_arch(args: PmbArgs, pkgname, arch):
arch) arch)
def get_depends(args: PmbArgs, apkbuild): def get_depends(context: Context, apkbuild):
"""Alpine's abuild always builds/installs the "depends" and "makedepends" of a package """Alpine's abuild always builds/installs the "depends" and "makedepends" of a package
before building it. before building it.
@ -116,7 +118,7 @@ def get_depends(args: PmbArgs, apkbuild):
ret = list(apkbuild["makedepends"]) ret = list(apkbuild["makedepends"])
if "!check" not in apkbuild["options"]: if "!check" not in apkbuild["options"]:
ret += apkbuild["checkdepends"] ret += apkbuild["checkdepends"]
if "ignore_depends" not in args or not args.ignore_depends: if not context.ignore_depends:
ret += apkbuild["depends"] ret += apkbuild["depends"]
ret = sorted(set(ret)) ret = sorted(set(ret))
@ -130,35 +132,35 @@ def get_depends(args: PmbArgs, apkbuild):
return ret return ret
def build_depends(args: PmbArgs, apkbuild, arch, strict): def build_depends(context: Context, apkbuild, arch, strict):
"""Get and build dependencies with verbose logging messages. """Get and build dependencies with verbose logging messages.
:returns: (depends, depends_built) :returns: (depends, depends_built)
""" """
# Get dependencies # Get dependencies
pkgname = apkbuild["pkgname"] pkgname = apkbuild["pkgname"]
depends = get_depends(args, apkbuild) depends = get_depends(context, apkbuild)
logging.verbose(pkgname + ": build/install dependencies: " + logging.verbose(pkgname + ": build/install dependencies: " +
", ".join(depends)) ", ".join(depends))
# --no-depends: check for binary packages # --no-depends: check for binary packages
depends_built = [] depends_built = []
if "no_depends" in args and args.no_depends: if context.no_depends:
pmb.helpers.repo.update(args, arch) pmb.helpers.repo.update(arch)
for depend in depends: for depend in depends:
# Ignore conflicting dependencies # Ignore conflicting dependencies
if depend.startswith("!"): if depend.startswith("!"):
continue continue
# Check if binary package is missing # Check if binary package is missing
if not pmb.parse.apkindex.package(args, depend, arch, False): if not pmb.parse.apkindex.package(depend, arch, False):
raise RuntimeError("Missing binary package for dependency '" + raise RuntimeError("Missing binary package for dependency '" +
depend + "' of '" + pkgname + "', but" depend + "' of '" + pkgname + "', but"
" pmbootstrap won't build any depends since" " pmbootstrap won't build any depends since"
" it was started with --no-depends.") " it was started with --no-depends.")
# Check if binary package is outdated # Check if binary package is outdated
apkbuild_dep = get_apkbuild(args, depend, arch) apkbuild_dep = get_apkbuild(depend, arch)
if apkbuild_dep and \ if apkbuild_dep and \
pmb.build.is_necessary(args, arch, apkbuild_dep): pmb.build.is_necessary(arch, apkbuild_dep):
raise RuntimeError(f"Binary package for dependency '{depend}'" raise RuntimeError(f"Binary package for dependency '{depend}'"
f" of '{pkgname}' is outdated, but" f" of '{pkgname}' is outdated, but"
f" pmbootstrap won't build any depends" f" pmbootstrap won't build any depends"
@ -168,7 +170,7 @@ def build_depends(args: PmbArgs, apkbuild, arch, strict):
for depend in depends: for depend in depends:
if depend.startswith("!"): if depend.startswith("!"):
continue continue
if package(args, depend, arch, strict=strict): if package(context, depend, arch, strict=strict):
depends_built += [depend] depends_built += [depend]
logging.verbose(pkgname + ": build dependencies: done, built: " + logging.verbose(pkgname + ": build dependencies: done, built: " +
", ".join(depends_built)) ", ".join(depends_built))
@ -176,7 +178,7 @@ def build_depends(args: PmbArgs, apkbuild, arch, strict):
return (depends, depends_built) return (depends, depends_built)
def is_necessary_warn_depends(args: PmbArgs, apkbuild, arch, force, depends_built): def is_necessary_warn_depends(apkbuild, arch, force, depends_built):
"""Check if a build is necessary, and warn if it is not, but there were dependencies built. """Check if a build is necessary, and warn if it is not, but there were dependencies built.
:returns: True or False :returns: True or False
@ -185,7 +187,7 @@ def is_necessary_warn_depends(args: PmbArgs, apkbuild, arch, force, depends_buil
# Check if necessary (this warns about binary version > aport version, so # Check if necessary (this warns about binary version > aport version, so
# call it even in force mode) # call it even in force mode)
ret = pmb.build.is_necessary(args, arch, apkbuild) ret = pmb.build.is_necessary(arch, apkbuild)
if force: if force:
ret = True ret = True
@ -197,7 +199,7 @@ def is_necessary_warn_depends(args: PmbArgs, apkbuild, arch, force, depends_buil
return ret return ret
def init_buildenv(args: PmbArgs, apkbuild, arch, strict=False, force=False, cross=None, def init_buildenv(context: Context, apkbuild, arch, strict=False, force=False, cross=None,
chroot: Chroot = Chroot.native(), skip_init_buildenv=False, src=None): chroot: Chroot = Chroot.native(), skip_init_buildenv=False, src=None):
"""Build all dependencies. """Build all dependencies.
@ -219,30 +221,30 @@ def init_buildenv(args: PmbArgs, apkbuild, arch, strict=False, force=False, cros
depends_arch = pmb.config.arch_native depends_arch = pmb.config.arch_native
# Build dependencies # Build dependencies
depends, built = build_depends(args, apkbuild, depends_arch, strict) depends, built = build_depends(context, apkbuild, depends_arch, strict)
# Check if build is necessary # Check if build is necessary
if not is_necessary_warn_depends(args, apkbuild, arch, force, built): if not is_necessary_warn_depends(apkbuild, arch, force, built):
return False return False
# Install and configure abuild, ccache, gcc, dependencies # Install and configure abuild, ccache, gcc, dependencies
if not skip_init_buildenv: if not skip_init_buildenv:
pmb.build.init(args, chroot) pmb.build.init(chroot)
pmb.build.other.configure_abuild(args, chroot) pmb.build.other.configure_abuild(chroot)
if args.ccache: if context.ccache:
pmb.build.other.configure_ccache(args, chroot) pmb.build.other.configure_ccache(chroot)
if "rust" in depends or "cargo" in depends: if "rust" in depends or "cargo" in depends:
pmb.chroot.apk.install(args, ["sccache"], chroot) pmb.chroot.apk.install(["sccache"], chroot)
if not strict and "pmb:strict" not in apkbuild["options"] and len(depends): if not strict and "pmb:strict" not in apkbuild["options"] and len(depends):
pmb.chroot.apk.install(args, depends, chroot) pmb.chroot.apk.install(depends, chroot)
if src: if src:
pmb.chroot.apk.install(args, ["rsync"], chroot) pmb.chroot.apk.install(["rsync"], chroot)
# Cross-compiler init # Cross-compiler init
if cross: if cross:
pmb.build.init_compiler(args, depends, cross, arch) pmb.build.init_compiler(context, depends, cross, arch)
if cross == "crossdirect": if cross == "crossdirect":
pmb.chroot.mount_native_into_foreign(args, chroot) pmb.chroot.mount_native_into_foreign(chroot)
return True return True
@ -270,7 +272,7 @@ def get_pkgver(original_pkgver, original_source=False, now=None):
return no_suffix + new_suffix return no_suffix + new_suffix
def override_source(args: PmbArgs, apkbuild, pkgver, src, chroot: Chroot=Chroot.native()): def override_source(apkbuild, pkgver, src, chroot: Chroot=Chroot.native()):
"""Mount local source inside chroot and append new functions (prepare() etc.) """Mount local source inside chroot and append new functions (prepare() etc.)
to the APKBUILD to make it use the local source. to the APKBUILD to make it use the local source.
""" """
@ -286,7 +288,7 @@ def override_source(args: PmbArgs, apkbuild, pkgver, src, chroot: Chroot=Chroot.
append_path = "/tmp/APKBUILD.append" append_path = "/tmp/APKBUILD.append"
append_path_outside = chroot / append_path append_path_outside = chroot / append_path
if append_path_outside.exists(): if append_path_outside.exists():
pmb.chroot.root(args, ["rm", append_path], chroot) pmb.chroot.root(["rm", append_path], chroot)
# Add src path to pkgdesc, cut it off after max length # Add src path to pkgdesc, cut it off after max length
pkgdesc = ("[" + src + "] " + apkbuild["pkgdesc"])[:127] pkgdesc = ("[" + src + "] " + apkbuild["pkgdesc"])[:127]
@ -327,14 +329,14 @@ def override_source(args: PmbArgs, apkbuild, pkgver, src, chroot: Chroot=Chroot.
with open(append_path_outside, "w", encoding="utf-8") as handle: with open(append_path_outside, "w", encoding="utf-8") as handle:
for line in append.split("\n"): for line in append.split("\n"):
handle.write(line[13:].replace(" " * 4, "\t") + "\n") handle.write(line[13:].replace(" " * 4, "\t") + "\n")
pmb.chroot.user(args, ["cat", append_path], chroot) pmb.chroot.user(["cat", append_path], chroot)
# Append it to the APKBUILD # Append it to the APKBUILD
apkbuild_path = "/home/pmos/build/APKBUILD" apkbuild_path = "/home/pmos/build/APKBUILD"
shell_cmd = ("cat " + apkbuild_path + " " + append_path + " > " + shell_cmd = ("cat " + apkbuild_path + " " + append_path + " > " +
append_path + "_") append_path + "_")
pmb.chroot.user(args, ["sh", "-c", shell_cmd], chroot) pmb.chroot.user(["sh", "-c", shell_cmd], chroot)
pmb.chroot.user(args, ["mv", append_path + "_", apkbuild_path], chroot) pmb.chroot.user(["mv", append_path + "_", apkbuild_path], chroot)
def mount_pmaports(destination, chroot: Chroot=Chroot.native()): def mount_pmaports(destination, chroot: Chroot=Chroot.native()):
@ -344,10 +346,10 @@ def mount_pmaports(destination, chroot: Chroot=Chroot.native()):
:param destination: mount point inside the chroot :param destination: mount point inside the chroot
""" """
outside_destination = chroot / destination outside_destination = chroot / destination
pmb.helpers.mount.bind(args.aports, outside_destination, umount=True) pmb.helpers.mount.bind(get_context().config.aports, outside_destination, umount=True)
def link_to_git_dir(args: PmbArgs, suffix): def link_to_git_dir(suffix):
""" Make ``/home/pmos/build/.git`` point to the .git dir from pmaports.git, with a """ Make ``/home/pmos/build/.git`` point to the .git dir from pmaports.git, with a
symlink so abuild does not fail (#1841). symlink so abuild does not fail (#1841).
@ -367,15 +369,15 @@ def link_to_git_dir(args: PmbArgs, suffix):
# at that point. Use umount=True, so we don't have an old path mounted # at that point. Use umount=True, so we don't have an old path mounted
# (some tests change the pmaports dir). # (some tests change the pmaports dir).
destination = "/mnt/pmaports" destination = "/mnt/pmaports"
mount_pmaports(args, destination, suffix) mount_pmaports(destination, suffix)
# Create .git symlink # Create .git symlink
pmb.chroot.user(args, ["mkdir", "-p", "/home/pmos/build"], suffix) pmb.chroot.user(["mkdir", "-p", "/home/pmos/build"], suffix)
pmb.chroot.user(args, ["ln", "-sf", destination + "/.git", pmb.chroot.user(["ln", "-sf", destination + "/.git",
"/home/pmos/build/.git"], suffix) "/home/pmos/build/.git"], suffix)
def run_abuild(args: PmbArgs, apkbuild, arch, strict=False, force=False, cross=None, def run_abuild(context: Context, apkbuild, arch, strict=False, force=False, cross=None,
suffix: Chroot=Chroot.native(), src=None, bootstrap_stage=BootstrapStage.NONE): suffix: Chroot=Chroot.native(), src=None, bootstrap_stage=BootstrapStage.NONE):
""" """
Set up all environment variables and construct the abuild command (all Set up all environment variables and construct the abuild command (all
@ -415,11 +417,11 @@ def run_abuild(args: PmbArgs, apkbuild, arch, strict=False, force=False, cross=N
if cross == "crossdirect": if cross == "crossdirect":
env["PATH"] = ":".join(["/native/usr/lib/crossdirect/" + arch, env["PATH"] = ":".join(["/native/usr/lib/crossdirect/" + arch,
pmb.config.chroot_path]) pmb.config.chroot_path])
if not args.ccache: if not context.ccache:
env["CCACHE_DISABLE"] = "1" env["CCACHE_DISABLE"] = "1"
# Use sccache without crossdirect (crossdirect uses it via rustc.sh) # Use sccache without crossdirect (crossdirect uses it via rustc.sh)
if args.ccache and cross != "crossdirect": if context.ccache and cross != "crossdirect":
env["RUSTC_WRAPPER"] = "/usr/bin/sccache" env["RUSTC_WRAPPER"] = "/usr/bin/sccache"
# Cache binary objects from go in this path (like ccache) # Cache binary objects from go in this path (like ccache)
@ -431,7 +433,7 @@ def run_abuild(args: PmbArgs, apkbuild, arch, strict=False, force=False, cross=N
# e.g. when using --src they are not bundled, in that case it makes sense # e.g. when using --src they are not bundled, in that case it makes sense
# to point GOMODCACHE at pmbootstrap's work dir so the modules are only # to point GOMODCACHE at pmbootstrap's work dir so the modules are only
# downloaded once. # downloaded once.
if args.go_mod_cache: if context.go_mod_cache:
env["GOMODCACHE"] = "/home/pmos/go/pkg/mod" env["GOMODCACHE"] = "/home/pmos/go/pkg/mod"
if bootstrap_stage: if bootstrap_stage:
@ -450,18 +452,18 @@ def run_abuild(args: PmbArgs, apkbuild, arch, strict=False, force=False, cross=N
cmd += ["-f"] cmd += ["-f"]
# Copy the aport to the chroot and build it # Copy the aport to the chroot and build it
pmb.build.copy_to_buildpath(args, apkbuild["pkgname"], suffix) pmb.build.copy_to_buildpath(apkbuild["pkgname"], suffix)
override_source(args, apkbuild, pkgver, src, suffix) override_source(apkbuild, pkgver, src, suffix)
link_to_git_dir(args, suffix) link_to_git_dir(suffix)
pmb.chroot.user(args, cmd, suffix, Path("/home/pmos/build"), env=env) pmb.chroot.user(cmd, suffix, Path("/home/pmos/build"), env=env)
return (output, cmd, env) return (output, cmd, env)
def finish(args: PmbArgs, apkbuild, arch, output: str, chroot: Chroot, strict=False): def finish(apkbuild, arch, output: str, chroot: Chroot, strict=False):
"""Various finishing tasks that need to be done after a build.""" """Various finishing tasks that need to be done after a build."""
# Verify output file # Verify output file
channel: str = pmb.config.pmaports.read_config(args)["channel"] channel: str = pmb.config.pmaports.read_config()["channel"]
out_dir = (pmb.config.work / "packages" / channel) out_dir = (get_context().config.work / "packages" / channel)
if not (out_dir / output).exists(): if not (out_dir / output).exists():
raise RuntimeError(f"Package not found after build: {(out_dir / output)}") raise RuntimeError(f"Package not found after build: {(out_dir / output)}")
@ -473,14 +475,14 @@ def finish(args: PmbArgs, apkbuild, arch, output: str, chroot: Chroot, strict=Fa
# Uninstall build dependencies (strict mode) # Uninstall build dependencies (strict mode)
if strict or "pmb:strict" in apkbuild["options"]: if strict or "pmb:strict" in apkbuild["options"]:
logging.info(f"({chroot}) uninstall build dependencies") logging.info(f"({chroot}) uninstall build dependencies")
pmb.chroot.user(args, ["abuild", "undeps"], chroot, Path("/home/pmos/build"), pmb.chroot.user(["abuild", "undeps"], chroot, Path("/home/pmos/build"),
env={"SUDO_APK": "abuild-apk --no-progress"}) env={"SUDO_APK": "abuild-apk --no-progress"})
# If the build depends contain postmarketos-keys or postmarketos-base, # If the build depends contain postmarketos-keys or postmarketos-base,
# abuild will have removed the postmarketOS repository key (pma#1230) # abuild will have removed the postmarketOS repository key (pma#1230)
pmb.chroot.init_keys(args) pmb.chroot.init_keys()
def package(args: PmbArgs, pkgname, arch=None, force=False, strict=False, def package(context: Context, pkgname, arch=None, force=False, strict=False,
skip_init_buildenv=False, src=None, skip_init_buildenv=False, src=None,
bootstrap_stage=BootstrapStage.NONE): bootstrap_stage=BootstrapStage.NONE):
""" """
@ -515,24 +517,24 @@ def package(args: PmbArgs, pkgname, arch=None, force=False, strict=False,
return return
# Only build when APKBUILD exists # Only build when APKBUILD exists
apkbuild = get_apkbuild(args, pkgname, arch) apkbuild = get_apkbuild(pkgname, arch)
if not apkbuild: if not apkbuild:
return return
# Detect the build environment (skip unnecessary builds) # Detect the build environment (skip unnecessary builds)
if not check_build_for_arch(args, pkgname, arch): if not check_build_for_arch(pkgname, arch):
return return
chroot = pmb.build.autodetect.chroot(apkbuild, arch) chroot = pmb.build.autodetect.chroot(apkbuild, arch)
cross = pmb.build.autodetect.crosscompile(args, apkbuild, arch, chroot) cross = pmb.build.autodetect.crosscompile(apkbuild, arch, chroot)
if not init_buildenv(args, apkbuild, arch, strict, force, cross, chroot, if not init_buildenv(context, apkbuild, arch, strict, force, cross, chroot,
skip_init_buildenv, src): skip_init_buildenv, src):
return return
# Build and finish up # Build and finish up
try: try:
(output, cmd, env) = run_abuild(args, apkbuild, arch, strict, force, cross, (output, cmd, env) = run_abuild(context, apkbuild, arch, strict, force, cross,
chroot, src, bootstrap_stage) chroot, src, bootstrap_stage)
except RuntimeError: except RuntimeError:
raise BuildFailedError(f"Build for {arch}/{pkgname} failed!") raise BuildFailedError(f"Build for {arch}/{pkgname} failed!")
finish(args, apkbuild, arch, output, chroot, strict) finish(apkbuild, arch, output, chroot, strict)
return output return output

View file

@ -6,10 +6,10 @@ from typing import Dict, Optional
import pmb.config import pmb.config
import pmb.chroot.apk import pmb.chroot.apk
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.pmaports import pmb.helpers.pmaports
import pmb.parse.arch import pmb.parse.arch
from pmb.core import Chroot, ChrootType from pmb.core import Chroot, ChrootType, get_context
# FIXME (#2324): type hint Arch # FIXME (#2324): type hint Arch
@ -31,7 +31,7 @@ def arch_from_deviceinfo(args: PmbArgs, pkgname, aport: Path) -> Optional[str]:
# Return its arch # Return its arch
device = pkgname.split("-", 1)[1] device = pkgname.split("-", 1)[1]
arch = pmb.parse.deviceinfo(args, device)["arch"] arch = pmb.parse.deviceinfo(device)["arch"]
logging.verbose(pkgname + ": arch from deviceinfo: " + arch) logging.verbose(pkgname + ": arch from deviceinfo: " + arch)
return arch return arch
@ -47,7 +47,7 @@ def arch(args: PmbArgs, pkgname: str):
* device arch (this will be preferred instead if build_default_device_arch is true) * device arch (this will be preferred instead if build_default_device_arch is true)
* first arch in the APKBUILD * first arch in the APKBUILD
""" """
aport = pmb.helpers.pmaports.find(args, pkgname) aport = pmb.helpers.pmaports.find(pkgname)
if not aport: if not aport:
raise FileNotFoundError(f"APKBUILD not found for {pkgname}") raise FileNotFoundError(f"APKBUILD not found for {pkgname}")
ret = arch_from_deviceinfo(args, pkgname, aport) ret = arch_from_deviceinfo(args, pkgname, aport)
@ -57,7 +57,7 @@ def arch(args: PmbArgs, pkgname: str):
apkbuild = pmb.parse.apkbuild(aport) apkbuild = pmb.parse.apkbuild(aport)
arches = apkbuild["arch"] arches = apkbuild["arch"]
if args.build_default_device_arch: if get_context().config.build_default_device_arch:
preferred_arch = args.deviceinfo["arch"] preferred_arch = args.deviceinfo["arch"]
preferred_arch_2nd = pmb.config.arch_native preferred_arch_2nd = pmb.config.arch_native
else: else:
@ -86,11 +86,11 @@ def chroot(apkbuild: Dict[str, str], arch: str) -> Chroot:
return Chroot.buildroot(arch) return Chroot.buildroot(arch)
def crosscompile(args: PmbArgs, apkbuild, arch, suffix: Chroot): def crosscompile(apkbuild, arch, suffix: Chroot):
""" """
:returns: None, "native", "crossdirect" :returns: None, "native", "crossdirect"
""" """
if not args.cross: if not get_context().cross:
return None return None
if not pmb.parse.arch.cpu_emulation_required(arch): if not pmb.parse.arch.cpu_emulation_required(arch):
return None return None

View file

@ -6,7 +6,7 @@ from pathlib import Path
import pmb.chroot import pmb.chroot
import pmb.build import pmb.build
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.run import pmb.helpers.run
import pmb.helpers.pmaports import pmb.helpers.pmaports
from pmb.core import Chroot from pmb.core import Chroot
@ -14,25 +14,25 @@ from pmb.core import Chroot
def update(args: PmbArgs, pkgname): def update(args: PmbArgs, pkgname):
"""Fetch all sources and update the checksums in the APKBUILD.""" """Fetch all sources and update the checksums in the APKBUILD."""
pmb.build.init_abuild_minimal(args) pmb.build.init_abuild_minimal()
pmb.build.copy_to_buildpath(args, pkgname) pmb.build.copy_to_buildpath(args, pkgname)
logging.info("(native) generate checksums for " + pkgname) logging.info("(native) generate checksums for " + pkgname)
pmb.chroot.user(args, ["abuild", "checksum"], pmb.chroot.user(["abuild", "checksum"],
working_dir=Path("/home/pmos/build")) working_dir=Path("/home/pmos/build"))
# Copy modified APKBUILD back # Copy modified APKBUILD back
source = Chroot.native() / "home/pmos/build/APKBUILD" source = Chroot.native() / "home/pmos/build/APKBUILD"
target = f"{os.fspath(pmb.helpers.pmaports.find(args, pkgname))}/" target = f"{os.fspath(pmb.helpers.pmaports.find(pkgname))}/"
pmb.helpers.run.user(["cp", source, target]) pmb.helpers.run.user(["cp", source, target])
def verify(args: PmbArgs, pkgname): def verify(args: PmbArgs, pkgname):
"""Fetch all sources and verify their checksums.""" """Fetch all sources and verify their checksums."""
pmb.build.init_abuild_minimal(args) pmb.build.init_abuild_minimal()
pmb.build.copy_to_buildpath(args, pkgname) pmb.build.copy_to_buildpath(args, pkgname)
logging.info("(native) verify checksums for " + pkgname) logging.info("(native) verify checksums for " + pkgname)
# Fetch and verify sources, "fetch" alone does not verify them: # Fetch and verify sources, "fetch" alone does not verify them:
# https://github.com/alpinelinux/abuild/pull/86 # https://github.com/alpinelinux/abuild/pull/86
pmb.chroot.user(args, ["abuild", "fetch", "verify"], pmb.chroot.user(["abuild", "fetch", "verify"],
working_dir=Path("/home/pmos/build")) working_dir=Path("/home/pmos/build"))

View file

@ -7,15 +7,16 @@ from pathlib import Path
import re import re
import pmb.aportgen import pmb.aportgen
import pmb.aportgen.core
import pmb.build import pmb.build
import pmb.build.autodetect import pmb.build.autodetect
import pmb.chroot import pmb.chroot
from pmb.core.types import PathString, PmbArgs from pmb.types import PathString, PmbArgs
import pmb.helpers import pmb.helpers
import pmb.helpers.mount import pmb.helpers.mount
import pmb.helpers.pmaports import pmb.helpers.pmaports
import pmb.parse import pmb.parse
from pmb.core import Chroot from pmb.core import Chroot, get_context
def match_kbuild_out(word): def match_kbuild_out(word):
@ -90,16 +91,17 @@ def find_kbuild_output_dir(function_body):
"can't resolve it, please open an issue.") "can't resolve it, please open an issue.")
def modify_apkbuild(args: PmbArgs, pkgname: str, aport: Path): def modify_apkbuild(pkgname: str, aport: Path):
"""Modify kernel APKBUILD to package build output from envkernel.sh.""" """Modify kernel APKBUILD to package build output from envkernel.sh."""
apkbuild_path = aport + "/APKBUILD" work = get_context().config.work
apkbuild_path = aport / "APKBUILD"
apkbuild = pmb.parse.apkbuild(apkbuild_path) apkbuild = pmb.parse.apkbuild(apkbuild_path)
if os.path.exists(pmb.config.work / "aportgen"): if os.path.exists(work / "aportgen"):
pmb.helpers.run.user(["rm", "-r", pmb.config.work / "aportgen"]) pmb.helpers.run.user(["rm", "-r", work / "aportgen"])
pmb.helpers.run.user(["mkdir", pmb.config.work / "aportgen"]) pmb.helpers.run.user(["mkdir", work / "aportgen"])
pmb.helpers.run.user(["cp", "-r", apkbuild_path, pmb.helpers.run.user(["cp", "-r", apkbuild_path,
pmb.config.work / "aportgen"]) work / "aportgen"])
pkgver = pmb.build._package.get_pkgver(apkbuild["pkgver"], pkgver = pmb.build._package.get_pkgver(apkbuild["pkgver"],
original_source=False) original_source=False)
@ -108,7 +110,7 @@ def modify_apkbuild(args: PmbArgs, pkgname: str, aport: Path):
"subpackages": "", "subpackages": "",
"builddir": "/home/pmos/build/src"} "builddir": "/home/pmos/build/src"}
pmb.aportgen.core.rewrite(args, pkgname, apkbuild_path, fields=fields) pmb.aportgen.core.rewrite(pkgname, apkbuild_path, fields=fields)
def run_abuild(args: PmbArgs, pkgname: str, arch: str, apkbuild_path: Path, kbuild_out): def run_abuild(args: PmbArgs, pkgname: str, arch: str, apkbuild_path: Path, kbuild_out):
@ -140,16 +142,16 @@ def run_abuild(args: PmbArgs, pkgname: str, arch: str, apkbuild_path: Path, kbui
"as an argument to make.") "as an argument to make.")
# Create working directory for abuild # Create working directory for abuild
pmb.build.copy_to_buildpath(args, pkgname) pmb.build.copy_to_buildpath(pkgname)
# Create symlink from abuild working directory to envkernel build directory # Create symlink from abuild working directory to envkernel build directory
if kbuild_out != "": if kbuild_out != "":
if os.path.islink(chroot / "mnt/linux" / kbuild_out) and \ if os.path.islink(chroot / "mnt/linux" / kbuild_out) and \
os.path.lexists(chroot / "mnt/linux" / kbuild_out): os.path.lexists(chroot / "mnt/linux" / kbuild_out):
pmb.chroot.root(args, ["rm", "/mnt/linux" / kbuild_out]) pmb.chroot.root(["rm", "/mnt/linux" / kbuild_out])
pmb.chroot.root(args, ["ln", "-s", "/mnt/linux", pmb.chroot.root(["ln", "-s", "/mnt/linux",
build_path / "src"]) build_path / "src"])
pmb.chroot.root(args, ["ln", "-s", kbuild_out_source, pmb.chroot.root(["ln", "-s", kbuild_out_source,
build_path / "src" / kbuild_out]) build_path / "src" / kbuild_out])
cmd: List[PathString] = ["cp", apkbuild_path, chroot / build_path / "APKBUILD"] cmd: List[PathString] = ["cp", apkbuild_path, chroot / build_path / "APKBUILD"]
@ -161,7 +163,7 @@ def run_abuild(args: PmbArgs, pkgname: str, arch: str, apkbuild_path: Path, kbui
"CBUILD": pmb.config.arch_native, "CBUILD": pmb.config.arch_native,
"SUDO_APK": "abuild-apk --no-progress"} "SUDO_APK": "abuild-apk --no-progress"}
cmd = ["abuild", "rootpkg"] cmd = ["abuild", "rootpkg"]
pmb.chroot.user(args, cmd, working_dir=build_path, env=env) pmb.chroot.user(cmd, working_dir=build_path, env=env)
# Clean up bindmount # Clean up bindmount
pmb.helpers.mount.umount_all(chroot / "mnt/linux") pmb.helpers.mount.umount_all(chroot / "mnt/linux")
@ -170,8 +172,8 @@ def run_abuild(args: PmbArgs, pkgname: str, arch: str, apkbuild_path: Path, kbui
if kbuild_out != "": if kbuild_out != "":
if os.path.islink(chroot / "mnt/linux" / kbuild_out) and \ if os.path.islink(chroot / "mnt/linux" / kbuild_out) and \
os.path.lexists(chroot / "mnt/linux" / kbuild_out): os.path.lexists(chroot / "mnt/linux" / kbuild_out):
pmb.chroot.root(args, ["rm", "/mnt/linux" / kbuild_out]) pmb.chroot.root(["rm", "/mnt/linux" / kbuild_out])
pmb.chroot.root(args, ["rm", build_path / "src"]) pmb.chroot.root(["rm", build_path / "src"])
def package_kernel(args: PmbArgs): def package_kernel(args: PmbArgs):
@ -181,10 +183,10 @@ def package_kernel(args: PmbArgs):
raise RuntimeError("--envkernel needs exactly one linux-* package as " raise RuntimeError("--envkernel needs exactly one linux-* package as "
"argument.") "argument.")
aport = pmb.helpers.pmaports.find(args, pkgname) aport = pmb.helpers.pmaports.find(pkgname)
modify_apkbuild(args, pkgname, aport) modify_apkbuild(pkgname, aport)
apkbuild_path = pmb.config.work / "aportgen/APKBUILD" apkbuild_path = get_context().config.work / "aportgen/APKBUILD"
arch = args.deviceinfo["arch"] arch = args.deviceinfo["arch"]
apkbuild = pmb.parse.apkbuild(apkbuild_path, check_pkgname=False) apkbuild = pmb.parse.apkbuild(apkbuild_path, check_pkgname=False)
@ -201,7 +203,7 @@ def package_kernel(args: PmbArgs):
pmb.build.init(args, chroot) pmb.build.init(args, chroot)
if pmb.parse.arch.cpu_emulation_required(arch): if pmb.parse.arch.cpu_emulation_required(arch):
depends.append("binutils-" + arch) depends.append("binutils-" + arch)
pmb.chroot.apk.install(args, depends, chroot) pmb.chroot.apk.install(depends, chroot)
output = (arch + "/" + apkbuild["pkgname"] + "-" + apkbuild["pkgver"] + output = (arch + "/" + apkbuild["pkgname"] + "-" + apkbuild["pkgver"] +
"-r" + apkbuild["pkgrel"] + ".apk") "-r" + apkbuild["pkgrel"] + ".apk")

View file

@ -1,5 +1,6 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pmb.core.context import Context
from pmb.helpers import logging from pmb.helpers import logging
import os import os
import pathlib import pathlib
@ -8,13 +9,13 @@ import pmb.build
import pmb.config import pmb.config
import pmb.chroot import pmb.chroot
import pmb.chroot.apk import pmb.chroot.apk
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.run import pmb.helpers.run
import pmb.parse.arch import pmb.parse.arch
from pmb.core import Chroot from pmb.core import Chroot, get_context
def init_abuild_minimal(args: PmbArgs, chroot: Chroot=Chroot.native()): def init_abuild_minimal(chroot: Chroot=Chroot.native()):
"""Initialize a minimal chroot with abuild where one can do 'abuild checksum'.""" """Initialize a minimal chroot with abuild where one can do 'abuild checksum'."""
marker = chroot / "tmp/pmb_chroot_abuild_init_done" marker = chroot / "tmp/pmb_chroot_abuild_init_done"
if os.path.exists(marker): if os.path.exists(marker):
@ -22,45 +23,45 @@ def init_abuild_minimal(args: PmbArgs, chroot: Chroot=Chroot.native()):
# pigz is multithreaded and makes compression must faster, we install it in the native # pigz is multithreaded and makes compression must faster, we install it in the native
# chroot and then symlink it into the buildroot so we aren't running it through QEMU. # chroot and then symlink it into the buildroot so we aren't running it through QEMU.
pmb.chroot.apk.install(args, ["pigz"], Chroot.native(), build=False) pmb.chroot.apk.install(["pigz"], Chroot.native(), build=False)
pmb.chroot.apk.install(args, ["abuild"], chroot, build=False) pmb.chroot.apk.install(["abuild"], chroot, build=False)
# Fix permissions # Fix permissions
pmb.chroot.root(args, ["chown", "root:abuild", pmb.chroot.root(["chown", "root:abuild",
"/var/cache/distfiles"], chroot) "/var/cache/distfiles"], chroot)
pmb.chroot.root(args, ["chmod", "g+w", pmb.chroot.root(["chmod", "g+w",
"/var/cache/distfiles"], chroot) "/var/cache/distfiles"], chroot)
# Add user to group abuild # Add user to group abuild
pmb.chroot.root(args, ["adduser", "pmos", "abuild"], chroot) pmb.chroot.root(["adduser", "pmos", "abuild"], chroot)
pathlib.Path(marker).touch() pathlib.Path(marker).touch()
def init(args: PmbArgs, chroot: Chroot=Chroot.native()): def init(chroot: Chroot=Chroot.native()):
"""Initialize a chroot for building packages with abuild.""" """Initialize a chroot for building packages with abuild."""
marker = chroot / "tmp/pmb_chroot_build_init_done" marker = chroot / "tmp/pmb_chroot_build_init_done"
if marker.exists(): if marker.exists():
return return
# Initialize chroot, install packages # Initialize chroot, install packages
pmb.chroot.init(args, Chroot.native()) pmb.chroot.init(Chroot.native())
pmb.chroot.init(args, chroot) pmb.chroot.init(chroot)
init_abuild_minimal(args, chroot) init_abuild_minimal(chroot)
pmb.chroot.apk.install(args, pmb.config.build_packages, chroot, pmb.chroot.apk.install(pmb.config.build_packages, chroot,
build=False) build=False)
# Generate package signing keys # Generate package signing keys
if not os.path.exists(pmb.config.work / "config_abuild/abuild.conf"): if not os.path.exists(get_context().config.work / "config_abuild/abuild.conf"):
logging.info(f"({chroot}) generate abuild keys") logging.info(f"({chroot}) generate abuild keys")
pmb.chroot.user(args, ["abuild-keygen", "-n", "-q", "-a"], pmb.chroot.user(["abuild-keygen", "-n", "-q", "-a"],
chroot, env={"PACKAGER": "pmos <pmos@local>"}) chroot, env={"PACKAGER": "pmos <pmos@local>"})
# Copy package signing key to /etc/apk/keys # Copy package signing key to /etc/apk/keys
for key in (chroot / "mnt/pmbootstrap/abuild-config").glob("*.pub"): for key in (chroot / "mnt/pmbootstrap/abuild-config").glob("*.pub"):
key = key.relative_to(chroot.path) key = key.relative_to(chroot.path)
pmb.chroot.root(args, ["cp", key, "/etc/apk/keys/"], chroot) pmb.chroot.root(["cp", key, "/etc/apk/keys/"], chroot)
apk_arch = chroot.arch apk_arch = chroot.arch
@ -93,25 +94,25 @@ def init(args: PmbArgs, chroot: Chroot=Chroot.native()):
for i in range(len(lines)): for i in range(len(lines)):
lines[i] = lines[i][16:] lines[i] = lines[i][16:]
handle.write("\n".join(lines)) handle.write("\n".join(lines))
pmb.chroot.root(args, ["cp", "/tmp/apk_wrapper.sh", pmb.chroot.root(["cp", "/tmp/apk_wrapper.sh",
"/usr/local/bin/abuild-apk"], chroot) "/usr/local/bin/abuild-apk"], chroot)
pmb.chroot.root(args, ["chmod", "+x", "/usr/local/bin/abuild-apk"], chroot) pmb.chroot.root(["chmod", "+x", "/usr/local/bin/abuild-apk"], chroot)
# abuild.conf: Don't clean the build folder after building, so we can # abuild.conf: Don't clean the build folder after building, so we can
# inspect it afterwards for debugging # inspect it afterwards for debugging
pmb.chroot.root(args, ["sed", "-i", "-e", "s/^CLEANUP=.*/CLEANUP=''/", pmb.chroot.root(["sed", "-i", "-e", "s/^CLEANUP=.*/CLEANUP=''/",
"/etc/abuild.conf"], chroot) "/etc/abuild.conf"], chroot)
# abuild.conf: Don't clean up installed packages in strict mode, so # abuild.conf: Don't clean up installed packages in strict mode, so
# abuild exits directly when pressing ^C in pmbootstrap. # abuild exits directly when pressing ^C in pmbootstrap.
pmb.chroot.root(args, ["sed", "-i", "-e", pmb.chroot.root(["sed", "-i", "-e",
"s/^ERROR_CLEANUP=.*/ERROR_CLEANUP=''/", "s/^ERROR_CLEANUP=.*/ERROR_CLEANUP=''/",
"/etc/abuild.conf"], chroot) "/etc/abuild.conf"], chroot)
pathlib.Path(marker).touch() pathlib.Path(marker).touch()
def init_compiler(args: PmbArgs, depends, cross, arch): def init_compiler(context: Context, depends, cross, arch):
cross_pkgs = ["ccache-cross-symlinks", "abuild"] cross_pkgs = ["ccache-cross-symlinks", "abuild"]
if "gcc4" in depends: if "gcc4" in depends:
cross_pkgs += ["gcc4-" + arch] cross_pkgs += ["gcc4-" + arch]
@ -124,11 +125,11 @@ def init_compiler(args: PmbArgs, depends, cross, arch):
if cross == "crossdirect": if cross == "crossdirect":
cross_pkgs += ["crossdirect"] cross_pkgs += ["crossdirect"]
if "rust" in depends or "cargo" in depends: if "rust" in depends or "cargo" in depends:
if args.ccache: if context.ccache:
cross_pkgs += ["sccache"] cross_pkgs += ["sccache"]
# crossdirect for rust installs all build dependencies in the # crossdirect for rust installs all build dependencies in the
# native chroot too, as some of them can be required for building # native chroot too, as some of them can be required for building
# native macros / build scripts # native macros / build scripts
cross_pkgs += depends cross_pkgs += depends
pmb.chroot.apk.install(args, cross_pkgs, Chroot.native()) pmb.chroot.apk.install(cross_pkgs, Chroot.native())

View file

@ -11,7 +11,7 @@ import pmb.build.checksum
import pmb.chroot import pmb.chroot
import pmb.chroot.apk import pmb.chroot.apk
import pmb.chroot.other import pmb.chroot.other
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.pmaports import pmb.helpers.pmaports
import pmb.helpers.run import pmb.helpers.run
import pmb.parse import pmb.parse
@ -65,7 +65,7 @@ def get_outputdir(args: PmbArgs, pkgname: str, apkbuild: Dict[str, Any]) -> Path
# New style ($builddir) # New style ($builddir)
cmd = "srcdir=/home/pmos/build/src source APKBUILD; echo $builddir" cmd = "srcdir=/home/pmos/build/src source APKBUILD; echo $builddir"
ret = Path(pmb.chroot.user(args, ["sh", "-c", cmd], ret = Path(pmb.chroot.user(["sh", "-c", cmd],
chroot, Path("/home/pmos/build"), chroot, Path("/home/pmos/build"),
output_return=True).rstrip()) output_return=True).rstrip())
if (chroot / ret / ".config").exists(): if (chroot / ret / ".config").exists():
@ -87,9 +87,9 @@ def get_outputdir(args: PmbArgs, pkgname: str, apkbuild: Dict[str, Any]) -> Path
def extract_and_patch_sources(args: PmbArgs, pkgname: str, arch): def extract_and_patch_sources(args: PmbArgs, pkgname: str, arch):
pmb.build.copy_to_buildpath(args, pkgname) pmb.build.copy_to_buildpath(args, pkgname)
logging.info("(native) extract kernel source") logging.info("(native) extract kernel source")
pmb.chroot.user(args, ["abuild", "unpack"], working_dir=Path("/home/pmos/build")) pmb.chroot.user(["abuild", "unpack"], working_dir=Path("/home/pmos/build"))
logging.info("(native) apply patches") logging.info("(native) apply patches")
pmb.chroot.user(args, ["abuild", "prepare"], working_dir=Path("/home/pmos/build"), pmb.chroot.user(["abuild", "prepare"], working_dir=Path("/home/pmos/build"),
output="interactive", env={"CARCH": arch}) output="interactive", env={"CARCH": arch})
@ -99,7 +99,7 @@ def menuconfig(args: PmbArgs, pkgname: str, use_oldconfig):
pkgname = "linux-" + pkgname pkgname = "linux-" + pkgname
# Read apkbuild # Read apkbuild
aport = pmb.helpers.pmaports.find(args, pkgname) aport = pmb.helpers.pmaports.find(pkgname)
apkbuild = pmb.parse.apkbuild(aport / "APKBUILD") apkbuild = pmb.parse.apkbuild(aport / "APKBUILD")
arch = args.arch or get_arch(apkbuild) arch = args.arch or get_arch(apkbuild)
suffix = pmb.build.autodetect.chroot(apkbuild, arch) suffix = pmb.build.autodetect.chroot(apkbuild, arch)
@ -128,7 +128,7 @@ def menuconfig(args: PmbArgs, pkgname: str, use_oldconfig):
else: else:
depends += ["ncurses-dev"] depends += ["ncurses-dev"]
pmb.chroot.apk.install(args, depends) pmb.chroot.apk.install(depends)
# Copy host's .xauthority into native # Copy host's .xauthority into native
if copy_xauth: if copy_xauth:
@ -150,7 +150,7 @@ def menuconfig(args: PmbArgs, pkgname: str, use_oldconfig):
env["CC"] = f"{hostspec}-gcc" env["CC"] = f"{hostspec}-gcc"
if color: if color:
env["MENUCONFIG_COLOR"] = color env["MENUCONFIG_COLOR"] = color
pmb.chroot.user(args, ["make", kopt], Chroot.native(), pmb.chroot.user(["make", kopt], Chroot.native(),
outputdir, output="tui", env=env) outputdir, output="tui", env=env)
# Find the updated config # Find the updated config

View file

@ -4,12 +4,12 @@ import os
from pmb.helpers import logging from pmb.helpers import logging
from pathlib import Path from pathlib import Path
import pmb.chroot.run import pmb.chroot.run
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.cli import pmb.helpers.cli
import pmb.parse import pmb.parse
import pmb.build import pmb.build
from pmb.core import Chroot from pmb.core import Chroot, get_context
def newapkbuild(args: PmbArgs, folder, args_passed, force=False): def newapkbuild(args: PmbArgs, folder, args_passed, force=False):
@ -18,11 +18,11 @@ def newapkbuild(args: PmbArgs, folder, args_passed, force=False):
build = Path("/home/pmos/build") build = Path("/home/pmos/build")
build_outside = Chroot.native() / build build_outside = Chroot.native() / build
if build_outside.exists(): if build_outside.exists():
pmb.chroot.root(args, ["rm", "-r", build]) pmb.chroot.root(["rm", "-r", build])
pmb.chroot.user(args, ["mkdir", "-p", build]) pmb.chroot.user(["mkdir", "-p", build])
# Run newapkbuild # Run newapkbuild
pmb.chroot.user(args, ["newapkbuild"] + args_passed, working_dir=build) pmb.chroot.user(["newapkbuild"] + args_passed, working_dir=build)
glob_result = list(build_outside.glob("/*/APKBUILD")) glob_result = list(build_outside.glob("/*/APKBUILD"))
if not len(glob_result): if not len(glob_result):
return return
@ -30,13 +30,13 @@ def newapkbuild(args: PmbArgs, folder, args_passed, force=False):
# Paths for copying # Paths for copying
source_apkbuild = glob_result[0] source_apkbuild = glob_result[0]
pkgname = pmb.parse.apkbuild(source_apkbuild, False)["pkgname"] pkgname = pmb.parse.apkbuild(source_apkbuild, False)["pkgname"]
target = args.aports / folder / pkgname target = get_context().config.aports / folder / pkgname
# Move /home/pmos/build/$pkgname/* to /home/pmos/build/* # Move /home/pmos/build/$pkgname/* to /home/pmos/build/*
for path in build_outside.glob("/*/*"): for path in build_outside.glob("/*/*"):
path_inside = build / pkgname / os.path.basename(path) path_inside = build / pkgname / os.path.basename(path)
pmb.chroot.user(args, ["mv", path_inside, build]) pmb.chroot.user(["mv", path_inside, build])
pmb.chroot.user(args, ["rmdir", build / pkgname]) pmb.chroot.user(["rmdir", build / pkgname])
# Overwrite confirmation # Overwrite confirmation
if os.path.exists(target): if os.path.exists(target):

View file

@ -8,7 +8,7 @@ import datetime
from typing import List from typing import List
import pmb.chroot import pmb.chroot
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.file import pmb.helpers.file
import pmb.helpers.git import pmb.helpers.git
import pmb.helpers.pmaports import pmb.helpers.pmaports
@ -16,12 +16,12 @@ import pmb.helpers.run
import pmb.parse.arch import pmb.parse.arch
import pmb.parse.apkindex import pmb.parse.apkindex
import pmb.parse.version import pmb.parse.version
from pmb.core import Chroot from pmb.core import Chroot, get_context
def copy_to_buildpath(args: PmbArgs, package, chroot: Chroot=Chroot.native()): def copy_to_buildpath(package, chroot: Chroot=Chroot.native()):
# Sanity check # Sanity check
aport = pmb.helpers.pmaports.find(args, package) aport = pmb.helpers.pmaports.find(package)
if not os.path.exists(aport / "APKBUILD"): if not os.path.exists(aport / "APKBUILD"):
raise ValueError(f"Path does not contain an APKBUILD file: {aport}") raise ValueError(f"Path does not contain an APKBUILD file: {aport}")
@ -42,11 +42,11 @@ def copy_to_buildpath(args: PmbArgs, package, chroot: Chroot=Chroot.native()):
continue continue
pmb.helpers.run.root(["cp", "-rL", aport / file, build / file]) pmb.helpers.run.root(["cp", "-rL", aport / file, build / file])
pmb.chroot.root(args, ["chown", "-R", "pmos:pmos", pmb.chroot.root(["chown", "-R", "pmos:pmos",
"/home/pmos/build"], chroot) "/home/pmos/build"], chroot)
def is_necessary(args: PmbArgs, arch, apkbuild, indexes=None): def is_necessary(arch, apkbuild, indexes=None):
"""Check if the package has already been built. """Check if the package has already been built.
Compared to abuild's check, this check also works for different architectures. Compared to abuild's check, this check also works for different architectures.
@ -61,7 +61,7 @@ def is_necessary(args: PmbArgs, arch, apkbuild, indexes=None):
msg = "Build is necessary for package '" + package + "': " msg = "Build is necessary for package '" + package + "': "
# Get version from APKINDEX # Get version from APKINDEX
index_data = pmb.parse.apkindex.package(args, package, arch, False, index_data = pmb.parse.apkindex.package(package, arch, False,
indexes) indexes)
if not index_data: if not index_data:
logging.debug(msg + "No binary package available") logging.debug(msg + "No binary package available")
@ -101,8 +101,8 @@ def index_repo(args: PmbArgs, arch=None):
""" """
pmb.build.init(args) pmb.build.init(args)
channel = pmb.config.pmaports.read_config(args)["channel"] channel = pmb.config.pmaports.read_config()["channel"]
pkgdir = (pmb.config.work / "packages" / channel) pkgdir = (get_context().config.work / "packages" / channel)
paths: List[Path] = [] paths: List[Path] = []
if arch: if arch:
paths = [pkgdir / arch] paths = [pkgdir / arch]
@ -124,55 +124,57 @@ def index_repo(args: PmbArgs, arch=None):
["mv", "APKINDEX.tar.gz_", "APKINDEX.tar.gz"] ["mv", "APKINDEX.tar.gz_", "APKINDEX.tar.gz"]
] ]
for command in commands: for command in commands:
pmb.chroot.user(args, command, working_dir=path_repo_chroot) pmb.chroot.user(command, working_dir=path_repo_chroot)
else: else:
logging.debug(f"NOTE: Can't build index for: {path}") logging.debug(f"NOTE: Can't build index for: {path}")
pmb.parse.apkindex.clear_cache(path / "APKINDEX.tar.gz") pmb.parse.apkindex.clear_cache(path / "APKINDEX.tar.gz")
def configure_abuild(args: PmbArgs, chroot: Chroot, verify=False): def configure_abuild(chroot: Chroot, verify=False):
"""Set the correct JOBS count in ``abuild.conf``. """Set the correct JOBS count in ``abuild.conf``.
:param verify: internally used to test if changing the config has worked. :param verify: internally used to test if changing the config has worked.
""" """
jobs = get_context().config.jobs
path = chroot / "etc/abuild.conf" path = chroot / "etc/abuild.conf"
prefix = "export JOBS=" prefix = "export JOBS="
with path.open(encoding="utf-8") as handle: with path.open(encoding="utf-8") as handle:
for line in handle: for line in handle:
if not line.startswith(prefix): if not line.startswith(prefix):
continue continue
if line != (prefix + args.jobs + "\n"): if line != (prefix + jobs + "\n"):
if verify: if verify:
raise RuntimeError(f"Failed to configure abuild: {path}" raise RuntimeError(f"Failed to configure abuild: {path}"
"\nTry to delete the file" "\nTry to delete the file"
"(or zap the chroot).") "(or zap the chroot).")
pmb.chroot.root(args, ["sed", "-i", "-e", pmb.chroot.root(["sed", "-i", "-e",
f"s/^{prefix}.*/{prefix}{args.jobs}/", f"s/^{prefix}.*/{prefix}{jobs}/",
"/etc/abuild.conf"], "/etc/abuild.conf"],
chroot) chroot)
configure_abuild(args, chroot, True) configure_abuild(chroot, True)
return return
pmb.chroot.root(args, ["sed", "-i", f"$ a\\{prefix}{args.jobs}", "/etc/abuild.conf"], chroot) pmb.chroot.root(["sed", "-i", f"$ a\\{prefix}{jobs}", "/etc/abuild.conf"], chroot)
def configure_ccache(args: PmbArgs, chroot: Chroot=Chroot.native(), verify=False): def configure_ccache(chroot: Chroot=Chroot.native(), verify=False):
"""Set the maximum ccache size. """Set the maximum ccache size.
:param verify: internally used to test if changing the config has worked. :param verify: internally used to test if changing the config has worked.
""" """
# Check if the settings have been set already # Check if the settings have been set already
arch = chroot.arch arch = chroot.arch
path = pmb.config.work / f"cache_ccache_{arch}" / "ccache.conf" config = get_context().config
path = config.work / f"cache_ccache_{arch}" / "ccache.conf"
if os.path.exists(path): if os.path.exists(path):
with open(path, encoding="utf-8") as handle: with open(path, encoding="utf-8") as handle:
for line in handle: for line in handle:
if line == ("max_size = " + args.ccache_size + "\n"): if line == ("max_size = " + config.ccache_size + "\n"):
return return
if verify: if verify:
raise RuntimeError(f"Failed to configure ccache: {path}\nTry to" raise RuntimeError(f"Failed to configure ccache: {path}\nTry to"
" delete the file (or zap the chroot).") " delete the file (or zap the chroot).")
# Set the size and verify # Set the size and verify
pmb.chroot.user(args, ["ccache", "--max-size", args.ccache_size], pmb.chroot.user(["ccache", "--max-size", config.ccache_size],
chroot) chroot)
configure_ccache(args, chroot, True) configure_ccache(chroot, True)

View file

@ -8,9 +8,10 @@ from pmb.helpers import logging
import shlex import shlex
from typing import List from typing import List
import pmb.build
import pmb.chroot import pmb.chroot
import pmb.config import pmb.config
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.apk import pmb.helpers.apk
import pmb.helpers.other import pmb.helpers.other
import pmb.helpers.pmaports import pmb.helpers.pmaports
@ -23,7 +24,7 @@ import pmb.parse.version
from pmb.core import Chroot, get_context from pmb.core import Chroot, get_context
def update_repository_list(args: PmbArgs, suffix: Chroot, postmarketos_mirror=True, def update_repository_list(suffix: Chroot, postmarketos_mirror=True,
check=False): check=False):
""" """
Update /etc/apk/repositories, if it is outdated (when the user changed the Update /etc/apk/repositories, if it is outdated (when the user changed the
@ -52,7 +53,7 @@ def update_repository_list(args: PmbArgs, suffix: Chroot, postmarketos_mirror=Tr
pmb.helpers.run.root(["mkdir", "-p", path.parent]) pmb.helpers.run.root(["mkdir", "-p", path.parent])
# Up to date: Save cache, return # Up to date: Save cache, return
lines_new = pmb.helpers.repo.urls(args, postmarketos_mirror=postmarketos_mirror) lines_new = pmb.helpers.repo.urls(postmarketos_mirror=postmarketos_mirror)
if lines_old == lines_new: if lines_old == lines_new:
pmb.helpers.other.cache["apk_repository_list_updated"].append(suffix) pmb.helpers.other.cache["apk_repository_list_updated"].append(suffix)
return return
@ -68,10 +69,10 @@ def update_repository_list(args: PmbArgs, suffix: Chroot, postmarketos_mirror=Tr
for line in lines_new: for line in lines_new:
pmb.helpers.run.root(["sh", "-c", "echo " pmb.helpers.run.root(["sh", "-c", "echo "
f"{shlex.quote(line)} >> {path}"]) f"{shlex.quote(line)} >> {path}"])
update_repository_list(args, suffix, postmarketos_mirror, True) update_repository_list(suffix, postmarketos_mirror, True)
def check_min_version(args: PmbArgs, chroot: Chroot=Chroot.native()): def check_min_version(chroot: Chroot=Chroot.native()):
""" """
Check the minimum apk version, before running it the first time in the Check the minimum apk version, before running it the first time in the
current session (lifetime of one pmbootstrap call). current session (lifetime of one pmbootstrap call).
@ -90,7 +91,7 @@ def check_min_version(args: PmbArgs, chroot: Chroot=Chroot.native()):
# Compare # Compare
version_installed = installed(chroot)["apk-tools"]["version"] version_installed = installed(chroot)["apk-tools"]["version"]
pmb.helpers.apk.check_outdated( pmb.helpers.apk.check_outdated(
args, version_installed, version_installed,
"Delete your http cache and zap all chroots, then try again:" "Delete your http cache and zap all chroots, then try again:"
" 'pmbootstrap zap -hc'") " 'pmbootstrap zap -hc'")
@ -98,32 +99,6 @@ def check_min_version(args: PmbArgs, chroot: Chroot=Chroot.native()):
pmb.helpers.other.cache["apk_min_version_checked"].append(chroot.path) pmb.helpers.other.cache["apk_min_version_checked"].append(chroot.path)
def install_build(args: PmbArgs, package, arch):
"""
Build an outdated package unless pmbootstrap was invoked with
"pmbootstrap install" and the option to build packages during pmb install
is disabled.
:param package: name of the package to build
:param arch: architecture of the package to build
"""
# User may have disabled building packages during "pmbootstrap install"
if args.action == "install" and not args.build_pkgs_on_install:
if not pmb.parse.apkindex.package(args, package, arch, False):
raise RuntimeError(f"{package}: no binary package found for"
f" {arch}, and compiling packages during"
" 'pmbootstrap install' has been disabled."
" Consider changing this option in"
" 'pmbootstrap init'.")
# Use the existing binary package
return
# Build the package if it's in pmaports and there is no binary package
# with the same pkgver and pkgrel. This check is done in
# pmb.build.is_necessary, which gets called in pmb.build.package.
return pmb.build.package(args, package, arch)
def packages_split_to_add_del(packages): def packages_split_to_add_del(packages):
""" """
Sort packages into "to_add" and "to_del" lists depending on their pkgname Sort packages into "to_add" and "to_del" lists depending on their pkgname
@ -145,7 +120,7 @@ def packages_split_to_add_del(packages):
return (to_add, to_del) return (to_add, to_del)
def packages_get_locally_built_apks(args: PmbArgs, packages, arch: str) -> List[Path]: def packages_get_locally_built_apks(packages, arch: str) -> List[Path]:
""" """
Iterate over packages and if existing, get paths to locally built packages. Iterate over packages and if existing, get paths to locally built packages.
This is used to force apk to upgrade packages to newer local versions, even This is used to force apk to upgrade packages to newer local versions, even
@ -156,16 +131,16 @@ def packages_get_locally_built_apks(args: PmbArgs, packages, arch: str) -> List[
:returns: list of apk file paths that are valid inside the chroots, e.g. :returns: list of apk file paths that are valid inside the chroots, e.g.
["/mnt/pmbootstrap/packages/x86_64/hello-world-1-r6.apk", ...] ["/mnt/pmbootstrap/packages/x86_64/hello-world-1-r6.apk", ...]
""" """
channel: str = pmb.config.pmaports.read_config(args)["channel"] channel: str = pmb.config.pmaports.read_config()["channel"]
ret: List[Path] = [] ret: List[Path] = []
for package in packages: for package in packages:
data_repo = pmb.parse.apkindex.package(args, package, arch, False) data_repo = pmb.parse.apkindex.package(package, arch, False)
if not data_repo: if not data_repo:
continue continue
apk_file = f"{package}-{data_repo['version']}.apk" apk_file = f"{package}-{data_repo['version']}.apk"
apk_path = pmb.config.work / "packages" / channel / arch / apk_file apk_path = get_context().config.work / "packages" / channel / arch / apk_file
if not apk_path.exists(): if not apk_path.exists():
continue continue
@ -210,7 +185,7 @@ def install_run_apk(to_add, to_add_local, to_del, chroot: Chroot):
# it from there. # it from there.
apk_static = Chroot.native() / "sbin/apk.static" apk_static = Chroot.native() / "sbin/apk.static"
arch = chroot.arch arch = chroot.arch
apk_cache = pmb.config.work / f"cache_apk_{arch}" apk_cache = get_context().config.work / f"cache_apk_{arch}"
for (i, command) in enumerate(commands): for (i, command) in enumerate(commands):
# --no-interactive is a parameter to `add`, so it must be appended or apk # --no-interactive is a parameter to `add`, so it must be appended or apk
@ -233,7 +208,7 @@ def install_run_apk(to_add, to_add_local, to_del, chroot: Chroot):
pmb.helpers.run.root([apk_static, "--no-progress"] + command) pmb.helpers.run.root([apk_static, "--no-progress"] + command)
def install(args: PmbArgs, packages, chroot: Chroot, build=True): def install(packages, chroot: Chroot, build=True):
""" """
Install packages from pmbootstrap's local package index or the pmOS/Alpine Install packages from pmbootstrap's local package index or the pmOS/Alpine
binary package mirrors. Iterate over all dependencies recursively, and binary package mirrors. Iterate over all dependencies recursively, and
@ -247,6 +222,7 @@ def install(args: PmbArgs, packages, chroot: Chroot, build=True):
repositories, set this to False for performance optimization. repositories, set this to False for performance optimization.
""" """
arch = chroot.arch arch = chroot.arch
context = get_context()
if not packages: if not packages:
logging.verbose("pmb.chroot.apk.install called with empty packages list," logging.verbose("pmb.chroot.apk.install called with empty packages list,"
@ -254,21 +230,21 @@ def install(args: PmbArgs, packages, chroot: Chroot, build=True):
return return
# Initialize chroot # Initialize chroot
check_min_version(args, chroot) check_min_version(chroot)
installed_pkgs = pmb.chroot.user(["apk", "info", "-e"] + packages, chroot, output_return=True, check=False) installed_pkgs = pmb.chroot.user(["apk", "info", "-e"] + packages, chroot, output_return=True, check=False)
if installed_pkgs is not None and installed_pkgs.strip().split("\n") == packages: if installed_pkgs is not None and installed_pkgs.strip().split("\n") == packages:
logging.debug(f"({chroot}) all packages already installed") logging.debug(f"({chroot}) all packages already installed")
return return
packages_with_depends = pmb.parse.depends.recurse(args, packages, chroot) packages_with_depends = pmb.parse.depends.recurse(packages, chroot)
to_add, to_del = packages_split_to_add_del(packages_with_depends) to_add, to_del = packages_split_to_add_del(packages_with_depends)
if build: if build and context.config.build_pkgs_on_install:
for package in to_add: for package in to_add:
install_build(args, package, arch) pmb.build.package(context, package, arch)
to_add_local = packages_get_locally_built_apks(args, to_add, arch) to_add_local = packages_get_locally_built_apks(to_add, arch)
to_add_no_deps, _ = packages_split_to_add_del(packages) to_add_no_deps, _ = packages_split_to_add_del(packages)
logging.info(f"({chroot}) install {' '.join(to_add_no_deps)}") logging.info(f"({chroot}) install {' '.join(to_add_no_deps)}")

View file

@ -7,7 +7,7 @@ import tarfile
import tempfile import tempfile
import stat import stat
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.apk import pmb.helpers.apk
import pmb.helpers.run import pmb.helpers.run
import pmb.config import pmb.config
@ -15,6 +15,7 @@ import pmb.config.load
import pmb.parse.apkindex import pmb.parse.apkindex
import pmb.helpers.http import pmb.helpers.http
import pmb.parse.version import pmb.parse.version
from pmb.core import get_context
def read_signature_info(tar): def read_signature_info(tar):
@ -77,7 +78,7 @@ def extract_temp(tar, sigfilename):
return ret return ret
def verify_signature(args: PmbArgs, files, sigkey_path): def verify_signature(files, sigkey_path):
""" """
Verify the signature with openssl. Verify the signature with openssl.
@ -99,7 +100,7 @@ def verify_signature(args: PmbArgs, files, sigkey_path):
" delete the download and try again.") " delete the download and try again.")
def extract(args: PmbArgs, version, apk_path): def extract(version, apk_path):
""" """
Extract everything to temporary locations, verify signatures and reported Extract everything to temporary locations, verify signatures and reported
versions. When everything is right, move the extracted apk.static to the versions. When everything is right, move the extracted apk.static to the
@ -111,7 +112,7 @@ def extract(args: PmbArgs, version, apk_path):
files = extract_temp(tar, sigfilename) files = extract_temp(tar, sigfilename)
# Verify signature # Verify signature
verify_signature(args, files, sigkey_path) verify_signature(files, sigkey_path)
os.unlink(files["sig"]["temp_path"]) os.unlink(files["sig"]["temp_path"])
temp_path = files["apk"]["temp_path"] temp_path = files["apk"]["temp_path"]
@ -131,45 +132,45 @@ def extract(args: PmbArgs, version, apk_path):
" and try again.") " and try again.")
# Move it to the right path # Move it to the right path
target_path = pmb.config.work / "apk.static" target_path = get_context().config.work / "apk.static"
shutil.move(temp_path, target_path) shutil.move(temp_path, target_path)
def download(args: PmbArgs, file): def download(file):
""" """
Download a single file from an Alpine mirror. Download a single file from an Alpine mirror.
""" """
channel_cfg = pmb.config.pmaports.read_config_channel(args) channel_cfg = pmb.config.pmaports.read_config_channel()
mirrordir = channel_cfg["mirrordir_alpine"] mirrordir = channel_cfg["mirrordir_alpine"]
base_url = f"{args.mirror_alpine}{mirrordir}/main/{pmb.config.arch_native}" base_url = f"{get_context().config.mirror_alpine}{mirrordir}/main/{pmb.config.arch_native}"
return pmb.helpers.http.download(args, f"{base_url}/{file}", file) return pmb.helpers.http.download(f"{base_url}/{file}", file)
def init(args: PmbArgs): def init():
""" """
Download, verify, extract $WORK/apk.static. Download, verify, extract $WORK/apk.static.
""" """
# Get and parse the APKINDEX # Get and parse the APKINDEX
apkindex = pmb.helpers.repo.alpine_apkindex_path(args, "main") apkindex = pmb.helpers.repo.alpine_apkindex_path("main")
index_data = pmb.parse.apkindex.package(args, "apk-tools-static", index_data = pmb.parse.apkindex.package("apk-tools-static",
indexes=[apkindex]) indexes=[apkindex])
version = index_data["version"] version = index_data["version"]
# Verify the apk-tools-static version # Verify the apk-tools-static version
pmb.helpers.apk.check_outdated( pmb.helpers.apk.check_outdated(
args, version, "Run 'pmbootstrap update', then try again.") version, "Run 'pmbootstrap update', then try again.")
# Download, extract, verify apk-tools-static # Download, extract, verify apk-tools-static
apk_name = f"apk-tools-static-{version}.apk" apk_name = f"apk-tools-static-{version}.apk"
apk_static = download(args, apk_name) apk_static = download(apk_name)
extract(args, version, apk_static) extract(version, apk_static)
def run(args: PmbArgs, parameters): def run(parameters):
# --no-interactive is a parameter to `add`, so it must be appended or apk # --no-interactive is a parameter to `add`, so it must be appended or apk
# gets confused # gets confused
parameters += ["--no-interactive"] parameters += ["--no-interactive"]
if args.offline: if get_context().offline:
parameters = ["--no-network"] + parameters parameters = ["--no-network"] + parameters
pmb.helpers.apk.apk_with_progress([pmb.config.work / "apk.static"] + parameters) pmb.helpers.apk.apk_with_progress([get_context().config.work / "apk.static"] + parameters)

View file

@ -4,31 +4,33 @@ import os
from pmb.core.chroot import Chroot from pmb.core.chroot import Chroot
from pmb.helpers import logging from pmb.helpers import logging
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.run import pmb.helpers.run
import pmb.helpers.other import pmb.helpers.other
import pmb.parse import pmb.parse
import pmb.parse.arch import pmb.parse.arch
import pmb.chroot.apk
def is_registered(arch_qemu): def is_registered(arch_qemu):
return os.path.exists("/proc/sys/fs/binfmt_misc/qemu-" + arch_qemu) return os.path.exists("/proc/sys/fs/binfmt_misc/qemu-" + arch_qemu)
def register(args: PmbArgs, arch): def register(arch):
""" """
Get arch, magic, mask. Get arch, magic, mask.
""" """
arch_qemu = pmb.parse.arch.alpine_to_qemu(arch) arch_qemu = pmb.parse.arch.alpine_to_qemu(arch)
chroot = Chroot.native()
# always make sure the qemu-<arch> binary is installed, since registering # always make sure the qemu-<arch> binary is installed, since registering
# may happen outside of this method (e.g. by OS) # may happen outside of this method (e.g. by OS)
if f"qemu-{arch_qemu}" not in pmb.chroot.apk.installed(args): if f"qemu-{arch_qemu}" not in pmb.chroot.apk.installed(chroot):
pmb.chroot.apk.install(args, ["qemu-" + arch_qemu], Chroot.native()) pmb.chroot.apk.install(["qemu-" + arch_qemu], chroot)
if is_registered(arch_qemu): if is_registered(arch_qemu):
return return
pmb.helpers.other.check_binfmt_misc(args) pmb.helpers.other.check_binfmt_misc()
# Don't continue if the actions from check_binfmt_misc caused the OS to # Don't continue if the actions from check_binfmt_misc caused the OS to
# automatically register the target arch # automatically register the target arch

View file

@ -7,15 +7,15 @@ from pmb.helpers import logging
import os import os
import pmb.chroot import pmb.chroot
import pmb.chroot.binfmt
import pmb.chroot.apk_static import pmb.chroot.apk_static
import pmb.config import pmb.config
import pmb.config.workdir import pmb.config.workdir
from pmb.core.types import PmbArgs
import pmb.helpers.repo import pmb.helpers.repo
import pmb.helpers.run import pmb.helpers.run
import pmb.helpers.other import pmb.helpers.other
import pmb.parse.arch import pmb.parse.arch
from pmb.core import Chroot, ChrootType from pmb.core import Chroot, ChrootType, get_context
cache_chroot_is_outdated: List[str] = [] cache_chroot_is_outdated: List[str] = []
@ -29,7 +29,7 @@ class UsrMerge(enum.Enum):
OFF = 2 OFF = 2
def copy_resolv_conf(args: PmbArgs, chroot: Chroot): def copy_resolv_conf(chroot: Chroot):
""" """
Use pythons super fast file compare function (due to caching) Use pythons super fast file compare function (due to caching)
and copy the /etc/resolv.conf to the chroot, in case it is and copy the /etc/resolv.conf to the chroot, in case it is
@ -45,7 +45,7 @@ def copy_resolv_conf(args: PmbArgs, chroot: Chroot):
pmb.helpers.run.root(["touch", resolv_path]) pmb.helpers.run.root(["touch", resolv_path])
def mark_in_chroot(args: PmbArgs, chroot: Chroot=Chroot.native()): def mark_in_chroot(chroot: Chroot=Chroot.native()):
""" """
Touch a flag so we can know when we're running in chroot (and Touch a flag so we can know when we're running in chroot (and
don't accidentally flash partitions on our host). This marker don't accidentally flash partitions on our host). This marker
@ -56,7 +56,7 @@ def mark_in_chroot(args: PmbArgs, chroot: Chroot=Chroot.native()):
pmb.helpers.run.root(["touch", in_chroot_file]) pmb.helpers.run.root(["touch", in_chroot_file])
def setup_qemu_emulation(args: PmbArgs, chroot: Chroot): def setup_qemu_emulation(chroot: Chroot):
arch = chroot.arch arch = chroot.arch
if not pmb.parse.arch.cpu_emulation_required(arch): if not pmb.parse.arch.cpu_emulation_required(arch):
return return
@ -64,13 +64,13 @@ def setup_qemu_emulation(args: PmbArgs, chroot: Chroot):
arch_qemu = pmb.parse.arch.alpine_to_qemu(arch) arch_qemu = pmb.parse.arch.alpine_to_qemu(arch)
# mount --bind the qemu-user binary # mount --bind the qemu-user binary
pmb.chroot.binfmt.register(args, arch) pmb.chroot.binfmt.register(arch)
pmb.helpers.mount.bind_file(Chroot.native() / f"/usr/bin/qemu-{arch_qemu}", pmb.helpers.mount.bind_file(Chroot.native() / f"/usr/bin/qemu-{arch_qemu}",
chroot / f"usr/bin/qemu-{arch_qemu}-static", chroot / f"usr/bin/qemu-{arch_qemu}-static",
create_folders=True) create_folders=True)
def init_keys(args: PmbArgs): def init_keys():
""" """
All Alpine and postmarketOS repository keys are shipped with pmbootstrap. All Alpine and postmarketOS repository keys are shipped with pmbootstrap.
Copy them into $WORK/config_apk_keys, which gets mounted inside the various Copy them into $WORK/config_apk_keys, which gets mounted inside the various
@ -81,27 +81,27 @@ def init_keys(args: PmbArgs):
not installed yet. not installed yet.
""" """
for key in pmb.config.apk_keys_path.glob("*.pub"): for key in pmb.config.apk_keys_path.glob("*.pub"):
target = pmb.config.work / "config_apk_keys" / key.name target = get_context().config.work / "config_apk_keys" / key.name
if not target.exists(): if not target.exists():
# Copy as root, so the resulting files in chroots are owned by root # Copy as root, so the resulting files in chroots are owned by root
pmb.helpers.run.root(["cp", key, target]) pmb.helpers.run.root(["cp", key, target])
def init_usr_merge(args: PmbArgs, chroot: Chroot): def init_usr_merge(chroot: Chroot):
logging.info(f"({chroot}) merge /usr") logging.info(f"({chroot}) merge /usr")
script = f"{pmb.config.pmb_src}/pmb/data/merge-usr.sh" script = f"{pmb.config.pmb_src}/pmb/data/merge-usr.sh"
pmb.helpers.run.root(["sh", "-e", script, "CALLED_FROM_PMB", pmb.helpers.run.root(["sh", "-e", script, "CALLED_FROM_PMB",
chroot.path]) chroot.path])
def warn_if_chroot_is_outdated(args: PmbArgs, chroot: Chroot): def warn_if_chroot_is_outdated(chroot: Chroot):
global cache_chroot_is_outdated global cache_chroot_is_outdated
# Only check / display the warning once per session # Only check / display the warning once per session
if str(chroot) in cache_chroot_is_outdated: if str(chroot) in cache_chroot_is_outdated:
return return
if pmb.config.workdir.chroots_outdated(args, chroot): if pmb.config.workdir.chroots_outdated(chroot):
days_warn = int(pmb.config.chroot_outdated / 3600 / 24) days_warn = int(pmb.config.chroot_outdated / 3600 / 24)
logging.warning(f"WARNING: Your {chroot} chroot is older than" logging.warning(f"WARNING: Your {chroot} chroot is older than"
f" {days_warn} days. Consider running" f" {days_warn} days. Consider running"
@ -110,7 +110,7 @@ def warn_if_chroot_is_outdated(args: PmbArgs, chroot: Chroot):
cache_chroot_is_outdated += [str(chroot)] cache_chroot_is_outdated += [str(chroot)]
def init(args: PmbArgs, chroot: Chroot=Chroot.native(), usr_merge=UsrMerge.AUTO, def init(chroot: Chroot=Chroot.native(), usr_merge=UsrMerge.AUTO,
postmarketos_mirror=True): postmarketos_mirror=True):
""" """
Initialize a chroot by copying the resolv.conf and updating Initialize a chroot by copying the resolv.conf and updating
@ -125,61 +125,62 @@ def init(args: PmbArgs, chroot: Chroot=Chroot.native(), usr_merge=UsrMerge.AUTO,
# When already initialized: just prepare the chroot # When already initialized: just prepare the chroot
arch = chroot.arch arch = chroot.arch
config = get_context().config
already_setup = str(chroot) in pmb.helpers.other.cache["pmb.chroot.init"] already_setup = str(chroot) in pmb.helpers.other.cache["pmb.chroot.init"]
if already_setup: if already_setup:
logging.warning(f"({chroot}) FIXME! init() called multiple times!") logging.warning(f"({chroot}) FIXME! init() called multiple times!")
return return
pmb.chroot.mount(args, chroot) pmb.chroot.mount(chroot)
setup_qemu_emulation(args, chroot) setup_qemu_emulation(chroot)
mark_in_chroot(args, chroot) mark_in_chroot(chroot)
if (chroot / "bin/sh").is_symlink(): if (chroot / "bin/sh").is_symlink():
pmb.config.workdir.chroot_check_channel(args, chroot) pmb.config.workdir.chroot_check_channel(chroot)
copy_resolv_conf(args, chroot) copy_resolv_conf(chroot)
pmb.chroot.apk.update_repository_list(args, chroot, postmarketos_mirror) pmb.chroot.apk.update_repository_list(chroot, postmarketos_mirror)
warn_if_chroot_is_outdated(args, chroot) warn_if_chroot_is_outdated(chroot)
pmb.helpers.other.cache["pmb.chroot.init"][str(chroot)] = True pmb.helpers.other.cache["pmb.chroot.init"][str(chroot)] = True
return return
# Require apk-tools-static # Require apk-tools-static
pmb.chroot.apk_static.init(args) pmb.chroot.apk_static.init()
logging.info(f"({chroot}) install alpine-base") logging.info(f"({chroot}) install alpine-base")
# Initialize cache # Initialize cache
apk_cache = pmb.config.work / f"cache_apk_{arch}" apk_cache = config.work / f"cache_apk_{arch}"
pmb.helpers.run.root(["ln", "-s", "-f", "/var/cache/apk", pmb.helpers.run.root(["ln", "-s", "-f", "/var/cache/apk",
chroot / "etc/apk/cache"]) chroot / "etc/apk/cache"])
# Initialize /etc/apk/keys/, resolv.conf, repositories # Initialize /etc/apk/keys/, resolv.conf, repositories
init_keys(args) init_keys()
copy_resolv_conf(args, chroot) copy_resolv_conf(chroot)
pmb.chroot.apk.update_repository_list(args, chroot, postmarketos_mirror) pmb.chroot.apk.update_repository_list(chroot, postmarketos_mirror)
pmb.config.workdir.chroot_save_init(args, chroot) pmb.config.workdir.chroot_save_init(chroot)
# Install alpine-base # Install alpine-base
pmb.helpers.repo.update(args, arch) pmb.helpers.repo.update(arch)
pkgs = ["alpine-base"] pkgs = ["alpine-base"]
# install apk static in the native chroot so we can run it # install apk static in the native chroot so we can run it
# we have a forked apk for systemd and this is the easiest # we have a forked apk for systemd and this is the easiest
# way to install/run it. # way to install/run it.
if chroot.type == ChrootType.NATIVE: if chroot.type == ChrootType.NATIVE:
pkgs += ["apk-tools-static"] pkgs += ["apk-tools-static"]
pmb.chroot.apk_static.run(args, ["--root", chroot.path, pmb.chroot.apk_static.run(["--root", chroot.path,
"--cache-dir", apk_cache, "--cache-dir", apk_cache,
"--initdb", "--arch", arch, "--initdb", "--arch", arch,
"add"] + pkgs) "add"] + pkgs)
# Merge /usr # Merge /usr
if usr_merge is UsrMerge.AUTO and pmb.config.is_systemd_selected(args): if usr_merge is UsrMerge.AUTO and pmb.config.is_systemd_selected(config):
usr_merge = UsrMerge.ON usr_merge = UsrMerge.ON
if usr_merge is UsrMerge.ON: if usr_merge is UsrMerge.ON:
init_usr_merge(args, chroot) init_usr_merge(chroot)
# Building chroots: create "pmos" user, add symlinks to /home/pmos # Building chroots: create "pmos" user, add symlinks to /home/pmos
if not chroot.type == ChrootType.ROOTFS: if not chroot.type == ChrootType.ROOTFS:
pmb.chroot.root(args, ["adduser", "-D", "pmos", "-u", pmb.chroot.root(["adduser", "-D", "pmos", "-u",
pmb.config.chroot_uid_user], pmb.config.chroot_uid_user],
chroot) chroot)
@ -187,11 +188,11 @@ def init(args: PmbArgs, chroot: Chroot=Chroot.native(), usr_merge=UsrMerge.AUTO,
for target, link_name in pmb.config.chroot_home_symlinks.items(): for target, link_name in pmb.config.chroot_home_symlinks.items():
link_dir = os.path.dirname(link_name) link_dir = os.path.dirname(link_name)
if not os.path.exists(chroot / link_dir): if not os.path.exists(chroot / link_dir):
pmb.chroot.user(args, ["mkdir", "-p", link_dir], chroot) pmb.chroot.user(["mkdir", "-p", link_dir], chroot)
if not os.path.exists(chroot / target): if not os.path.exists(chroot / target):
pmb.chroot.root(args, ["mkdir", "-p", target], chroot) pmb.chroot.root(["mkdir", "-p", target], chroot)
pmb.chroot.user(args, ["ln", "-s", target, link_name], chroot) pmb.chroot.user(["ln", "-s", target, link_name], chroot)
pmb.chroot.root(args, ["chown", "pmos:pmos", target], chroot) pmb.chroot.root(["chown", "pmos:pmos", target], chroot)
# Upgrade packages in the chroot, in case alpine-base, apk, etc. have been # Upgrade packages in the chroot, in case alpine-base, apk, etc. have been
# built from source with pmbootstrap # built from source with pmbootstrap
@ -201,6 +202,6 @@ def init(args: PmbArgs, chroot: Chroot=Chroot.native(), usr_merge=UsrMerge.AUTO,
if os.getenv("PMB_APK_FORCE_MISSING_REPOSITORIES") == "1": if os.getenv("PMB_APK_FORCE_MISSING_REPOSITORIES") == "1":
command = ["--force-missing-repositories"] + command command = ["--force-missing-repositories"] + command
pmb.chroot.root(args, ["apk"] + command, chroot) pmb.chroot.root(["apk"] + command, chroot)
pmb.helpers.other.cache["pmb.chroot.init"][str(chroot)] pmb.helpers.other.cache["pmb.chroot.init"][str(chroot)] = True

View file

@ -6,26 +6,26 @@ import pmb.chroot.initfs_hooks
import pmb.chroot.other import pmb.chroot.other
import pmb.chroot.apk import pmb.chroot.apk
import pmb.config.pmaports import pmb.config.pmaports
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.cli import pmb.helpers.cli
from pmb.core import Chroot from pmb.core import Chroot
def build(args: PmbArgs, flavor, chroot: Chroot): def build(args: PmbArgs, flavor, chroot: Chroot):
# Update mkinitfs and hooks # Update mkinitfs and hooks
pmb.chroot.apk.install(args, ["postmarketos-mkinitfs"], chroot) pmb.chroot.apk.install(["postmarketos-mkinitfs"], chroot)
pmb.chroot.initfs_hooks.update(args, chroot) pmb.chroot.initfs_hooks.update(args, chroot)
pmaports_cfg = pmb.config.pmaports.read_config(args) pmaports_cfg = pmb.config.pmaports.read_config()
# Call mkinitfs # Call mkinitfs
logging.info(f"({chroot}) mkinitfs {flavor}") logging.info(f"({chroot}) mkinitfs {flavor}")
if pmaports_cfg.get("supported_mkinitfs_without_flavors", False): if pmaports_cfg.get("supported_mkinitfs_without_flavors", False):
pmb.chroot.root(args, ["mkinitfs"], chroot) pmb.chroot.root(["mkinitfs"], chroot)
else: else:
release_file = (chroot / "usr/share/kernel" / flavor / "kernel.release") release_file = (chroot / "usr/share/kernel" / flavor / "kernel.release")
with release_file.open() as handle: with release_file.open() as handle:
release = handle.read().rstrip() release = handle.read().rstrip()
pmb.chroot.root(args, ["mkinitfs", "-o", pmb.chroot.root(["mkinitfs", "-o",
f"/boot/initramfs-{flavor}", release], f"/boot/initramfs-{flavor}", release],
chroot) chroot)
@ -38,7 +38,7 @@ def extract(args: PmbArgs, flavor, chroot: Chroot, extra=False):
# Extraction folder # Extraction folder
inside = "/tmp/initfs-extracted" inside = "/tmp/initfs-extracted"
pmaports_cfg = pmb.config.pmaports.read_config(args) pmaports_cfg = pmb.config.pmaports.read_config()
if pmaports_cfg.get("supported_mkinitfs_without_flavors", False): if pmaports_cfg.get("supported_mkinitfs_without_flavors", False):
initfs_file = "/boot/initramfs" initfs_file = "/boot/initramfs"
else: else:
@ -53,7 +53,7 @@ def extract(args: PmbArgs, flavor, chroot: Chroot, extra=False):
" already exists." " already exists."
" Do you want to overwrite it?"): " Do you want to overwrite it?"):
raise RuntimeError("Aborted!") raise RuntimeError("Aborted!")
pmb.chroot.root(args, ["rm", "-r", inside], chroot) pmb.chroot.root(["rm", "-r", inside], chroot)
# Extraction script (because passing a file to stdin is not allowed # Extraction script (because passing a file to stdin is not allowed
# in pmbootstrap's chroot/shell functions for security reasons) # in pmbootstrap's chroot/shell functions for security reasons)
@ -71,7 +71,7 @@ def extract(args: PmbArgs, flavor, chroot: Chroot, extra=False):
["rm", "/tmp/_extract.sh", f"{inside}/_initfs"] ["rm", "/tmp/_extract.sh", f"{inside}/_initfs"]
] ]
for command in commands: for command in commands:
pmb.chroot.root(args, command, chroot) pmb.chroot.root(command, chroot)
# Return outside path for logging # Return outside path for logging
return outside return outside
@ -82,8 +82,8 @@ def ls(args: PmbArgs, flavor, suffix, extra=False):
if extra: if extra:
tmp = "/tmp/initfs-extra-extracted" tmp = "/tmp/initfs-extra-extracted"
extract(args, flavor, suffix, extra) extract(args, flavor, suffix, extra)
pmb.chroot.root(args, ["ls", "-lahR", "."], suffix, Path(tmp), "stdout") pmb.chroot.root(["ls", "-lahR", "."], suffix, Path(tmp), "stdout")
pmb.chroot.root(args, ["rm", "-r", tmp], suffix) pmb.chroot.root(["rm", "-r", tmp], suffix)
def frontend(args: PmbArgs): def frontend(args: PmbArgs):
@ -108,12 +108,12 @@ def frontend(args: PmbArgs):
# Handle hook actions # Handle hook actions
elif action == "hook_ls": elif action == "hook_ls":
pmb.chroot.initfs_hooks.ls(args, chroot) pmb.chroot.initfs_hooks.ls(chroot)
else: else:
if action == "hook_add": if action == "hook_add":
pmb.chroot.initfs_hooks.add(args, args.hook, chroot) pmb.chroot.initfs_hooks.add(args.hook, chroot)
elif action == "hook_del": elif action == "hook_del":
pmb.chroot.initfs_hooks.delete(args, args.hook, chroot) pmb.chroot.initfs_hooks.delete(args.hook, chroot)
# Rebuild the initfs after adding/removing a hook # Rebuild the initfs after adding/removing a hook
build(args, flavor, chroot) build(args, flavor, chroot)

View file

@ -6,11 +6,11 @@ from pmb.helpers import logging
import pmb.config import pmb.config
import pmb.chroot.apk import pmb.chroot.apk
from pmb.core import Chroot from pmb.core import Chroot, get_context
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
def list_chroot(args: PmbArgs, suffix: Chroot, remove_prefix=True): def list_chroot(suffix: Chroot, remove_prefix=True):
ret = [] ret = []
prefix = pmb.config.initfs_hook_prefix prefix = pmb.config.initfs_hook_prefix
for pkgname in pmb.chroot.apk.installed(suffix).keys(): for pkgname in pmb.chroot.apk.installed(suffix).keys():
@ -22,41 +22,41 @@ def list_chroot(args: PmbArgs, suffix: Chroot, remove_prefix=True):
return ret return ret
def list_aports(args: PmbArgs): def list_aports():
ret = [] ret = []
prefix = pmb.config.initfs_hook_prefix prefix = pmb.config.initfs_hook_prefix
for path in glob.glob(f"{args.aports}/*/{prefix}*"): for path in glob.glob(f"{get_context().config.aports}/*/{prefix}*"):
ret.append(os.path.basename(path)[len(prefix):]) ret.append(os.path.basename(path)[len(prefix):])
return ret return ret
def ls(args: PmbArgs, suffix: Chroot): def ls(suffix: Chroot):
hooks_chroot = list_chroot(args, suffix) hooks_chroot = list_chroot(suffix)
hooks_aports = list_aports(args) hooks_aports = list_aports()
for hook in hooks_aports: for hook in hooks_aports:
line = f"* {hook} ({'' if hook in hooks_chroot else 'not '}installed)" line = f"* {hook} ({'' if hook in hooks_chroot else 'not '}installed)"
logging.info(line) logging.info(line)
def add(args: PmbArgs, hook, suffix: Chroot): def add(hook, suffix: Chroot):
if hook not in list_aports(args): if hook not in list_aports():
raise RuntimeError("Invalid hook name!" raise RuntimeError("Invalid hook name!"
" Run 'pmbootstrap initfs hook_ls'" " Run 'pmbootstrap initfs hook_ls'"
" to get a list of all hooks.") " to get a list of all hooks.")
prefix = pmb.config.initfs_hook_prefix prefix = pmb.config.initfs_hook_prefix
pmb.chroot.apk.install(args, [f"{prefix}{hook}"], suffix) pmb.chroot.apk.install([f"{prefix}{hook}"], suffix)
def delete(args: PmbArgs, hook, suffix: Chroot): def delete(hook, suffix: Chroot):
if hook not in list_chroot(args, suffix): if hook not in list_chroot(suffix):
raise RuntimeError("There is no such hook installed!") raise RuntimeError("There is no such hook installed!")
prefix = pmb.config.initfs_hook_prefix prefix = pmb.config.initfs_hook_prefix
pmb.chroot.root(args, ["apk", "del", f"{prefix}{hook}"], suffix) pmb.chroot.root(["apk", "del", f"{prefix}{hook}"], suffix)
def update(args: PmbArgs, suffix: Chroot): def update(args: PmbArgs, suffix: Chroot):
""" """
Rebuild and update all hooks that are out of date Rebuild and update all hooks that are out of date
""" """
pmb.chroot.apk.install(args, list_chroot(args, suffix, False), suffix) pmb.chroot.apk.install(list_chroot(suffix, False), suffix)

View file

@ -5,13 +5,14 @@ import os
from pathlib import Path from pathlib import Path
from typing import Dict from typing import Dict
import pmb.config import pmb.config
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.run
import pmb.parse import pmb.parse
import pmb.helpers.mount import pmb.helpers.mount
from pmb.core import Chroot from pmb.core import Chroot, get_context
def create_device_nodes(args: PmbArgs, chroot: Chroot): def create_device_nodes(chroot: Chroot):
""" """
Create device nodes for null, zero, full, random, urandom in the chroot. Create device nodes for null, zero, full, random, urandom in the chroot.
""" """
@ -48,7 +49,7 @@ def create_device_nodes(args: PmbArgs, chroot: Chroot):
raise RuntimeError(f"Failed to create device nodes in the '{chroot}' chroot.") raise RuntimeError(f"Failed to create device nodes in the '{chroot}' chroot.")
def mount_dev_tmpfs(args: PmbArgs, chroot: Chroot=Chroot.native()): def mount_dev_tmpfs(chroot: Chroot=Chroot.native()):
""" """
Mount tmpfs inside the chroot's dev folder to make sure we can create Mount tmpfs inside the chroot's dev folder to make sure we can create
device nodes, even if the filesystem of the work folder does not support device nodes, even if the filesystem of the work folder does not support
@ -70,22 +71,22 @@ def mount_dev_tmpfs(args: PmbArgs, chroot: Chroot=Chroot.native()):
pmb.helpers.run.root(["mount", "-t", "tmpfs", pmb.helpers.run.root(["mount", "-t", "tmpfs",
"-o", "nodev,nosuid,noexec", "-o", "nodev,nosuid,noexec",
"tmpfs", dev / "shm"]) "tmpfs", dev / "shm"])
create_device_nodes(args, chroot) create_device_nodes(chroot)
# Setup /dev/fd as a symlink # Setup /dev/fd as a symlink
pmb.helpers.run.root(["ln", "-sf", "/proc/self/fd", f"{dev}/"]) pmb.helpers.run.root(["ln", "-sf", "/proc/self/fd", f"{dev}/"])
def mount(args: PmbArgs, chroot: Chroot=Chroot.native()): def mount(chroot: Chroot):
# Mount tmpfs as the chroot's /dev # Mount tmpfs as the chroot's /dev
mount_dev_tmpfs(args, chroot) mount_dev_tmpfs(chroot)
# Get all mountpoints # Get all mountpoints
arch = pmb.parse.arch.from_chroot_suffix(args, chroot) arch = chroot.arch
channel = pmb.config.pmaports.read_config(args)["channel"] channel = pmb.config.pmaports.read_config()["channel"]
mountpoints: Dict[Path, Path] = {} mountpoints: Dict[Path, Path] = {}
for src_template, target_template in pmb.config.chroot_mount_bind.items(): for src_template, target_template in pmb.config.chroot_mount_bind.items():
src_template = src_template.replace("$WORK", os.fspath(pmb.config.work)) src_template = src_template.replace("$WORK", os.fspath(get_context().config.work))
src_template = src_template.replace("$ARCH", arch) src_template = src_template.replace("$ARCH", arch)
src_template = src_template.replace("$CHANNEL", channel) src_template = src_template.replace("$CHANNEL", channel)
mountpoints[Path(src_template)] = Path(target_template) mountpoints[Path(src_template)] = Path(target_template)

View file

@ -4,7 +4,7 @@ import os
from pmb.helpers import logging from pmb.helpers import logging
from pathlib import Path from pathlib import Path
import pmb.chroot.apk import pmb.chroot.apk
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.install import pmb.install
from pmb.core import Chroot from pmb.core import Chroot
@ -24,7 +24,7 @@ def kernel_flavor_installed(args: PmbArgs, chroot: Chroot, autoinstall=True):
if autoinstall: if autoinstall:
packages = ([f"device-{args.device}"] + packages = ([f"device-{args.device}"] +
pmb.install.get_kernel_package(args, args.device)) pmb.install.get_kernel_package(args, args.device))
pmb.chroot.apk.install(args, packages, chroot) pmb.chroot.apk.install(packages, chroot)
glob_result = list((chroot / "usr/share/kernel").glob("*")) glob_result = list((chroot / "usr/share/kernel").glob("*"))
@ -41,8 +41,8 @@ def tempfolder(args: PmbArgs, path: Path, chroot: Chroot=Chroot.native()):
:returns: the path :returns: the path
""" """
if chroot / path: if chroot / path:
pmb.chroot.root(args, ["rm", "-r", path]) pmb.chroot.root(["rm", "-r", path])
pmb.chroot.user(args, ["mkdir", "-p", path]) pmb.chroot.user(["mkdir", "-p", path])
return path return path
@ -73,4 +73,4 @@ def copy_xauthority(args: PmbArgs):
if os.path.exists(copy): if os.path.exists(copy):
pmb.helpers.run.root(["rm", copy]) pmb.helpers.run.root(["rm", copy])
pmb.helpers.run.root(["cp", original, copy]) pmb.helpers.run.root(["cp", original, copy])
pmb.chroot.root(args, ["chown", "pmos:pmos", "/home/pmos/.Xauthority"]) pmb.chroot.root(["chown", "pmos:pmos", "/home/pmos/.Xauthority"])

View file

@ -11,7 +11,7 @@ import pmb.chroot.binfmt
import pmb.helpers.run import pmb.helpers.run
import pmb.helpers.run_core import pmb.helpers.run_core
from pmb.core import Chroot from pmb.core import Chroot
from pmb.core.types import Env, PathString, PmbArgs from pmb.types import Env, PathString, PmbArgs
def executables_absolute_path(): def executables_absolute_path():
@ -29,7 +29,7 @@ def executables_absolute_path():
return ret return ret
def root(args: PmbArgs, cmd: Sequence[PathString], chroot: Chroot=Chroot.native(), working_dir: PurePath=PurePath("/"), output="log", def root(cmd: Sequence[PathString], chroot: Chroot=Chroot.native(), working_dir: PurePath=PurePath("/"), output="log",
output_return=False, check=None, env={}, output_return=False, check=None, env={},
disable_timeout=False, add_proxy_env_vars=True): disable_timeout=False, add_proxy_env_vars=True):
""" """
@ -88,7 +88,7 @@ def root(args: PmbArgs, cmd: Sequence[PathString], chroot: Chroot=Chroot.native(
disable_timeout) disable_timeout)
def user(args: PmbArgs, cmd, chroot: Chroot=Chroot.native(), working_dir: Path = Path("/"), output="log", def user(cmd, chroot: Chroot=Chroot.native(), working_dir: Path = Path("/"), output="log",
output_return=False, check=None, env={}): output_return=False, check=None, env={}):
""" """
Run a command inside a chroot as "user". We always use the BusyBox Run a command inside a chroot as "user". We always use the BusyBox
@ -109,19 +109,19 @@ def user(args: PmbArgs, cmd, chroot: Chroot=Chroot.native(), working_dir: Path =
flat_cmd = pmb.helpers.run_core.flat_cmd(cmd, env=env) flat_cmd = pmb.helpers.run_core.flat_cmd(cmd, env=env)
cmd = ["busybox", "su", "pmos", "-c", flat_cmd] cmd = ["busybox", "su", "pmos", "-c", flat_cmd]
return pmb.chroot.root(args, cmd, chroot, working_dir, output, return pmb.chroot.root(cmd, chroot, working_dir, output,
output_return, check, {}, output_return, check, {},
add_proxy_env_vars=False) add_proxy_env_vars=False)
def exists(args: PmbArgs, username, chroot: Chroot=Chroot.native()): def exists(username, chroot: Chroot=Chroot.native()):
""" """
Checks if username exists in the system Checks if username exists in the system
:param username: User name :param username: User name
:returns: bool :returns: bool
""" """
output = pmb.chroot.root(args, ["getent", "passwd", username], output = pmb.chroot.root(["getent", "passwd", username],
chroot, output_return=True, check=False) chroot, output_return=True, check=False)
return len(output) > 0 return len(output) > 0

View file

@ -5,24 +5,24 @@ import socket
from contextlib import closing from contextlib import closing
import pmb.chroot import pmb.chroot
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.mount import pmb.helpers.mount
import pmb.install.losetup import pmb.install.losetup
import pmb.parse.arch import pmb.parse.arch
from pmb.core import Chroot, ChrootType from pmb.core import Chroot, ChrootType, get_context
def kill_adb(args: PmbArgs): def kill_adb():
""" """
Kill adb daemon if it's running. Kill adb daemon if it's running.
""" """
port = 5038 port = 5038
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock: with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
if sock.connect_ex(("127.0.0.1", port)) == 0: if sock.connect_ex(("127.0.0.1", port)) == 0:
pmb.chroot.root(args, ["adb", "-P", str(port), "kill-server"]) pmb.chroot.root(["adb", "-P", str(port), "kill-server"])
def kill_sccache(args: PmbArgs): def kill_sccache():
""" """
Kill sccache daemon if it's running. Unlike ccache it automatically spawns Kill sccache daemon if it's running. Unlike ccache it automatically spawns
a daemon when you call it and exits after some time of inactivity. a daemon when you call it and exits after some time of inactivity.
@ -30,17 +30,17 @@ def kill_sccache(args: PmbArgs):
port = 4226 port = 4226
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock: with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
if sock.connect_ex(("127.0.0.1", port)) == 0: if sock.connect_ex(("127.0.0.1", port)) == 0:
pmb.chroot.root(args, ["sccache", "--stop-server"]) pmb.chroot.root(["sccache", "--stop-server"])
def shutdown_cryptsetup_device(args: PmbArgs, name: str): def shutdown_cryptsetup_device(name: str):
""" """
:param name: cryptsetup device name, usually "pm_crypt" in pmbootstrap :param name: cryptsetup device name, usually "pm_crypt" in pmbootstrap
""" """
if not (Chroot.native() / "dev/mapper" / name).exists(): if not (Chroot.native() / "dev/mapper" / name).exists():
return return
pmb.chroot.apk.install(args, ["cryptsetup"]) pmb.chroot.apk.install(["cryptsetup"], Chroot.native())
status = pmb.chroot.root(args, ["cryptsetup", "status", name], status = pmb.chroot.root(["cryptsetup", "status", name],
output_return=True, check=False) output_return=True, check=False)
if not status: if not status:
logging.warning("WARNING: Failed to run cryptsetup to get the status" logging.warning("WARNING: Failed to run cryptsetup to get the status"
@ -49,31 +49,31 @@ def shutdown_cryptsetup_device(args: PmbArgs, name: str):
return return
if status.startswith("/dev/mapper/" + name + " is active."): if status.startswith("/dev/mapper/" + name + " is active."):
pmb.chroot.root(args, ["cryptsetup", "luksClose", name]) pmb.chroot.root(["cryptsetup", "luksClose", name])
elif status.startswith("/dev/mapper/" + name + " is inactive."): elif status.startswith("/dev/mapper/" + name + " is inactive."):
# When "cryptsetup status" fails, the device is not mounted and we # When "cryptsetup status" fails, the device is not mounted and we
# have a left over file (#83) # have a left over file (#83)
pmb.chroot.root(args, ["rm", "/dev/mapper/" + name]) pmb.chroot.root(["rm", "/dev/mapper/" + name])
else: else:
raise RuntimeError("Failed to parse 'cryptsetup status' output!") raise RuntimeError("Failed to parse 'cryptsetup status' output!")
def shutdown(args: PmbArgs, only_install_related=False): def shutdown(args: PmbArgs, only_install_related=False):
# Stop daemons # Stop daemons
kill_adb(args) kill_adb()
kill_sccache(args) kill_sccache()
chroot = Chroot.native() chroot = Chroot.native()
# Umount installation-related paths (order is important!) # Umount installation-related paths (order is important!)
pmb.helpers.mount.umount_all(chroot / "mnt/install") pmb.helpers.mount.umount_all(chroot / "mnt/install")
shutdown_cryptsetup_device(args, "pm_crypt") shutdown_cryptsetup_device("pm_crypt")
# Umount all losetup mounted images # Umount all losetup mounted images
if pmb.helpers.mount.ismount(chroot / "dev/loop-control"): if pmb.helpers.mount.ismount(chroot / "dev/loop-control"):
for path_outside in (chroot / "/home/pmos/rootfs").glob("*.img"): for path_outside in (chroot / "/home/pmos/rootfs").glob("*.img"):
path = path_outside.relative_to(chroot.path) path = path_outside.relative_to(chroot.path)
pmb.install.losetup.umount(args, path) pmb.install.losetup.umount(path)
# Umount device rootfs and installer chroots # Umount device rootfs and installer chroots
for chroot_type in [ChrootType.ROOTFS, ChrootType.INSTALLER]: for chroot_type in [ChrootType.ROOTFS, ChrootType.INSTALLER]:
@ -86,14 +86,14 @@ def shutdown(args: PmbArgs, only_install_related=False):
# the chroots, but we want it gone afterwards (e.g. when the chroot # the chroots, but we want it gone afterwards (e.g. when the chroot
# contents get copied to a rootfs / installer image, or if creating an # contents get copied to a rootfs / installer image, or if creating an
# android recovery zip from its contents). # android recovery zip from its contents).
for marker in pmb.config.work.glob("chroot_*/in-pmbootstrap"): for marker in get_context().config.work.glob("chroot_*/in-pmbootstrap"):
pmb.helpers.run.root(["rm", marker]) pmb.helpers.run.root(["rm", marker])
if not only_install_related: if not only_install_related:
# Umount all folders inside work dir # Umount all folders inside work dir
# The folders are explicitly iterated over, so folders symlinked inside # The folders are explicitly iterated over, so folders symlinked inside
# work dir get umounted as well (used in test_pkgrel_bump.py, #1595) # work dir get umounted as well (used in test_pkgrel_bump.py, #1595)
for path in pmb.config.work.glob("*"): for path in get_context().config.work.glob("*"):
pmb.helpers.mount.umount_all(path) pmb.helpers.mount.umount_all(path)
# Clean up the rest # Clean up the rest

View file

@ -7,11 +7,11 @@ import os
import pmb.chroot import pmb.chroot
import pmb.config.pmaports import pmb.config.pmaports
import pmb.config.workdir import pmb.config.workdir
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.pmaports import pmb.helpers.pmaports
import pmb.helpers.run import pmb.helpers.run
import pmb.parse.apkindex import pmb.parse.apkindex
from pmb.core import Chroot from pmb.core import Chroot, get_context
def zap(args: PmbArgs, confirm=True, dry=False, pkgs_local=False, http=False, def zap(args: PmbArgs, confirm=True, dry=False, pkgs_local=False, http=False,
@ -32,7 +32,7 @@ def zap(args: PmbArgs, confirm=True, dry=False, pkgs_local=False, http=False,
:param rust: Remove rust related caches :param rust: Remove rust related caches
:param netboot: Remove images for netboot :param netboot: Remove images for netboot
NOTE: This function gets called in pmb/config/init.py, with only pmb.config.work NOTE: This function gets called in pmb/config/init.py, with only get_context().config.work
and args.device set! and args.device set!
""" """
# Get current work folder size # Get current work folder size
@ -50,7 +50,7 @@ def zap(args: PmbArgs, confirm=True, dry=False, pkgs_local=False, http=False,
pmb.chroot.shutdown(args) pmb.chroot.shutdown(args)
# Deletion patterns for folders inside pmb.config.work # Deletion patterns for folders inside get_context().config.work
patterns = list(Chroot.iter_patterns()) patterns = list(Chroot.iter_patterns())
if pkgs_local: if pkgs_local:
patterns += ["packages"] patterns += ["packages"]
@ -66,7 +66,7 @@ def zap(args: PmbArgs, confirm=True, dry=False, pkgs_local=False, http=False,
# Delete everything matching the patterns # Delete everything matching the patterns
for pattern in patterns: for pattern in patterns:
logging.debug(f"Deleting {pattern}") logging.debug(f"Deleting {pattern}")
pattern = os.path.realpath(f"{pmb.config.work}/{pattern}") pattern = os.path.realpath(f"{get_context().config.work}/{pattern}")
matches = glob.glob(pattern) matches = glob.glob(pattern)
for match in matches: for match in matches:
if (not confirm or if (not confirm or
@ -87,8 +87,8 @@ def zap(args: PmbArgs, confirm=True, dry=False, pkgs_local=False, http=False,
def zap_pkgs_local_mismatch(args: PmbArgs, confirm=True, dry=False): def zap_pkgs_local_mismatch(args: PmbArgs, confirm=True, dry=False):
channel = pmb.config.pmaports.read_config(args)["channel"] channel = pmb.config.pmaports.read_config()["channel"]
if not os.path.exists(f"{pmb.config.work}/packages/{channel}"): if not os.path.exists(f"{get_context().config.work}/packages/{channel}"):
return return
question = "Remove binary packages that are newer than the corresponding" \ question = "Remove binary packages that are newer than the corresponding" \
@ -97,7 +97,7 @@ def zap_pkgs_local_mismatch(args: PmbArgs, confirm=True, dry=False):
return return
reindex = False reindex = False
for apkindex_path in (pmb.config.work / "packages" / channel).glob("*/APKINDEX.tar.gz"): for apkindex_path in (get_context().config.work / "packages" / channel).glob("*/APKINDEX.tar.gz"):
# Delete packages without same version in aports # Delete packages without same version in aports
blocks = pmb.parse.apkindex.parse_blocks(apkindex_path) blocks = pmb.parse.apkindex.parse_blocks(apkindex_path)
for block in blocks: for block in blocks:
@ -108,7 +108,7 @@ def zap_pkgs_local_mismatch(args: PmbArgs, confirm=True, dry=False):
# Apk path # Apk path
apk_path_short = f"{arch}/{pkgname}-{version}.apk" apk_path_short = f"{arch}/{pkgname}-{version}.apk"
apk_path = f"{pmb.config.work}/packages/{channel}/{apk_path_short}" apk_path = f"{get_context().config.work}/packages/{channel}/{apk_path_short}"
if not os.path.exists(apk_path): if not os.path.exists(apk_path):
logging.info("WARNING: Package mentioned in index not" logging.info("WARNING: Package mentioned in index not"
f" found: {apk_path_short}") f" found: {apk_path_short}")
@ -140,7 +140,7 @@ def zap_pkgs_local_mismatch(args: PmbArgs, confirm=True, dry=False):
def zap_pkgs_online_mismatch(args: PmbArgs, confirm=True, dry=False): def zap_pkgs_online_mismatch(args: PmbArgs, confirm=True, dry=False):
# Check whether we need to do anything # Check whether we need to do anything
paths = glob.glob(f"{pmb.config.work}/cache_apk_*") paths = glob.glob(f"{get_context().config.work}/cache_apk_*")
if not len(paths): if not len(paths):
return return
if (confirm and not pmb.helpers.cli.confirm(args, if (confirm and not pmb.helpers.cli.confirm(args,
@ -162,4 +162,4 @@ def zap_pkgs_online_mismatch(args: PmbArgs, confirm=True, dry=False):
# Clean the cache with apk # Clean the cache with apk
logging.info(f"({suffix}) apk -v cache clean") logging.info(f"({suffix}) apk -v cache clean")
if not dry: if not dry:
pmb.chroot.root(args, ["apk", "-v", "cache", "clean"], suffix) pmb.chroot.root(["apk", "-v", "cache", "clean"], suffix)

View file

@ -6,7 +6,7 @@ from pmb.helpers import logging
import os import os
from pathlib import Path from pathlib import Path
import pmb.chroot import pmb.chroot
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.cli import pmb.helpers.cli
from pmb.core import Chroot from pmb.core import Chroot
@ -135,9 +135,9 @@ def copy_git_repo_to_chroot(args: PmbArgs, topdir):
f"{tarball_path}.files"], topdir) f"{tarball_path}.files"], topdir)
ci_dir = Path("/home/pmos/ci") ci_dir = Path("/home/pmos/ci")
pmb.chroot.user(args, ["rm", "-rf", ci_dir]) pmb.chroot.user(["rm", "-rf", ci_dir])
pmb.chroot.user(args, ["mkdir", ci_dir]) pmb.chroot.user(["mkdir", ci_dir])
pmb.chroot.user(args, ["tar", "-xf", "/tmp/git.tar.gz"], pmb.chroot.user(["tar", "-xf", "/tmp/git.tar.gz"],
working_dir=ci_dir) working_dir=ci_dir)
@ -178,7 +178,7 @@ def run_scripts(args: PmbArgs, topdir, scripts):
repo_copied = True repo_copied = True
env = {"TESTUSER": "pmos"} env = {"TESTUSER": "pmos"}
rc = pmb.chroot.root(args, [script_path], check=False, env=env, rc = pmb.chroot.root([script_path], check=False, env=env,
working_dir=Path("/home/pmos/ci"), working_dir=Path("/home/pmos/ci"),
output="tui") output="tui")
if rc: if rc:

View file

@ -6,7 +6,7 @@ import enum
from typing import Generator, Optional from typing import Generator, Optional
from pathlib import Path, PosixPath, PurePosixPath from pathlib import Path, PosixPath, PurePosixPath
import pmb.config import pmb.config
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
from pmb.helpers import frontend from pmb.helpers import frontend
from .base import Command from .base import Command

View file

@ -5,7 +5,7 @@ from __future__ import annotations
from typing import Generator, List, Optional from typing import Generator, List, Optional
from pathlib import Path, PosixPath, PurePosixPath from pathlib import Path, PosixPath, PurePosixPath
from pmb import commands from pmb import commands
from pmb.core.types import PathString from pmb.types import PathString
from pmb.helpers import run from pmb.helpers import run
from pmb.core import get_context from pmb.core import get_context
from pmb import config from pmb import config

View file

@ -3,7 +3,7 @@
import multiprocessing import multiprocessing
import os import os
from pathlib import Path from pathlib import Path
from pmb.core.types import AportGenEntry, PathString from pmb.types import AportGenEntry, PathString
import pmb.parse.arch import pmb.parse.arch
import sys import sys
from typing import Dict, List, Sequence, TypedDict from typing import Dict, List, Sequence, TypedDict
@ -14,7 +14,6 @@ from typing import Dict, List, Sequence, TypedDict
# FIXME (#2324): this sucks, we should re-organise this and not rely on "lifting" # FIXME (#2324): this sucks, we should re-organise this and not rely on "lifting"
# this functions this way # this functions this way
from pmb.config.load import load, sanity_checks, save from pmb.config.load import load, sanity_checks, save
from pmb.config.merge_with_args import merge_with_args
from pmb.config.sudo import which_sudo from pmb.config.sudo import which_sudo
from pmb.config.other import is_systemd_selected from pmb.config.other import is_systemd_selected
@ -25,7 +24,6 @@ from pmb.config.other import is_systemd_selected
pmb_src: Path = Path(Path(__file__) / "../../..").resolve() pmb_src: Path = Path(Path(__file__) / "../../..").resolve()
apk_keys_path: Path = (pmb_src / "pmb/data/keys") apk_keys_path: Path = (pmb_src / "pmb/data/keys")
arch_native = pmb.parse.arch.alpine_native() arch_native = pmb.parse.arch.alpine_native()
work: Path
# apk-tools minimum version # apk-tools minimum version
# https://pkgs.alpinelinux.org/packages?name=apk-tools&branch=edge # https://pkgs.alpinelinux.org/packages?name=apk-tools&branch=edge
@ -77,15 +75,6 @@ def sudo(cmd: Sequence[PathString]) -> Sequence[PathString]:
return cmd return cmd
def work_dir(_work: Path) -> None:
"""Set the work directory. This is used in the main program to set the
work directory before any other code is run. It is not meant to be used
anywhere else."""
global work
if "work" in globals():
raise RuntimeError("work_dir() called multiple times!")
work = _work
# Keys saved in the config file (mostly what we ask in 'pmbootstrap init') # Keys saved in the config file (mostly what we ask in 'pmbootstrap init')
config_keys = [ config_keys = [
"aports", "aports",
@ -116,42 +105,7 @@ config_keys = [
"work", "work",
] ]
# Config file/commandline default values
# $WORK gets replaced with the actual value for pmb.config.work (which may be
# overridden on the commandline)
defaults = { defaults = {
# This first chunk matches config_keys
"aports": "$WORK/cache_git/pmaports",
"boot_size": "256",
"build_default_device_arch": False,
"build_pkgs_on_install": True,
"ccache_size": "5G",
"device": "qemu-amd64",
"extra_packages": "none",
"extra_space": "0",
"hostname": "",
"is_default_channel": True,
"jobs": str(multiprocessing.cpu_count() + 1),
"kernel": "stable",
"keymap": "",
"locale": "en_US.UTF-8",
# NOTE: mirrors use http by default to leverage caching
"mirror_alpine": "http://dl-cdn.alpinelinux.org/alpine/",
# NOTE: mirrors_postmarketos variable type is supposed to be
# comma-separated string, not a python list or any other type!
"mirrors_postmarketos": "http://mirror.postmarketos.org/postmarketos/",
"qemu_redir_stdio": False,
"ssh_key_glob": "~/.ssh/id_*.pub",
"ssh_keys": False,
"sudo_timer": False,
"systemd": "default",
"timezone": "GMT",
"ui": "console",
"ui_extras": False,
"user": "user",
"work": os.path.expanduser("~") + "/.local/var/pmbootstrap",
# These values are not part of config_keys
"cipher": "aes-xts-plain64", "cipher": "aes-xts-plain64",
"config": (os.environ.get('XDG_CONFIG_HOME') or "config": (os.environ.get('XDG_CONFIG_HOME') or
os.path.expanduser("~/.config")) + "/pmbootstrap.cfg", os.path.expanduser("~/.config")) + "/pmbootstrap.cfg",
@ -223,7 +177,7 @@ chroot_path = ":".join([
chroot_host_path = os.environ["PATH"] + ":/usr/sbin/" chroot_host_path = os.environ["PATH"] + ":/usr/sbin/"
# Folders that get mounted inside the chroot # Folders that get mounted inside the chroot
# $WORK gets replaced with pmb.config.work # $WORK gets replaced with get_context().config.work
# $ARCH gets replaced with the chroot architecture (eg. x86_64, armhf) # $ARCH gets replaced with the chroot architecture (eg. x86_64, armhf)
# $CHANNEL gets replaced with the release channel (e.g. edge, v21.03) # $CHANNEL gets replaced with the release channel (e.g. edge, v21.03)
# Use no more than one dir after /mnt/pmbootstrap, see remove_mnt_pmbootstrap. # Use no more than one dir after /mnt/pmbootstrap, see remove_mnt_pmbootstrap.

View file

@ -1,5 +1,6 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pmb.core import get_context
from pmb.core.chroot import Chroot from pmb.core.chroot import Chroot
from pmb.helpers import logging from pmb.helpers import logging
import glob import glob
@ -11,7 +12,7 @@ from typing import Any, List
import pmb.aportgen import pmb.aportgen
import pmb.config import pmb.config
import pmb.config.pmaports import pmb.config.pmaports
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.cli import pmb.helpers.cli
import pmb.helpers.devices import pmb.helpers.devices
import pmb.helpers.git import pmb.helpers.git
@ -67,7 +68,7 @@ def ask_for_work_path(args: PmbArgs):
while True: while True:
try: try:
work = os.path.expanduser(pmb.helpers.cli.ask( work = os.path.expanduser(pmb.helpers.cli.ask(
"Work path", None, pmb.config.work, False)) "Work path", None, get_context().config.work, False))
work = os.path.realpath(work) work = os.path.realpath(work)
exists = os.path.exists(work) exists = os.path.exists(work)
@ -107,7 +108,7 @@ def ask_for_channel(args: PmbArgs):
:returns: channel name (e.g. "edge", "v21.03") :returns: channel name (e.g. "edge", "v21.03")
""" """
channels_cfg = pmb.helpers.git.parse_channels_cfg(args) channels_cfg = pmb.helpers.git.parse_channels_cfg(get_context().config.aports)
count = len(channels_cfg["channels"]) count = len(channels_cfg["channels"])
# List channels # List channels
@ -123,7 +124,7 @@ def ask_for_channel(args: PmbArgs):
# Otherwise, if valid: channel from pmaports.cfg of current branch # Otherwise, if valid: channel from pmaports.cfg of current branch
# The actual channel name is not saved in pmbootstrap.cfg, because then we # The actual channel name is not saved in pmbootstrap.cfg, because then we
# would need to sync it with what is checked out in pmaports.git. # would need to sync it with what is checked out in pmaports.git.
default = pmb.config.pmaports.read_config(args)["channel"] default = pmb.config.pmaports.read_config()["channel"]
choices = channels_cfg["channels"].keys() choices = channels_cfg["channels"].keys()
if args.is_default_channel or default not in choices: if args.is_default_channel or default not in choices:
default = channels_cfg["meta"]["recommended"] default = channels_cfg["meta"]["recommended"]
@ -145,7 +146,7 @@ def ask_for_ui(args: PmbArgs, info):
if not device_is_accelerated: if not device_is_accelerated:
for i in reversed(range(len(ui_list))): for i in reversed(range(len(ui_list))):
pkgname = f"postmarketos-ui-{ui_list[i][0]}" pkgname = f"postmarketos-ui-{ui_list[i][0]}"
apkbuild = pmb.helpers.pmaports.get(args, pkgname, apkbuild = pmb.helpers.pmaports.get(pkgname,
subpackages=False, subpackages=False,
must_exist=False) must_exist=False)
if apkbuild and "pmb:gpu-accel" in apkbuild["options"]: if apkbuild and "pmb:gpu-accel" in apkbuild["options"]:
@ -176,7 +177,7 @@ def ask_for_ui(args: PmbArgs, info):
def ask_for_ui_extras(args: PmbArgs, ui): def ask_for_ui_extras(args: PmbArgs, ui):
apkbuild = pmb.helpers.pmaports.get(args, f"postmarketos-ui-{ui}", apkbuild = pmb.helpers.pmaports.get(f"postmarketos-ui-{ui}",
subpackages=False, must_exist=False) subpackages=False, must_exist=False)
if not apkbuild: if not apkbuild:
return False return False
@ -193,15 +194,15 @@ def ask_for_ui_extras(args: PmbArgs, ui):
def ask_for_systemd(args: PmbArgs, ui): def ask_for_systemd(args: PmbArgs, ui):
if "systemd" not in pmb.config.pmaports.read_config_repos(args): if "systemd" not in pmb.config.pmaports.read_config_repos():
return args.systemd return args.systemd
if pmb.helpers.ui.check_option(args, ui, "pmb:systemd-never"): if pmb.helpers.ui.check_option(ui, "pmb:systemd-never"):
logging.info("Based on your UI selection, OpenRC will be used as init" logging.info("Based on your UI selection, OpenRC will be used as init"
" system. This UI does not support systemd.") " system. This UI does not support systemd.")
return args.systemd return args.systemd
default_is_systemd = pmb.helpers.ui.check_option(args, ui, "pmb:systemd") default_is_systemd = pmb.helpers.ui.check_option(ui, "pmb:systemd")
not_str = " " if default_is_systemd else " not " not_str = " " if default_is_systemd else " not "
logging.info("Based on your UI selection, 'default' will result" logging.info("Based on your UI selection, 'default' will result"
f" in{not_str}installing systemd.") f" in{not_str}installing systemd.")
@ -267,7 +268,7 @@ def ask_for_provider_select(args: PmbArgs, apkbuild, providers_cfg):
providers. Updated with new providers after selection providers. Updated with new providers after selection
""" """
for select in apkbuild["_pmb_select"]: for select in apkbuild["_pmb_select"]:
providers = pmb.helpers.pmaports.find_providers(args, select) providers = pmb.helpers.pmaports.find_providers(select)
logging.info(f"Available providers for {select} ({len(providers)}):") logging.info(f"Available providers for {select} ({len(providers)}):")
has_default = False has_default = False
@ -322,7 +323,7 @@ def ask_for_provider_select_pkg(args: PmbArgs, pkgname, providers_cfg):
:param providers_cfg: the configuration section with previously selected :param providers_cfg: the configuration section with previously selected
providers. Updated with new providers after selection providers. Updated with new providers after selection
""" """
apkbuild = pmb.helpers.pmaports.get(args, pkgname, apkbuild = pmb.helpers.pmaports.get(pkgname,
subpackages=False, must_exist=False) subpackages=False, must_exist=False)
if not apkbuild: if not apkbuild:
return return
@ -330,7 +331,7 @@ def ask_for_provider_select_pkg(args: PmbArgs, pkgname, providers_cfg):
ask_for_provider_select(args, apkbuild, providers_cfg) ask_for_provider_select(args, apkbuild, providers_cfg)
def ask_for_device_kernel(args: PmbArgs, device): def ask_for_device_kernel(args: PmbArgs, device: str):
"""Ask for the kernel that should be used with the device. """Ask for the kernel that should be used with the device.
:param device: code name, e.g. "lg-mako" :param device: code name, e.g. "lg-mako"
@ -341,7 +342,7 @@ def ask_for_device_kernel(args: PmbArgs, device):
""" """
# Get kernels # Get kernels
kernels = pmb.parse._apkbuild.kernels(args, device) kernels = pmb.parse._apkbuild.kernels(device)
if not kernels: if not kernels:
return args.kernel return args.kernel
@ -383,7 +384,7 @@ def ask_for_device(args: PmbArgs):
* device_exists: bool indicating if device port exists in repo * device_exists: bool indicating if device port exists in repo
* kernel: type of kernel (downstream, etc) * kernel: type of kernel (downstream, etc)
""" """
vendors = sorted(pmb.helpers.devices.list_vendors(args)) vendors = sorted(pmb.helpers.devices.list_vendors(get_context().config.aports))
logging.info("Choose your target device vendor (either an " logging.info("Choose your target device vendor (either an "
"existing one, or a new one for porting).") "existing one, or a new one for porting).")
logging.info(f"Available vendors ({len(vendors)}): {', '.join(vendors)}") logging.info(f"Available vendors ({len(vendors)}): {', '.join(vendors)}")
@ -409,7 +410,7 @@ def ask_for_device(args: PmbArgs):
else: else:
# Archived devices can be selected, but are not displayed # Archived devices can be selected, but are not displayed
devices = sorted(pmb.helpers.devices.list_codenames( devices = sorted(pmb.helpers.devices.list_codenames(
args, vendor, archived=False)) get_context().config.aports, vendor, archived=False))
# Remove "vendor-" prefixes from device list # Remove "vendor-" prefixes from device list
codenames = [x.split('-', 1)[1] for x in devices] codenames = [x.split('-', 1)[1] for x in devices]
logging.info(f"Available codenames ({len(codenames)}): " + logging.info(f"Available codenames ({len(codenames)}): " +
@ -422,7 +423,7 @@ def ask_for_device(args: PmbArgs):
codenames) codenames)
device = f"{vendor}-{codename}" device = f"{vendor}-{codename}"
device_path = pmb.helpers.devices.find_path(args, device, 'deviceinfo') device_path = pmb.helpers.devices.find_path(device, 'deviceinfo')
if device_path is None: if device_path is None:
if device == args.device: if device == args.device:
raise RuntimeError( raise RuntimeError(
@ -460,7 +461,7 @@ def ask_for_additional_options(args: PmbArgs, cfg):
f" parallel jobs: {args.jobs}," f" parallel jobs: {args.jobs},"
f" ccache per arch: {args.ccache_size}," f" ccache per arch: {args.ccache_size},"
f" sudo timer: {context.sudo_timer}," f" sudo timer: {context.sudo_timer},"
f" mirror: {','.join(args.mirrors_postmarketos)}") f" mirror: {','.join(context.config.mirrors_postmarketos)}")
if not pmb.helpers.cli.confirm(args, "Change them?", if not pmb.helpers.cli.confirm(args, "Change them?",
default=False): default=False):
@ -517,7 +518,7 @@ def ask_for_additional_options(args: PmbArgs, cfg):
# Mirrors # Mirrors
# prompt for mirror change # prompt for mirror change
logging.info("Selected mirror:" logging.info("Selected mirror:"
f" {','.join(args.mirrors_postmarketos)}") f" {','.join(context.config.mirrors_postmarketos)}")
if pmb.helpers.cli.confirm(args, "Change mirror?", default=False): if pmb.helpers.cli.confirm(args, "Change mirror?", default=False):
mirrors = ask_for_mirror(args) mirrors = ask_for_mirror(args)
cfg["pmbootstrap"]["mirrors_postmarketos"] = ",".join(mirrors) cfg["pmbootstrap"]["mirrors_postmarketos"] = ",".join(mirrors)
@ -527,7 +528,7 @@ def ask_for_mirror(args: PmbArgs):
regex = "^[1-9][0-9]*$" # single non-zero number only regex = "^[1-9][0-9]*$" # single non-zero number only
json_path = pmb.helpers.http.download( json_path = pmb.helpers.http.download(
args, "https://postmarketos.org/mirrors.json", "pmos_mirrors", "https://postmarketos.org/mirrors.json", "pmos_mirrors",
cache=False) cache=False)
with open(json_path, "rt") as handle: with open(json_path, "rt") as handle:
s = handle.read() s = handle.read()
@ -558,7 +559,7 @@ def ask_for_mirror(args: PmbArgs):
urls.append(link_list[0]) urls.append(link_list[0])
mirror_indexes = [] mirror_indexes = []
for mirror in args.mirrors_postmarketos: for mirror in get_context().config.mirrors_postmarketos:
for i in range(len(urls)): for i in range(len(urls)):
if urls[i] == mirror: if urls[i] == mirror:
mirror_indexes.append(str(i + 1)) mirror_indexes.append(str(i + 1))
@ -647,64 +648,64 @@ def frontend(args: PmbArgs):
require_programs() require_programs()
# Work folder (needs to be first, so we can create chroots early) # Work folder (needs to be first, so we can create chroots early)
cfg = pmb.config.load(args) config = pmb.config.load(args)
work, work_exists = ask_for_work_path(args) config.work, work_exists = ask_for_work_path(args)
cfg["pmbootstrap"]["work"] = work
# Update args and save config (so chroots and 'pmbootstrap log' work) # Update args and save config (so chroots and 'pmbootstrap log' work)
pmb.helpers.args.update_work(args, work) pmb.helpers.args.update_work(args, config.work)
pmb.config.save(args, cfg) pmb.config.save(args.config, config)
# Migrate work dir if necessary # Migrate work dir if necessary
pmb.helpers.other.migrate_work_folder(args) pmb.helpers.other.migrate_work_folder(args)
# Clone pmaports # Clone pmaports
pmb.config.pmaports.init(args) pmb.config.pmaports.init()
# Choose release channel, possibly switch pmaports branch # Choose release channel, possibly switch pmaports branch
channel = ask_for_channel(args) channel = ask_for_channel(args)
pmb.config.pmaports.switch_to_channel_branch(args, channel) pmb.config.pmaports.switch_to_channel_branch(args, channel)
cfg["pmbootstrap"]["is_default_channel"] = "False" # FIXME: ???
config.is_default_channel = False
# Copy the git hooks if master was checked out. (Don't symlink them and # Copy the git hooks if master was checked out. (Don't symlink them and
# only do it on master, so the git hooks don't change unexpectedly when # only do it on master, so the git hooks don't change unexpectedly when
# having a random branch checked out.) # having a random branch checked out.)
branch_current = pmb.helpers.git.rev_parse(args.aports, branch_current = pmb.helpers.git.rev_parse(get_context().config.aports,
extra_args=["--abbrev-ref"]) extra_args=["--abbrev-ref"])
if branch_current == "master": if branch_current == "master":
logging.info("NOTE: pmaports is on master branch, copying git hooks.") logging.info("NOTE: pmaports is on master branch, copying git hooks.")
pmb.config.pmaports.install_githooks(args) pmb.config.pmaports.install_githooks()
# Device # Device
device, device_exists, kernel = ask_for_device(args) device, device_exists, kernel = ask_for_device(args)
cfg["pmbootstrap"]["device"] = device config.device = device
cfg["pmbootstrap"]["kernel"] = kernel config.kernel = kernel
info = pmb.parse.deviceinfo(args, device) info = pmb.parse.deviceinfo(args, device)
apkbuild_path = pmb.helpers.devices.find_path(args, device, 'APKBUILD') apkbuild_path = pmb.helpers.devices.find_path(device, 'APKBUILD')
if apkbuild_path: if apkbuild_path:
apkbuild = pmb.parse.apkbuild(apkbuild_path) apkbuild = pmb.parse.apkbuild(apkbuild_path)
ask_for_provider_select(args, apkbuild, cfg["providers"]) ask_for_provider_select(args, apkbuild, config.providers)
# Device keymap # Device keymap
if device_exists: if device_exists:
cfg["pmbootstrap"]["keymap"] = ask_for_keymaps(args, info) config.keymap = ask_for_keymaps(args, info)
cfg["pmbootstrap"]["user"] = ask_for_username(args) config.user = ask_for_username(args)
ask_for_provider_select_pkg(args, "postmarketos-base", cfg["providers"]) ask_for_provider_select_pkg(args, "postmarketos-base", config.providers)
ask_for_provider_select_pkg(args, "postmarketos-base-ui", cfg["providers"]) ask_for_provider_select_pkg(args, "postmarketos-base-ui", config.providers)
# UI and various build options # UI and various build options
ui = ask_for_ui(args, info) ui = ask_for_ui(args, info)
cfg["pmbootstrap"]["ui"] = ui config.ui = ui
cfg["pmbootstrap"]["ui_extras"] = str(ask_for_ui_extras(args, ui)) config.ui_extras = ask_for_ui_extras(args, ui)
# systemd # systemd
cfg["pmbootstrap"]["systemd"] = ask_for_systemd(args, ui) config.systemd = ask_for_systemd(args, ui)
ask_for_provider_select_pkg(args, f"postmarketos-ui-{ui}", ask_for_provider_select_pkg(args, f"postmarketos-ui-{ui}",
cfg["providers"]) config.providers)
ask_for_additional_options(args, cfg) ask_for_additional_options(args, config)
# Extra packages to be installed to rootfs # Extra packages to be installed to rootfs
logging.info("Additional packages that will be installed to rootfs." logging.info("Additional packages that will be installed to rootfs."
@ -713,29 +714,28 @@ def frontend(args: PmbArgs):
extra = pmb.helpers.cli.ask("Extra packages", None, extra = pmb.helpers.cli.ask("Extra packages", None,
args.extra_packages, args.extra_packages,
validation_regex=r"^([-.+\w]+)(,[-.+\w]+)*$") validation_regex=r"^([-.+\w]+)(,[-.+\w]+)*$")
cfg["pmbootstrap"]["extra_packages"] = extra config.extra_packages = extra
# Configure timezone info # Configure timezone info
cfg["pmbootstrap"]["timezone"] = ask_for_timezone(args) config.timezone = ask_for_timezone(args)
# Locale # Locale
cfg["pmbootstrap"]["locale"] = ask_for_locale(args) config.locale = ask_for_locale(args)
# Hostname # Hostname
cfg["pmbootstrap"]["hostname"] = ask_for_hostname(args, device) config.hostname = ask_for_hostname(args, device)
# SSH keys # SSH keys
cfg["pmbootstrap"]["ssh_keys"] = str(ask_for_ssh_keys(args)) config.ssh_keys = ask_for_ssh_keys(args)
# pmaports path (if users change it with: 'pmbootstrap --aports=... init') # pmaports path (if users change it with: 'pmbootstrap --aports=... init')
cfg["pmbootstrap"]["aports"] = args.aports config.aports = get_context().config.aports
# Build outdated packages in pmbootstrap install # Build outdated packages in pmbootstrap install
cfg["pmbootstrap"]["build_pkgs_on_install"] = str( config.build_pkgs_on_install = ask_build_pkgs_on_install(args)
ask_build_pkgs_on_install(args))
# Save config # Save config
pmb.config.save(args, cfg) pmb.config.save(args.config, config)
# Zap existing chroots # Zap existing chroots
if (work_exists and device_exists and if (work_exists and device_exists and

View file

@ -1,15 +1,18 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pathlib import Path, PosixPath
from typing import Any, Dict
from pmb.helpers import logging from pmb.helpers import logging
import configparser import configparser
import os import os
import sys import sys
import pmb.config import pmb.config
from pmb.core.types import PmbArgs from pmb.types import Config
from pmb.types import PmbArgs
def sanity_check(args: PmbArgs, cfg, key, allowed, print_path): def sanity_check(args: PmbArgs, cfg: Config, key, allowed, print_path):
value = cfg["pmbootstrap"][key] value = getattr(cfg, key)
if value in allowed: if value in allowed:
return return
@ -23,12 +26,14 @@ def sanity_check(args: PmbArgs, cfg, key, allowed, print_path):
sys.exit(1) sys.exit(1)
def sanity_checks(args: PmbArgs, cfg, print_path=True): def sanity_checks(args: PmbArgs, cfg: Config, print_path=True):
for key, allowed in pmb.config.allowed_values.items(): for key, allowed in pmb.config.allowed_values.items():
sanity_check(args, cfg, key, allowed, print_path) sanity_check(args, cfg, key, allowed, print_path)
def load(args: PmbArgs): def load(args: PmbArgs) -> Config:
config = Config()
cfg = configparser.ConfigParser() cfg = configparser.ConfigParser()
if os.path.isfile(args.config): if os.path.isfile(args.config):
cfg.read(args.config) cfg.read(args.config)
@ -38,27 +43,47 @@ def load(args: PmbArgs):
if "providers" not in cfg: if "providers" not in cfg:
cfg["providers"] = {} cfg["providers"] = {}
for key in pmb.config.defaults: for key in Config.__dict__.keys():
if key in pmb.config.config_keys and key not in cfg["pmbootstrap"]: if key == "providers":
cfg["pmbootstrap"][key] = str(pmb.config.defaults[key]) setattr(config, key, cfg["providers"])
# Handle whacky type conversions
elif key == "mirrors_postmarketos":
config.mirrors_postmarketos = cfg["pmbootstrap"]["mirrors_postmarketos"].split(",")
# Convert strings to paths
elif type(getattr(Config, key)) == PosixPath:
setattr(config, key, Path(cfg["pmbootstrap"][key]))
elif isinstance(getattr(Config, key), bool):
setattr(config, key, cfg["pmbootstrap"][key].lower() == "true")
elif key in cfg["pmbootstrap"]:
setattr(config, key, cfg["pmbootstrap"][key])
# We used to save default values in the config, which can *not* be sanity_checks(args, config)
# configured in "pmbootstrap init". That doesn't make sense, we always
# want to use the defaults from pmb/config/__init__.py in that case,
# not some outdated version we saved some time back (eg. aports folder,
# postmarketOS binary packages mirror).
if key not in pmb.config.config_keys and key in cfg["pmbootstrap"]:
logging.debug("Ignored unconfigurable and possibly outdated"
" default value from config:"
f" {cfg['pmbootstrap'][key]}")
del cfg["pmbootstrap"][key]
sanity_checks(args, cfg) return config
return cfg def save(output: Path, config: Config):
logging.debug(f"Save config: {output}")
output.parent.mkdir(parents=True, exist_ok=True)
output.touch(0o700, exist_ok=True)
def save(args: PmbArgs, cfg): cfg = configparser.ConfigParser()
logging.debug(f"Save config: {args.config}") cfg["pmbootstrap"] = {}
os.makedirs(os.path.dirname(args.config), 0o700, True) cfg["providers"] = {}
with open(args.config, "w") as handle:
for key in Config.__dict__.keys():
print(key)
if key == "providers":
cfg["providers"] = config.providers
# Handle whacky type conversions
elif key == "mirrors_postmarketos":
cfg["pmbootstrap"]["mirrors_postmarketos"] = ",".join(config.mirrors_postmarketos)
# Convert strings to paths
elif type(getattr(Config, key)) == Path:
cfg["pmbootstrap"][key] = str(getattr(config, key))
elif isinstance(getattr(Config, key), bool):
cfg["pmbootstrap"][key] = str(getattr(config, key))
else:
cfg["pmbootstrap"] = getattr(config, key)
with output.open("w") as handle:
cfg.write(handle) cfg.write(handle)

View file

@ -2,45 +2,42 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pathlib import Path from pathlib import Path
import pmb.config import pmb.config
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
def merge_with_args(args: PmbArgs): # def merge_with_args(args: PmbArgs):
"""We have the internal config (pmb/config/__init__.py) and the user config # """We have the internal config (pmb/config/__init__.py) and the user config
(usually ~/.config/pmbootstrap.cfg, can be changed with the '-c' # (usually ~/.config/pmbootstrap.cfg, can be changed with the '-c'
parameter). # parameter).
Args holds the variables parsed from the commandline (e.g. -j fills out # Args holds the variables parsed from the commandline (e.g. -j fills out
args.jobs), and values specified on the commandline count the most. # args.jobs), and values specified on the commandline count the most.
In case it is not specified on the commandline, for the keys in # In case it is not specified on the commandline, for the keys in
pmb.config.config_keys, we look into the value set in the the user config. # pmb.config.config_keys, we look into the value set in the the user config.
When that is empty as well (e.g. just before pmbootstrap init), or the key # When that is empty as well (e.g. just before pmbootstrap init), or the key
is not in pmb.config_keys, we use the default value from the internal # is not in pmb.config_keys, we use the default value from the internal
config. # config.
""" # """
# Use defaults from the user's config file # # Use defaults from the user's config file
cfg = pmb.config.load(args) # cfg = pmb.config.load(args)
for key in cfg["pmbootstrap"]: # for key in cfg["pmbootstrap"]:
if key not in args or getattr(args, key) is None: # if key not in args or getattr(args, key) is None:
value = cfg["pmbootstrap"][key] # value = cfg["pmbootstrap"][key]
if key in pmb.config.defaults: # if key in pmb.config.defaults:
default = pmb.config.defaults[key] # default = pmb.config.defaults[key]
if isinstance(default, bool): # if isinstance(default, bool):
value = (value.lower() == "true") # value = (value.lower() == "true")
setattr(args, key, value) # setattr(args, key, value)
setattr(args, 'selected_providers', cfg['providers']) # setattr(args, 'selected_providers', cfg['providers'])
# Use defaults from pmb.config.defaults # # Use defaults from pmb.config.defaults
for key, value in pmb.config.defaults.items(): # for key, value in pmb.config.defaults.items():
if key not in args or getattr(args, key) is None: # if key not in args or getattr(args, key) is None:
setattr(args, key, value) # setattr(args, key, value)
pmb.config.work_dir(Path(cfg["pmbootstrap"]["work"])) # pmb.config.work_dir(Path(cfg["pmbootstrap"]["work"]))
# Make sure args.aports is a Path object # # Make sure args.aports is a Path object
setattr(args, "aports", Path(args.aports)) # setattr(args, "aports", Path(args.aports))
# args.work is deprecated!
delattr(args, "work")

View file

@ -1,31 +1,31 @@
# Copyright 2024 Oliver Smith # Copyright 2024 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pmb.core.types import PmbArgs from pmb.types import Config
import pmb.helpers.ui import pmb.helpers.ui
import pmb.config.pmaports import pmb.config.pmaports
def is_systemd_selected(args: PmbArgs): def is_systemd_selected(config: Config):
if "systemd" not in pmb.config.pmaports.read_config_repos(args): if "systemd" not in pmb.config.pmaports.read_config_repos():
return False return False
if pmb.helpers.ui.check_option(args, args.ui, "pmb:systemd-never"): if pmb.helpers.ui.check_option(config.ui, "pmb:systemd-never"):
return False return False
if args.systemd == "always": if config.systemd == "always":
return True return True
if args.systemd == "never": if config.systemd == "never":
return False return False
return pmb.helpers.ui.check_option(args, args.ui, "pmb:systemd") return pmb.helpers.ui.check_option(config.ui, "pmb:systemd")
def systemd_selected_str(args): def systemd_selected_str(config: Config):
if "systemd" not in pmb.config.pmaports.read_config_repos(args): if "systemd" not in pmb.config.pmaports.read_config_repos():
return "no", "not supported by pmaports branch" return "no", "not supported by pmaports branch"
if pmb.helpers.ui.check_option(args, args.ui, "pmb:systemd-never"): if pmb.helpers.ui.check_option(config.ui, "pmb:systemd-never"):
return "no", "not supported by selected UI" return "no", "not supported by selected UI"
if args.systemd == "always": if config.systemd == "always":
return "yes", "'always' selected in 'pmbootstrap init'" return "yes", "'always' selected in 'pmbootstrap init'"
if args.systemd == "never": if config.systemd == "never":
return "no", "'never' selected in 'pmbootstrap init'" return "no", "'never' selected in 'pmbootstrap init'"
if pmb.helpers.ui.check_option(args, args.ui, "pmb:systemd"): if pmb.helpers.ui.check_option(config.ui, "pmb:systemd"):
return "yes", "default for selected UI" return "yes", "default for selected UI"
return "no", "default for selected UI" return "no", "default for selected UI"

View file

@ -1,12 +1,14 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import configparser import configparser
from pathlib import Path
from pmb.core import get_context
from pmb.helpers import logging from pmb.helpers import logging
import os import os
import sys import sys
import pmb.config import pmb.config
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.git import pmb.helpers.git
import pmb.helpers.pmaports import pmb.helpers.pmaports
import pmb.parse.version import pmb.parse.version
@ -56,7 +58,7 @@ def check_version_pmbootstrap(min_ver):
" of pmbootstrap from git.") " of pmbootstrap from git.")
def read_config_repos(args: PmbArgs): def read_config_repos():
""" Read the sections starting with "repo:" from pmaports.cfg. """ """ Read the sections starting with "repo:" from pmaports.cfg. """
# Try cache first # Try cache first
cache_key = "pmb.config.pmaports.read_config_repos" cache_key = "pmb.config.pmaports.read_config_repos"
@ -64,7 +66,7 @@ def read_config_repos(args: PmbArgs):
return pmb.helpers.other.cache[cache_key] return pmb.helpers.other.cache[cache_key]
cfg = configparser.ConfigParser() cfg = configparser.ConfigParser()
cfg.read(f"{args.aports}/pmaports.cfg") cfg.read(f"{get_context().config.aports}/pmaports.cfg")
ret = {} ret = {}
for section in cfg.keys(): for section in cfg.keys():
@ -78,21 +80,22 @@ def read_config_repos(args: PmbArgs):
return ret return ret
def read_config(args: PmbArgs): def read_config():
"""Read and verify pmaports.cfg.""" """Read and verify pmaports.cfg."""
# Try cache first # Try cache first
cache_key = "pmb.config.pmaports.read_config" cache_key = "pmb.config.pmaports.read_config"
if pmb.helpers.other.cache[cache_key]: if pmb.helpers.other.cache[cache_key]:
return pmb.helpers.other.cache[cache_key] return pmb.helpers.other.cache[cache_key]
aports = get_context().config.aports
# Migration message # Migration message
if not os.path.exists(args.aports): if not os.path.exists(aports):
logging.error(f"ERROR: pmaports dir not found: {args.aports}") logging.error(f"ERROR: pmaports dir not found: {aports}")
logging.error("Did you run 'pmbootstrap init'?") logging.error("Did you run 'pmbootstrap init'?")
sys.exit(1) sys.exit(1)
# Require the config # Require the config
path_cfg = args.aports / "pmaports.cfg" path_cfg = aports / "pmaports.cfg"
if not os.path.exists(path_cfg): if not os.path.exists(path_cfg):
raise RuntimeError("Invalid pmaports repository, could not find the" raise RuntimeError("Invalid pmaports repository, could not find the"
f" config: {path_cfg}") f" config: {path_cfg}")
@ -114,7 +117,7 @@ def read_config(args: PmbArgs):
return ret return ret
def read_config_channel(args: PmbArgs): def read_config_channel():
"""Get the properties of the currently active channel in pmaports.git. """Get the properties of the currently active channel in pmaports.git.
As specified in channels.cfg (https://postmarketos.org/channels.cfg). As specified in channels.cfg (https://postmarketos.org/channels.cfg).
@ -125,18 +128,19 @@ def read_config_channel(args: PmbArgs):
"mirrordir_alpine": ...} "mirrordir_alpine": ...}
""" """
channel = read_config(args)["channel"] aports = get_context().config.aports
channels_cfg = pmb.helpers.git.parse_channels_cfg(args) channel = read_config()["channel"]
channels_cfg = pmb.helpers.git.parse_channels_cfg(aports)
if channel in channels_cfg["channels"]: if channel in channels_cfg["channels"]:
return channels_cfg["channels"][channel] return channels_cfg["channels"][channel]
# Channel not in channels.cfg, try to be helpful # Channel not in channels.cfg, try to be helpful
branch = pmb.helpers.git.rev_parse(args.aports, branch = pmb.helpers.git.rev_parse(aports,
extra_args=["--abbrev-ref"]) extra_args=["--abbrev-ref"])
branches_official = pmb.helpers.git.get_branches_official("pmaports") branches_official = pmb.helpers.git.get_branches_official(aports)
branches_official = ", ".join(branches_official) branches_official = ", ".join(branches_official)
remote = pmb.helpers.git.get_upstream_remote("pmaports") remote = pmb.helpers.git.get_upstream_remote(aports)
logging.info("NOTE: fix the error by rebasing or cherry picking relevant" logging.info("NOTE: fix the error by rebasing or cherry picking relevant"
" commits from this branch onto a branch that is on a" " commits from this branch onto a branch that is on a"
f" supported channel: {branches_official}") f" supported channel: {branches_official}")
@ -150,7 +154,7 @@ def read_config_channel(args: PmbArgs):
def init(): def init():
if not os.path.exists(get_context().aports): if not os.path.exists(get_context().config.aports):
clone() clone()
read_config() read_config()
@ -163,14 +167,15 @@ def switch_to_channel_branch(args: PmbArgs, channel_new):
:returns: True if another branch was checked out, False otherwise :returns: True if another branch was checked out, False otherwise
""" """
# Check current pmaports branch channel # Check current pmaports branch channel
channel_current = read_config(args)["channel"] channel_current = read_config()["channel"]
if channel_current == channel_new: if channel_current == channel_new:
return False return False
aports = get_context().config.aports
# List current and new branches/channels # List current and new branches/channels
channels_cfg = pmb.helpers.git.parse_channels_cfg(args) channels_cfg = pmb.helpers.git.parse_channels_cfg(aports)
branch_new = channels_cfg["channels"][channel_new]["branch_pmaports"] branch_new = channels_cfg["channels"][channel_new]["branch_pmaports"]
branch_current = pmb.helpers.git.rev_parse(args.aports, branch_current = pmb.helpers.git.rev_parse(aports,
extra_args=["--abbrev-ref"]) extra_args=["--abbrev-ref"])
logging.info(f"Currently checked out branch '{branch_current}' of" logging.info(f"Currently checked out branch '{branch_current}' of"
f" pmaports.git is on channel '{channel_current}'.") f" pmaports.git is on channel '{channel_current}'.")
@ -183,25 +188,26 @@ def switch_to_channel_branch(args: PmbArgs, channel_new):
# Attempt to switch branch (git gives a nice error message, mentioning # Attempt to switch branch (git gives a nice error message, mentioning
# which files need to be committed/stashed, so just pass it through) # which files need to be committed/stashed, so just pass it through)
if pmb.helpers.run.user(["git", "checkout", branch_new], if pmb.helpers.run.user(["git", "checkout", branch_new],
args.aports, "interactive", check=False): aports, "interactive", check=False):
raise RuntimeError("Failed to switch branch. Go to your pmaports and" raise RuntimeError("Failed to switch branch. Go to your pmaports and"
" fix what git complained about, then try again: " " fix what git complained about, then try again: "
f"{args.aports}") f"{aports}")
# Verify pmaports.cfg on new branch # Verify pmaports.cfg on new branch
read_config(args) read_config()
return True return True
def install_githooks(args: PmbArgs): def install_githooks():
hooks_dir = os.path.join(args.aports, ".githooks") aports = get_context().config.aports
if not os.path.exists(hooks_dir): hooks_dir = aports / ".githooks"
if not hooks_dir.exists():
logging.info("No .githooks dir found") logging.info("No .githooks dir found")
return return
for h in os.listdir(hooks_dir): for h in os.listdir(hooks_dir):
src = os.path.join(hooks_dir, h) src = os.path.join(hooks_dir, h)
# Use git default hooks dir so users can ignore our hooks # Use git default hooks dir so users can ignore our hooks
# if they dislike them by setting "core.hooksPath" git config # if they dislike them by setting "core.hooksPath" git config
dst = os.path.join(args.aports, ".git", "hooks", h) dst = aports / ".git/hooks" / h
if pmb.helpers.run.user(["cp", src, dst], check=False): if pmb.helpers.run.user(["cp", src, dst], check=False):
logging.warning(f"WARNING: Copying git hook failed: {dst}") logging.warning(f"WARNING: Copying git hook failed: {dst}")

View file

@ -11,15 +11,15 @@ from typing import Optional
import pmb.config import pmb.config
import pmb.config.pmaports import pmb.config.pmaports
from pmb.core import Chroot from pmb.core import Chroot, get_context
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
def chroot_save_init(args: PmbArgs, suffix: Chroot): def chroot_save_init(suffix: Chroot):
"""Save the chroot initialization data in $WORK/workdir.cfg.""" """Save the chroot initialization data in $WORK/workdir.cfg."""
# Read existing cfg # Read existing cfg
cfg = configparser.ConfigParser() cfg = configparser.ConfigParser()
path = pmb.config.work / "workdir.cfg" path = get_context().config.work / "workdir.cfg"
if os.path.isfile(path): if os.path.isfile(path):
cfg.read(path) cfg.read(path)
@ -29,7 +29,7 @@ def chroot_save_init(args: PmbArgs, suffix: Chroot):
cfg[key] = {} cfg[key] = {}
# Update sections # Update sections
channel = pmb.config.pmaports.read_config(args)["channel"] channel = pmb.config.pmaports.read_config()["channel"]
cfg["chroot-channels"][str(suffix)] = channel cfg["chroot-channels"][str(suffix)] = channel
cfg["chroot-init-dates"][str(suffix)] = str(int(time.time())) cfg["chroot-init-dates"][str(suffix)] = str(int(time.time()))
@ -38,7 +38,7 @@ def chroot_save_init(args: PmbArgs, suffix: Chroot):
cfg.write(handle) cfg.write(handle)
def chroots_outdated(args: PmbArgs, chroot: Optional[Chroot]=None): def chroots_outdated(chroot: Optional[Chroot]=None):
"""Check if init dates from workdir.cfg indicate that any chroot is """Check if init dates from workdir.cfg indicate that any chroot is
outdated. outdated.
@ -48,7 +48,7 @@ def chroots_outdated(args: PmbArgs, chroot: Optional[Chroot]=None):
False otherwise False otherwise
""" """
# Skip if workdir.cfg doesn't exist # Skip if workdir.cfg doesn't exist
path = pmb.config.work / "workdir.cfg" path = get_context().config.work / "workdir.cfg"
if not os.path.exists(path): if not os.path.exists(path):
return False return False
@ -68,8 +68,8 @@ def chroots_outdated(args: PmbArgs, chroot: Optional[Chroot]=None):
return False return False
def chroot_check_channel(args: PmbArgs, chroot: Chroot): def chroot_check_channel(chroot: Chroot):
path = pmb.config.work / "workdir.cfg" path = get_context().config.work / "workdir.cfg"
msg_again = "Run 'pmbootstrap zap' to delete your chroots and try again." msg_again = "Run 'pmbootstrap zap' to delete your chroots and try again."
msg_unknown = ("Could not figure out on which release channel the" msg_unknown = ("Could not figure out on which release channel the"
f" '{chroot}' chroot is.") f" '{chroot}' chroot is.")
@ -82,7 +82,7 @@ def chroot_check_channel(args: PmbArgs, chroot: Chroot):
if key not in cfg or str(chroot) not in cfg[key]: if key not in cfg or str(chroot) not in cfg[key]:
raise RuntimeError(f"{msg_unknown} {msg_again}") raise RuntimeError(f"{msg_unknown} {msg_again}")
channel = pmb.config.pmaports.read_config(args)["channel"] channel = pmb.config.pmaports.read_config()["channel"]
channel_cfg = cfg[key][str(chroot)] channel_cfg = cfg[key][str(chroot)]
if channel != channel_cfg: if channel != channel_cfg:
raise RuntimeError(f"Chroot '{chroot}' was created for the" raise RuntimeError(f"Chroot '{chroot}' was created for the"
@ -98,7 +98,7 @@ def clean(args: PmbArgs):
False if config did not change False if config did not change
""" """
# Skip if workdir.cfg doesn't exist # Skip if workdir.cfg doesn't exist
path = pmb.config.work / "workdir.cfg" path = get_context().config.work / "workdir.cfg"
if not os.path.exists(path): if not os.path.exists(path):
return None return None

View file

@ -1,17 +1,6 @@
# Copyright 2024 Caleb Connolly # Copyright 2024 Caleb Connolly
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pmb.core.chroot import Chroot, ChrootType from .chroot import Chroot, ChrootType
from pmb.core.context import Context from .context import Context, get_context, set_context
from .config import Config
__context: Context
def get_context() -> Context:
"""Get immutable global runtime context."""
global __context
# We must defer this to first call to avoid
# circular imports.
if "__context" not in globals():
__context = Context()
return __context

View file

@ -6,6 +6,7 @@ import enum
from typing import Generator, Optional from typing import Generator, Optional
from pathlib import Path, PosixPath, PurePosixPath from pathlib import Path, PosixPath, PurePosixPath
import pmb.config import pmb.config
from .context import get_context
class ChrootType(enum.Enum): class ChrootType(enum.Enum):
ROOTFS = "rootfs" ROOTFS = "rootfs"
@ -69,7 +70,7 @@ class Chroot:
@property @property
def path(self) -> Path: def path(self) -> Path:
return Path(pmb.config.work, self.dirname) return Path(get_context().config.work, self.dirname)
@property @property
@ -182,4 +183,4 @@ class Chroot:
Glob all initialized chroot directories Glob all initialized chroot directories
""" """
for pattern in Chroot.iter_patterns(): for pattern in Chroot.iter_patterns():
yield from Path(pmb.config.work).glob(pattern) yield from Path(get_context().config.work).glob(pattern)

View file

@ -3,9 +3,9 @@
"""Global runtime context""" """Global runtime context"""
from typing import Optional from typing import List, Optional
import pmb.config
from pathlib import Path from pathlib import Path
from pmb.types import Config
class Context(): class Context():
@ -17,14 +17,62 @@ class Context():
# The architecture of the selected device # The architecture of the selected device
device_arch: Optional[str] device_arch: Optional[str]
offline: bool offline: bool
aports: Path
def __init__(self): # Never build packages
sdnfivnsifdvsbdf: bool
# The pmbootstrap subcommand
command: str
## FIXME: build options, should not be here ##
# disable cross compilation and use QEMU
cross: bool
no_depends: bool
ignore_depends: bool
ccache: bool
go_mod_cache: bool
config: Config
def __init__(self, config: Config):
self.details_to_stdout = False self.details_to_stdout = False
self.command_timeout = 0 self.command_timeout = 0
self.sudo_timer = False self.sudo_timer = False
self.log = pmb.config.work / "log.txt" self.log = config.work / "log.txt"
self.quiet = False self.quiet = False
self.device_arch = None self.device_arch = None
self.offline = False self.offline = False
self.aports = pmb.config.work / "cache_git" / "pmaports" self.config = config
self.sdnfivnsifdvsbdf = False
self.command = ""
self.cross = False
self.no_depends = False
self.ignore_depends = False
self.ccache = False
self.go_mod_cache = False
__context: Context
def get_context(allow_failure: bool=False) -> Context:
"""Get immutable global runtime context."""
global __context
# We must defer this to first call to avoid
# circular imports.
if "__context" not in globals():
if allow_failure:
return None
raise RuntimeError("Context not loaded yet")
return __context
def set_context(context: Context):
"""Set global runtime context."""
global __context
if "__context" in globals():
raise RuntimeError("Context already loaded")
__context = context

43
pmb/core/crosstool.py Normal file
View file

@ -0,0 +1,43 @@
# Copyright 2024 Caleb Connolly
# SPDX-License-Identifier: GPL-3.0-or-later
from argparse import Namespace
import enum
from pathlib import Path
from typing import Dict, List, Optional, Tuple, TypedDict, Union
from pmb.core.chroot import ChrootType
from pmb.types import PathString
class CrossToolTarget(enum.Enum):
BUILDROOT = 0
ROOTFS = 1
class CrossTool():
__target: CrossToolTarget
__package: str
__paths: List[Path]
def __init__(self, target: CrossToolTarget, package: str, paths: List[PathString]):
self.__target = target
self.__package = package
self.__paths = list(map(lambda p: Path(p) if isinstance(p, str) else p, paths))
def __repr__(self) -> str:
return f"CrossTool({self.__target}, {self.__package}, {self.__paths})"
@property
def package(self) -> str:
return self.__package
@property
def paths(self) -> List[Path]:
return self.__paths
def should_install(self, target: ChrootType) -> bool:
if target == ChrootType.BUILDROOT and self.__target == CrossToolTarget.BUILDROOT:
return True
if target == ChrootType.ROOTFS or target == ChrootType.INSTALLER and self.__target == CrossToolTarget.ROOTFS:
return True
return False

View file

@ -1,7 +1,7 @@
from pmb.helpers import logging from pmb.helpers import logging
import os import os
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.run import pmb.helpers.run
import pmb.helpers.frontend import pmb.helpers.frontend
import pmb.chroot.initfs import pmb.chroot.initfs

View file

@ -6,7 +6,7 @@ from pathlib import Path
import pmb.build import pmb.build
import pmb.chroot.apk import pmb.chroot.apk
import pmb.config import pmb.config
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.flasher import pmb.flasher
import pmb.helpers.file import pmb.helpers.file
from pmb.core import Chroot, ChrootType from pmb.core import Chroot, ChrootType
@ -23,7 +23,7 @@ def odin(args: PmbArgs, flavor, folder: Path):
# Backwards compatibility with old mkinitfs (pma#660) # Backwards compatibility with old mkinitfs (pma#660)
suffix_flavor = f"-{flavor}" suffix_flavor = f"-{flavor}"
pmaports_cfg = pmb.config.pmaports.read_config(args) pmaports_cfg = pmb.config.pmaports.read_config()
if pmaports_cfg.get("supported_mkinitfs_without_flavors", False): if pmaports_cfg.get("supported_mkinitfs_without_flavors", False):
suffix_flavor = "" suffix_flavor = ""
@ -44,7 +44,7 @@ def odin(args: PmbArgs, flavor, folder: Path):
# Temporary folder # Temporary folder
temp_folder = "/tmp/odin-flashable-tar" temp_folder = "/tmp/odin-flashable-tar"
if (Chroot.native() / temp_folder).exists(): if (Chroot.native() / temp_folder).exists():
pmb.chroot.root(args, ["rm", "-rf", temp_folder]) pmb.chroot.root(["rm", "-rf", temp_folder])
# Odin flashable tar generation script # Odin flashable tar generation script
# (because redirecting stdin/stdout is not allowed # (because redirecting stdin/stdout is not allowed
@ -86,15 +86,15 @@ def odin(args: PmbArgs, flavor, folder: Path):
["rm", "/tmp/_odin.sh"] ["rm", "/tmp/_odin.sh"]
] ]
for command in commands: for command in commands:
pmb.chroot.root(args, command, suffix) pmb.chroot.root(command, suffix)
# Move Odin flashable tar to native chroot and cleanup temp folder # Move Odin flashable tar to native chroot and cleanup temp folder
pmb.chroot.user(args, ["mkdir", "-p", "/home/pmos/rootfs"]) pmb.chroot.user(["mkdir", "-p", "/home/pmos/rootfs"])
pmb.chroot.root(args, ["mv", f"/mnt/rootfs_{args.device}{temp_folder}" pmb.chroot.root(["mv", f"/mnt/rootfs_{args.device}{temp_folder}"
f"/{odin_device_tar_md5}", "/home/pmos/rootfs/"]), f"/{odin_device_tar_md5}", "/home/pmos/rootfs/"]),
pmb.chroot.root(args, ["chown", "pmos:pmos", pmb.chroot.root(["chown", "pmos:pmos",
f"/home/pmos/rootfs/{odin_device_tar_md5}"]) f"/home/pmos/rootfs/{odin_device_tar_md5}"])
pmb.chroot.root(args, ["rmdir", temp_folder], suffix) pmb.chroot.root(["rmdir", temp_folder], suffix)
# Create the symlink # Create the symlink
file = Chroot.native() / "home/pmos/rootfs" / odin_device_tar_md5 file = Chroot.native() / "home/pmos/rootfs" / odin_device_tar_md5

View file

@ -8,7 +8,7 @@ import pmb.build
import pmb.chroot.apk import pmb.chroot.apk
import pmb.config import pmb.config
import pmb.config.pmaports import pmb.config.pmaports
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.flasher import pmb.flasher
import pmb.helpers.file import pmb.helpers.file
from pmb.core import Chroot, ChrootType from pmb.core import Chroot, ChrootType
@ -21,7 +21,7 @@ def symlinks(args: PmbArgs, flavor, folder: Path):
# Backwards compatibility with old mkinitfs (pma#660) # Backwards compatibility with old mkinitfs (pma#660)
suffix = f"-{flavor}" suffix = f"-{flavor}"
pmaports_cfg = pmb.config.pmaports.read_config(args) pmaports_cfg = pmb.config.pmaports.read_config()
if pmaports_cfg.get("supported_mkinitfs_without_flavors", False): if pmaports_cfg.get("supported_mkinitfs_without_flavors", False):
suffix = "" suffix = ""

View file

@ -3,7 +3,7 @@
from pmb.helpers import logging from pmb.helpers import logging
import pmb.config import pmb.config
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.flasher import pmb.flasher
import pmb.install import pmb.install
import pmb.chroot.apk import pmb.chroot.apk
@ -116,7 +116,7 @@ def flash_lk2nd(args: PmbArgs):
# manually since supporting the codepath with heimdall requires more effort. # manually since supporting the codepath with heimdall requires more effort.
pmb.flasher.init(args) pmb.flasher.init(args)
logging.info("(native) checking current fastboot product") logging.info("(native) checking current fastboot product")
output = pmb.chroot.root(args, ["fastboot", "getvar", "product"], output = pmb.chroot.root(["fastboot", "getvar", "product"],
output="interactive", output_return=True) output="interactive", output_return=True)
# Variable "product" is e.g. "LK2ND_MSM8974" or "lk2nd-msm8226" depending # Variable "product" is e.g. "LK2ND_MSM8974" or "lk2nd-msm8226" depending
# on the lk2nd version. # on the lk2nd version.
@ -126,7 +126,7 @@ def flash_lk2nd(args: PmbArgs):
# Get the lk2nd package (which is a dependency of the device package) # Get the lk2nd package (which is a dependency of the device package)
device_pkg = f"device-{args.device}" device_pkg = f"device-{args.device}"
apkbuild = pmb.helpers.pmaports.get(args, device_pkg) apkbuild = pmb.helpers.pmaports.get(device_pkg)
lk2nd_pkg = None lk2nd_pkg = None
for dep in apkbuild["depends"]: for dep in apkbuild["depends"]:
if dep.startswith("lk2nd"): if dep.startswith("lk2nd"):
@ -137,7 +137,7 @@ def flash_lk2nd(args: PmbArgs):
raise RuntimeError(f"{device_pkg} does not depend on any lk2nd package") raise RuntimeError(f"{device_pkg} does not depend on any lk2nd package")
suffix = Chroot(ChrootType.ROOTFS, args.device) suffix = Chroot(ChrootType.ROOTFS, args.device)
pmb.chroot.apk.install(args, [lk2nd_pkg], suffix) pmb.chroot.apk.install([lk2nd_pkg], suffix)
logging.info("(native) flash lk2nd image") logging.info("(native) flash lk2nd image")
pmb.flasher.run(args, "flash_lk2nd") pmb.flasher.run(args, "flash_lk2nd")

View file

@ -3,7 +3,7 @@
import pmb.chroot.apk import pmb.chroot.apk
import pmb.config import pmb.config
import pmb.config.pmaports import pmb.config.pmaports
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.mount import pmb.helpers.mount
from pmb.helpers.mount import mount_device_rootfs from pmb.helpers.mount import mount_device_rootfs
from pmb.core import Chroot, ChrootType from pmb.core import Chroot, ChrootType
@ -28,19 +28,19 @@ def install_depends(args: PmbArgs):
# Depends for some flash methods may be different for various pmaports # Depends for some flash methods may be different for various pmaports
# branches, so read them from pmaports.cfg. # branches, so read them from pmaports.cfg.
if method == "fastboot": if method == "fastboot":
pmaports_cfg = pmb.config.pmaports.read_config(args) pmaports_cfg = pmb.config.pmaports.read_config()
depends = pmaports_cfg.get("supported_fastboot_depends", depends = pmaports_cfg.get("supported_fastboot_depends",
"android-tools,avbtool").split(",") "android-tools,avbtool").split(",")
elif method == "heimdall-bootimg": elif method == "heimdall-bootimg":
pmaports_cfg = pmb.config.pmaports.read_config(args) pmaports_cfg = pmb.config.pmaports.read_config()
depends = pmaports_cfg.get("supported_heimdall_depends", depends = pmaports_cfg.get("supported_heimdall_depends",
"heimdall,avbtool").split(",") "heimdall,avbtool").split(",")
elif method == "mtkclient": elif method == "mtkclient":
pmaports_cfg = pmb.config.pmaports.read_config(args) pmaports_cfg = pmb.config.pmaports.read_config()
depends = pmaports_cfg.get("supported_mtkclient_depends", depends = pmaports_cfg.get("supported_mtkclient_depends",
"mtkclient,android-tools").split(",") "mtkclient,android-tools").split(",")
pmb.chroot.apk.install(args, depends, Chroot.native()) pmb.chroot.apk.install(depends, Chroot.native())
def init(args: PmbArgs): def init(args: PmbArgs):

View file

@ -1,6 +1,6 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.flasher import pmb.flasher
import pmb.chroot.initfs import pmb.chroot.initfs
@ -80,4 +80,4 @@ def run(args: PmbArgs, action, flavor=None):
# Remove empty strings # Remove empty strings
command = [x for x in command if x != ''] command = [x for x in command if x != '']
# Run the action # Run the action
pmb.chroot.root(args, command, output="interactive") pmb.chroot.root(command, output="interactive")

View file

@ -2,7 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from typing import Optional from typing import Optional
import pmb.config.pmaports import pmb.config.pmaports
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
def variables(args: PmbArgs, flavor, method): def variables(args: PmbArgs, flavor, method):
@ -99,7 +99,7 @@ def variables(args: PmbArgs, flavor, method):
} }
# Backwards compatibility with old mkinitfs (pma#660) # Backwards compatibility with old mkinitfs (pma#660)
pmaports_cfg = pmb.config.pmaports.read_config(args) pmaports_cfg = pmb.config.pmaports.read_config()
if pmaports_cfg.get("supported_mkinitfs_without_flavors", False): if pmaports_cfg.get("supported_mkinitfs_without_flavors", False):
vars["$FLAVOR"] = "" vars["$FLAVOR"] = ""
else: else:

View file

@ -6,12 +6,12 @@ from typing import List, Sequence
import pmb.chroot.run import pmb.chroot.run
import pmb.config.pmaports import pmb.config.pmaports
from pmb.core.types import PathString, PmbArgs from pmb.types import PathString, PmbArgs
import pmb.helpers.cli import pmb.helpers.cli
import pmb.helpers.run import pmb.helpers.run
import pmb.helpers.run_core import pmb.helpers.run_core
import pmb.parse.version import pmb.parse.version
from pmb.core import get_context
def _prepare_fifo(): def _prepare_fifo():
"""Prepare the progress fifo for reading / writing. """Prepare the progress fifo for reading / writing.
@ -24,8 +24,8 @@ def _prepare_fifo():
path of the fifo as needed by cat to read from it (always path of the fifo as needed by cat to read from it (always
relative to the host) relative to the host)
""" """
pmb.helpers.run.root(["mkdir", "-p", pmb.config.work / "tmp"]) pmb.helpers.run.root(["mkdir", "-p", get_context().config.work / "tmp"])
fifo = fifo_outside = pmb.config.work / "tmp/apk_progress_fifo" fifo = fifo_outside = get_context().config.work / "tmp/apk_progress_fifo"
if os.path.exists(fifo_outside): if os.path.exists(fifo_outside):
pmb.helpers.run.root(["rm", "-f", fifo_outside]) pmb.helpers.run.root(["rm", "-f", fifo_outside])
pmb.helpers.run.root(["mkfifo", fifo_outside]) pmb.helpers.run.root(["mkfifo", fifo_outside])
@ -88,7 +88,7 @@ def apk_with_progress(command: Sequence[PathString]):
log_msg) log_msg)
def check_outdated(args: PmbArgs, version_installed, action_msg): def check_outdated(version_installed, action_msg):
"""Check if the provided alpine version is outdated. """Check if the provided alpine version is outdated.
This depends on the alpine mirrordir (edge, v3.12, ...) related to currently checked out This depends on the alpine mirrordir (edge, v3.12, ...) related to currently checked out
@ -99,7 +99,7 @@ def check_outdated(args: PmbArgs, version_installed, action_msg):
this this
:raises: RuntimeError if the version is outdated :raises: RuntimeError if the version is outdated
""" """
channel_cfg = pmb.config.pmaports.read_config_channel(args) channel_cfg = pmb.config.pmaports.read_config_channel()
mirrordir_alpine = channel_cfg["mirrordir_alpine"] mirrordir_alpine = channel_cfg["mirrordir_alpine"]
version_min = pmb.config.apk_tools_min_version[mirrordir_alpine] version_min = pmb.config.apk_tools_min_version[mirrordir_alpine]

View file

@ -8,7 +8,7 @@ import re
import urllib.parse import urllib.parse
from typing import Dict, Optional from typing import Dict, Optional
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.file import pmb.helpers.file
import pmb.helpers.http import pmb.helpers.http
import pmb.helpers.pmaports import pmb.helpers.pmaports
@ -263,7 +263,7 @@ def upgrade(args: PmbArgs, pkgname, git=True, stable=True) -> None:
# Initialize request headers # Initialize request headers
init_req_headers() init_req_headers()
package = pmb.helpers.pmaports.get(args, pkgname) package = pmb.helpers.pmaports.get(pkgname)
# Run the correct function # Run the correct function
if "_git" in package["pkgver"]: if "_git" in package["pkgver"]:
if git: if git:

View file

@ -4,7 +4,8 @@ import copy
import os import os
from pathlib import Path from pathlib import Path
import pmb.config import pmb.config
from pmb.core.types import PmbArgs from pmb.core.context import Context
from pmb.types import PmbArgs
import pmb.helpers.git import pmb.helpers.git
import pmb.helpers.args import pmb.helpers.args
@ -32,7 +33,7 @@ import pmb.helpers.args
Examples: Examples:
args.aports ("$WORK/cache_git/pmaports", override with --aports) args.aports ("$WORK/cache_git/pmaports", override with --aports)
args.device ("samsung-i9100", "qemu-amd64" etc.) args.device ("samsung-i9100", "qemu-amd64" etc.)
pmb.config.work ("/home/user/.local/var/pmbootstrap", override with --work) get_context().config.work ("/home/user/.local/var/pmbootstrap", override with --work)
3. Parsed configs 3. Parsed configs
Similar to the cache above, specific config files get parsed and added Similar to the cache above, specific config files get parsed and added
@ -46,28 +47,6 @@ import pmb.helpers.args
""" """
def fix_mirrors_postmarketos(args: PmbArgs):
"""Fix args.mirrors_postmarketos when it is supposed to be empty or the default value.
In pmb/parse/arguments.py, we set the -mp/--mirror-pmOS argument to
action="append" and start off with an empty list. That way, users can
specify multiple custom mirrors by specifying -mp multiple times on the
command line. Here we fix the default and no mirrors case.
NOTE: we don't use nargs="+", because it does not play nicely with
subparsers: <https://bugs.python.org/issue9338>
"""
# -mp not specified: use default mirrors
if not args.mirrors_postmarketos:
cfg = pmb.config.load(args)
args.mirrors_postmarketos = \
cfg["pmbootstrap"]["mirrors_postmarketos"].split(",")
# -mp="": use no postmarketOS mirrors (build everything locally)
if args.mirrors_postmarketos == [""]:
args.mirrors_postmarketos = []
def check_pmaports_path(args: PmbArgs): def check_pmaports_path(args: PmbArgs):
"""Make sure that args.aports exists when it was overridden by --aports. """Make sure that args.aports exists when it was overridden by --aports.
@ -79,28 +58,28 @@ def check_pmaports_path(args: PmbArgs):
f" not exist: {args.aports}") f" not exist: {args.aports}")
def replace_placeholders(args: PmbArgs): # def replace_placeholders(args: PmbArgs):
"""Replace $WORK and ~ (for path variables) in variables from any config. # """Replace $WORK and ~ (for path variables) in variables from any config.
(user's config file, default config settings or config parameters specified on commandline) # (user's config file, default config settings or config parameters specified on commandline)
""" # """
# Replace $WORK # # Replace $WORK
for key, value in pmb.config.defaults.items(): # for key, value in pmb.config.defaults.items():
if key not in args: # if key not in args:
continue # continue
old = getattr(args, key) # old = getattr(args, key)
if isinstance(old, str): # if isinstance(old, str):
setattr(args, key, old.replace("$WORK", str(pmb.config.work))) # setattr(args, key, old.replace("$WORK", str(get_context().config.work)))
# Replace ~ (path variables only) # # Replace ~ (path variables only)
for key in ["aports", "config", "work"]: # for key in ["aports", "config", "work"]:
if key in args: # if key in args:
setattr(args, key, Path(getattr(args, key)).expanduser()) # setattr(args, key, Path(getattr(args, key)).expanduser())
def add_deviceinfo(args: PmbArgs): def add_deviceinfo(args: PmbArgs):
"""Add and verify the deviceinfo (only after initialization)""" """Add and verify the deviceinfo (only after initialization)"""
setattr(args, "deviceinfo", pmb.parse.deviceinfo(args)) setattr(args, "deviceinfo", pmb.parse.deviceinfo())
arch = args.deviceinfo["arch"] arch = args.deviceinfo["arch"]
if (arch != pmb.config.arch_native and if (arch != pmb.config.arch_native and
arch not in pmb.config.build_device_architectures): arch not in pmb.config.build_device_architectures):
@ -111,20 +90,28 @@ def add_deviceinfo(args: PmbArgs):
def init(args: PmbArgs) -> PmbArgs: def init(args: PmbArgs) -> PmbArgs:
# Basic initialization # Basic initialization
fix_mirrors_postmarketos(args) config = pmb.config.load(args)
pmb.config.merge_with_args(args) # pmb.config.merge_with_args(args)
replace_placeholders(args) # replace_placeholders(args)
# Configure runtime context # Configure runtime context
context = pmb.core.get_context() context = Context(config)
context.command_timeout = args.timeout context.command_timeout = args.timeout
context.details_to_stdout = args.details_to_stdout context.details_to_stdout = args.details_to_stdout
context.sudo_timer = args.sudo_timer
context.quiet = args.quiet context.quiet = args.quiet
context.offline = args.offline context.offline = args.offline
context.command = args.action
context.cross = args.cross
if args.mirrors_postmarketos:
context.config.mirrors_postmarketos = args.mirrors_postmarketos
if args.mirror_alpine:
context.config.mirror_alpine = args.mirror_alpine
if args.aports: if args.aports:
print(f"Using pmaports from: {args.aports}") print(f"Using pmaports from: {args.aports}")
context.aports = args.aports context.config.aports = args.aports
# Initialize context
pmb.core.set_context(context)
# Initialize logs (we could raise errors below) # Initialize logs (we could raise errors below)
pmb.helpers.logging.init(args) pmb.helpers.logging.init(args)
@ -133,19 +120,22 @@ def init(args: PmbArgs) -> PmbArgs:
check_pmaports_path(args) check_pmaports_path(args)
if args.action not in ["init", "checksum", "config", "bootimg_analyze", "log", if args.action not in ["init", "checksum", "config", "bootimg_analyze", "log",
"pull", "shutdown", "zap"]: "pull", "shutdown", "zap"]:
pmb.config.pmaports.read_config(args) pmb.config.pmaports.read_config()
add_deviceinfo(args) add_deviceinfo(args)
pmb.helpers.git.parse_channels_cfg() pmb.helpers.git.parse_channels_cfg(config.aports)
context.device_arch = args.deviceinfo["arch"] context.device_arch = args.deviceinfo["arch"]
# Remove attributes from args so they don't get used by mistake # Remove attributes from args so they don't get used by mistake
delattr(args, "timeout") delattr(args, "timeout")
delattr(args, "details_to_stdout") delattr(args, "details_to_stdout")
delattr(args, "sudo_timer")
delattr(args, "log") delattr(args, "log")
delattr(args, "quiet") delattr(args, "quiet")
delattr(args, "offline") delattr(args, "offline")
delattr(args, "aports") delattr(args, "aports")
delattr(args, "mirrors_postmarketos")
delattr(args, "mirror_alpine")
# args.work is deprecated!
delattr(args, "work")
return args return args

View file

@ -8,7 +8,7 @@ import readline
import sys import sys
import pmb.config import pmb.config
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
from pmb.core import get_context from pmb.core import get_context

View file

@ -3,18 +3,17 @@
import os import os
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
from pmb.core.types import PmbArgs from pmb.core import get_context
import pmb.parse
def find_path(args: PmbArgs, codename: str, file='') -> Optional[Path]: def find_path(codename: str, file='') -> Optional[Path]:
"""Find path to device APKBUILD under `device/*/device-`. """Find path to device APKBUILD under `device/*/device-`.
:param codename: device codename :param codename: device codename
:param file: file to look for (e.g. APKBUILD or deviceinfo), may be empty :param file: file to look for (e.g. APKBUILD or deviceinfo), may be empty
:returns: path to APKBUILD :returns: path to APKBUILD
""" """
g = list((args.aports / "device").glob(f"*/device-{codename}/{file}")) g = list((get_context().config.aports / "device").glob(f"*/device-{codename}/{file}"))
if not g: if not g:
return None return None
@ -25,7 +24,7 @@ def find_path(args: PmbArgs, codename: str, file='') -> Optional[Path]:
return g[0] return g[0]
def list_codenames(args: PmbArgs, vendor=None, archived=True): def list_codenames(aports: Path, vendor=None, archived=True):
"""Get all devices, for which aports are available. """Get all devices, for which aports are available.
:param vendor: vendor name to choose devices from, or None for all vendors :param vendor: vendor name to choose devices from, or None for all vendors
@ -33,7 +32,7 @@ def list_codenames(args: PmbArgs, vendor=None, archived=True):
:returns: ["first-device", "second-device", ...] :returns: ["first-device", "second-device", ...]
""" """
ret = [] ret = []
for path in args.aports.glob("device/*/device-*"): for path in aports.glob("device/*/device-*"):
if not archived and 'archived' in path.parts: if not archived and 'archived' in path.parts:
continue continue
device = os.path.basename(path).split("-", 1)[1] device = os.path.basename(path).split("-", 1)[1]
@ -42,30 +41,13 @@ def list_codenames(args: PmbArgs, vendor=None, archived=True):
return ret return ret
def list_vendors(args: PmbArgs): def list_vendors(aports: Path):
"""Get all device vendors, for which aports are available. """Get all device vendors, for which aports are available.
:returns: {"vendor1", "vendor2", ...} :returns: {"vendor1", "vendor2", ...}
""" """
ret = set() ret = set()
for path in (args.aports / "device").glob("*/device-*"): for path in (aports / "device").glob("*/device-*"):
vendor = path.name.split("-", 2)[1] vendor = path.name.split("-", 2)[1]
ret.add(vendor) ret.add(vendor)
return ret return ret
def list_apkbuilds(args: PmbArgs):
""":returns: { "first-device": {"pkgname": ..., "pkgver": ...}, ... }"""
ret = {}
for device in list_codenames(args):
apkbuild_path = next(args.aports.glob(f"device/*/device-{device}/APKBUILD"))
ret[device] = pmb.parse.apkbuild(apkbuild_path)
return ret
def list_deviceinfos(args: PmbArgs):
""":returns: { "first-device": {"name": ..., "screen_width": ...}, ... }"""
ret = {}
for device in list_codenames(args):
ret[device] = pmb.parse.deviceinfo(args, device)
return ret

View file

@ -5,7 +5,7 @@ import os
from pathlib import Path from pathlib import Path
import time import time
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.run import pmb.helpers.run
import pmb.helpers.pmaports import pmb.helpers.pmaports
@ -30,7 +30,7 @@ def replace_apkbuild(args: PmbArgs, pkgname, key, new, in_quotes=False):
:param in_quotes: expect the value to be in quotation marks ("") :param in_quotes: expect the value to be in quotation marks ("")
""" """
# Read old value # Read old value
path = pmb.helpers.pmaports.find(args, pkgname) / "APKBUILD" path = pmb.helpers.pmaports.find(pkgname) / "APKBUILD"
apkbuild = pmb.parse.apkbuild(path) apkbuild = pmb.parse.apkbuild(path)
old = apkbuild[key] old = apkbuild[key]

View file

@ -16,7 +16,7 @@ import pmb.chroot.initfs
import pmb.chroot.other import pmb.chroot.other
import pmb.ci import pmb.ci
import pmb.config import pmb.config
from pmb.core.types import PathString, PmbArgs from pmb.types import Config, PathString, PmbArgs
import pmb.export import pmb.export
import pmb.flasher import pmb.flasher
import pmb.helpers.aportupgrade import pmb.helpers.aportupgrade
@ -37,7 +37,7 @@ import pmb.netboot
import pmb.parse import pmb.parse
import pmb.qemu import pmb.qemu
import pmb.sideload import pmb.sideload
from pmb.core import ChrootType, Chroot from pmb.core import ChrootType, Chroot, get_context
def _parse_flavor(args: PmbArgs, autoinstall=True): def _parse_flavor(args: PmbArgs, autoinstall=True):
@ -62,7 +62,7 @@ def _parse_flavor(args: PmbArgs, autoinstall=True):
def _parse_suffix(args: PmbArgs) -> Chroot: def _parse_suffix(args: PmbArgs) -> Chroot:
if "rootfs" in args and args.rootfs: if "rootfs" in args and args.rootfs:
return Chroot(ChrootType.ROOTFS, args.device) return Chroot(ChrootType.ROOTFS, get_context().config.device)
elif args.buildroot: elif args.buildroot:
if args.buildroot == "device": if args.buildroot == "device":
return Chroot.buildroot(args.deviceinfo["arch"]) return Chroot.buildroot(args.deviceinfo["arch"])
@ -122,10 +122,11 @@ def build(args: PmbArgs):
pmb.helpers.repo_bootstrap.require_bootstrap(args, arch_package, pmb.helpers.repo_bootstrap.require_bootstrap(args, arch_package,
f"build {package} for {arch_package}") f"build {package} for {arch_package}")
context = get_context()
# Build all packages # Build all packages
for package in args.packages: for package in args.packages:
arch_package = args.arch or pmb.build.autodetect.arch(args, package) arch_package = args.arch or pmb.build.autodetect.arch(args, package)
if not pmb.build.package(args, package, arch_package, force, if not pmb.build.package(context, package, arch_package, force,
args.strict, src=src): args.strict, src=src):
logging.info("NOTE: Package '" + package + "' is up to date. Use" logging.info("NOTE: Package '" + package + "' is up to date. Use"
" 'pmbootstrap build " + package + " --force'" " 'pmbootstrap build " + package + " --force'"
@ -169,11 +170,11 @@ def chroot(args: PmbArgs):
raise RuntimeError("--xauth is only supported for native chroot.") raise RuntimeError("--xauth is only supported for native chroot.")
# apk: check minimum version, install packages # apk: check minimum version, install packages
pmb.chroot.apk.check_min_version(args, suffix) pmb.chroot.apk.check_min_version(suffix)
if args.add: if args.add:
pmb.chroot.apk.install(args, args.add.split(","), suffix) pmb.chroot.apk.install(args.add.split(","), suffix)
pmb.chroot.init(args, suffix) pmb.chroot.init(suffix)
# Xauthority # Xauthority
env = {} env = {}
@ -198,11 +199,11 @@ def chroot(args: PmbArgs):
if args.user: if args.user:
logging.info(f"({suffix}) % su pmos -c '" + logging.info(f"({suffix}) % su pmos -c '" +
" ".join(args.command) + "'") " ".join(args.command) + "'")
pmb.chroot.user(args, args.command, suffix, output=args.output, pmb.chroot.user(args.command, suffix, output=args.output,
env=env) env=env)
else: else:
logging.info(f"({suffix}) % " + " ".join(args.command)) logging.info(f"({suffix}) % " + " ".join(args.command))
pmb.chroot.root(args, args.command, suffix, output=args.output, pmb.chroot.root(args.command, suffix, output=args.output,
env=env) env=env)
@ -212,24 +213,27 @@ def config(args: PmbArgs):
logging.info("NOTE: Valid config keys: " + ", ".join(keys)) logging.info("NOTE: Valid config keys: " + ", ".join(keys))
raise RuntimeError("Invalid config key: " + args.name) raise RuntimeError("Invalid config key: " + args.name)
cfg = pmb.config.load(args) config = pmb.config.load(args)
if args.reset: if args.reset:
if args.name is None: if args.name is None:
raise RuntimeError("config --reset requires a name to be given.") raise RuntimeError("config --reset requires a name to be given.")
value = pmb.config.defaults[args.name] def_value = getattr(Config(), args.name)
cfg["pmbootstrap"][args.name] = value setattr(config, args.name, def_value)
logging.info(f"Config changed to default: {args.name}='{value}'") logging.info(f"Config changed to default: {args.name}='{value}'")
pmb.config.save(args, cfg) pmb.config.save(args.config, config)
elif args.value is not None: elif args.value is not None:
cfg["pmbootstrap"][args.name] = args.value setattr(config, args.name, args.value)
pmb.config.sanity_checks(args, cfg, False) pmb.config.sanity_checks(args, config, False)
logging.info("Config changed: " + args.name + "='" + args.value + "'") logging.info("Config changed: " + args.name + "='" + args.value + "'")
pmb.config.save(args, cfg) pmb.config.save(args.config, config)
elif args.name: elif args.name:
value = cfg["pmbootstrap"].get(args.name, "") if hasattr(config, args.name):
value = getattr(config, args.name)
else:
value = ""
print(value) print(value)
else: else:
cfg.write(sys.stdout) print(open(args.config).read())
# Don't write the "Done" message # Don't write the "Done" message
pmb.helpers.logging.disable() pmb.helpers.logging.disable()
@ -240,7 +244,7 @@ def repo_bootstrap(args: PmbArgs):
def repo_missing(args: PmbArgs): def repo_missing(args: PmbArgs):
missing = pmb.helpers.repo_missing.generate(args, args.arch, args.overview, missing = pmb.helpers.repo_missing.generate(args.arch, args.overview,
args.package, args.built) args.package, args.built)
print(json.dumps(missing, indent=4)) print(json.dumps(missing, indent=4))
@ -344,7 +348,7 @@ def install(args: PmbArgs):
args.build_pkgs_on_install = False args.build_pkgs_on_install = False
# Safest way to avoid installing local packages is having none # Safest way to avoid installing local packages is having none
if (pmb.config.work / "packages").glob("*"): if (get_context().config.work / "packages").glob("*"):
raise ValueError("--no-local-pkgs specified, but locally built" raise ValueError("--no-local-pkgs specified, but locally built"
" packages found. Consider 'pmbootstrap zap -p'" " packages found. Consider 'pmbootstrap zap -p'"
" to delete them.") " to delete them.")
@ -365,7 +369,7 @@ def export(args: PmbArgs):
def update(args: PmbArgs): def update(args: PmbArgs):
existing_only = not args.non_existing existing_only = not args.non_existing
if not pmb.helpers.repo.update(args, args.arch, True, existing_only): if not pmb.helpers.repo.update(args.arch, True, existing_only):
logging.info("No APKINDEX files exist, so none have been updated." logging.info("No APKINDEX files exist, so none have been updated."
" The pmbootstrap command downloads the APKINDEX files on" " The pmbootstrap command downloads the APKINDEX files on"
" demand.") " demand.")
@ -430,7 +434,7 @@ def kconfig(args: PmbArgs):
else: else:
packages = [args.package] packages = [args.package]
if not args.package: if not args.package:
for pkg in pmb.helpers.pmaports.get_list(args): for pkg in pmb.helpers.pmaports.get_list():
if pkg.startswith("linux-"): if pkg.startswith("linux-"):
packages.append(pkg.split("linux-")[1]) packages.append(pkg.split("linux-")[1])
@ -442,7 +446,7 @@ def kconfig(args: PmbArgs):
if not args.force: if not args.force:
pkgname = package if package.startswith("linux-") \ pkgname = package if package.startswith("linux-") \
else "linux-" + package else "linux-" + package
aport = pmb.helpers.pmaports.find(args, pkgname) aport = pmb.helpers.pmaports.find(pkgname)
apkbuild = pmb.parse.apkbuild(aport) apkbuild = pmb.parse.apkbuild(aport)
if "!pmb:kconfigcheck" in apkbuild["options"]: if "!pmb:kconfigcheck" in apkbuild["options"]:
skipped += 1 skipped += 1
@ -472,13 +476,13 @@ def deviceinfo_parse(args: PmbArgs):
# Default to all devices # Default to all devices
devices = args.devices devices = args.devices
if not devices: if not devices:
devices = pmb.helpers.devices.list_codenames(args) devices = pmb.helpers.devices.list_codenames(get_context().config.aports)
# Iterate over all devices # Iterate over all devices
kernel = args.deviceinfo_parse_kernel kernel = args.deviceinfo_parse_kernel
for device in devices: for device in devices:
print(f"{device}, with kernel={kernel}:") print(f"{device}, with kernel={kernel}:")
print(json.dumps(pmb.parse.deviceinfo(args, device, kernel), indent=4, print(json.dumps(pmb.parse.deviceinfo(device, kernel), indent=4,
sort_keys=True)) sort_keys=True))
@ -486,12 +490,12 @@ def apkbuild_parse(args: PmbArgs):
# Default to all packages # Default to all packages
packages: Sequence[str] = args.packages packages: Sequence[str] = args.packages
if not packages: if not packages:
packages = pmb.helpers.pmaports.get_list(args) packages = pmb.helpers.pmaports.get_list()
# Iterate over all packages # Iterate over all packages
for package in packages: for package in packages:
print(package + ":") print(package + ":")
aport = pmb.helpers.pmaports.find(args, package) aport = pmb.helpers.pmaports.find(package)
print(json.dumps(pmb.parse.apkbuild(aport), indent=4, print(json.dumps(pmb.parse.apkbuild(aport), indent=4,
sort_keys=True)) sort_keys=True))
@ -512,7 +516,7 @@ def pkgrel_bump(args: PmbArgs):
else: else:
# Each package must exist # Each package must exist
for package in args.packages: for package in args.packages:
pmb.helpers.pmaports.find(args, package) pmb.helpers.pmaports.find(package)
# Increase pkgrel # Increase pkgrel
for package in args.packages: for package in args.packages:
@ -529,7 +533,7 @@ def aportupgrade(args: PmbArgs):
else: else:
# Each package must exist # Each package must exist
for package in args.packages: for package in args.packages:
pmb.helpers.pmaports.find(args, package) pmb.helpers.pmaports.find(package)
# Check each package for a new version # Check each package for a new version
for package in args.packages: for package in args.packages:
@ -551,9 +555,9 @@ def stats(args: PmbArgs):
chroot = Chroot.buildroot(args.arch) chroot = Chroot.buildroot(args.arch)
# Install ccache and display stats # Install ccache and display stats
pmb.chroot.apk.install(args, ["ccache"], chroot) pmb.chroot.apk.install(["ccache"], chroot)
logging.info(f"({chroot}) % ccache -s") logging.info(f"({chroot}) % ccache -s")
pmb.chroot.user(args, ["ccache", "-s"], chroot, output="stdout") pmb.chroot.user(["ccache", "-s"], chroot, output="stdout")
def work_migrate(args: PmbArgs): def work_migrate(args: PmbArgs):
@ -581,7 +585,7 @@ def bootimg_analyze(args: PmbArgs):
logging.info(tmp_output) logging.info(tmp_output)
def pull(args: PmbArgs): def pull():
failed = [] failed = []
for repo in pmb.config.git_repos.keys(): for repo in pmb.config.git_repos.keys():
if pmb.helpers.git.pull(repo) < 0: if pmb.helpers.git.pull(repo) < 0:
@ -610,7 +614,7 @@ def pull(args: PmbArgs):
def lint(args: PmbArgs): def lint(args: PmbArgs):
packages: Sequence[str] = args.packages packages: Sequence[str] = args.packages
if not packages: if not packages:
packages = pmb.helpers.pmaports.get_list(args) packages = pmb.helpers.pmaports.get_list()
pmb.helpers.lint.check(args, packages) pmb.helpers.lint.check(args, packages)

View file

@ -16,7 +16,7 @@ import pmb.helpers.pmaports
import pmb.helpers.run import pmb.helpers.run
def get_path(context: Context, name_repo): def get_path(name_repo: str):
"""Get the path to the repository. """Get the path to the repository.
The path is either the default one in the work dir, or a user-specified one in args. The path is either the default one in the work dir, or a user-specified one in args.
@ -24,8 +24,8 @@ def get_path(context: Context, name_repo):
:returns: full path to repository :returns: full path to repository
""" """
if name_repo == "pmaports": if name_repo == "pmaports":
return context.aports return get_context().config.aports
return pmb.config.work / "cache_git" / name_repo return get_context().config.work / "cache_git" / name_repo
def clone(name_repo): def clone(name_repo):
@ -40,7 +40,7 @@ def clone(name_repo):
if name_repo not in pmb.config.git_repos: if name_repo not in pmb.config.git_repos:
raise ValueError("No git repository configured for " + name_repo) raise ValueError("No git repository configured for " + name_repo)
path = get_path(get_context(), name_repo) path = get_path(name_repo)
if not os.path.exists(path): if not os.path.exists(path):
# Build git command # Build git command
url = pmb.config.git_repos[name_repo][0] url = pmb.config.git_repos[name_repo][0]
@ -49,7 +49,7 @@ def clone(name_repo):
# Create parent dir and clone # Create parent dir and clone
logging.info("Clone git repository: " + url) logging.info("Clone git repository: " + url)
os.makedirs(pmb.config.work / "cache_git", exist_ok=True) os.makedirs(get_context().config.work / "cache_git", exist_ok=True)
pmb.helpers.run.user(command, output="stdout") pmb.helpers.run.user(command, output="stdout")
# FETCH_HEAD does not exist after initial clone. Create it, so # FETCH_HEAD does not exist after initial clone. Create it, so
@ -59,7 +59,7 @@ def clone(name_repo):
open(fetch_head, "w").close() open(fetch_head, "w").close()
def rev_parse(path, revision="HEAD", extra_args: list = []): def rev_parse(path: Path, revision="HEAD", extra_args: list = []):
"""Run "git rev-parse" in a specific repository dir. """Run "git rev-parse" in a specific repository dir.
:param path: to the git repository :param path: to the git repository
@ -84,29 +84,29 @@ def can_fast_forward(path, branch_upstream, branch="HEAD"):
raise RuntimeError("Unexpected exit code from git: " + str(ret)) raise RuntimeError("Unexpected exit code from git: " + str(ret))
def clean_worktree(path): def clean_worktree(path: Path):
"""Check if there are not any modified files in the git dir.""" """Check if there are not any modified files in the git dir."""
command = ["git", "status", "--porcelain"] command = ["git", "status", "--porcelain"]
return pmb.helpers.run.user_output(command, path) == "" return pmb.helpers.run.user_output(command, path) == ""
def get_upstream_remote(context: Context, name_repo): def get_upstream_remote(aports: Path):
"""Find the remote, which matches the git URL from the config. """Find the remote, which matches the git URL from the config.
Usually "origin", but the user may have set up their git repository differently. Usually "origin", but the user may have set up their git repository differently.
""" """
name_repo = aports.parts[-1]
urls = pmb.config.git_repos[name_repo] urls = pmb.config.git_repos[name_repo]
path = get_path(context, name_repo)
command = ["git", "remote", "-v"] command = ["git", "remote", "-v"]
output = pmb.helpers.run.user_output(command, path) output = pmb.helpers.run.user_output(command, aports)
for line in output.split("\n"): for line in output.split("\n"):
if any(u in line for u in urls): if any(u in line for u in urls):
return line.split("\t", 1)[0] return line.split("\t", 1)[0]
raise RuntimeError("{}: could not find remote name for any URL '{}' in git" raise RuntimeError("{}: could not find remote name for any URL '{}' in git"
" repository: {}".format(name_repo, urls, path)) " repository: {}".format(name_repo, urls, aports))
def parse_channels_cfg(): def parse_channels_cfg(aports: Path):
"""Parse channels.cfg from pmaports.git, origin/master branch. """Parse channels.cfg from pmaports.git, origin/master branch.
Reference: https://postmarketos.org/channels.cfg Reference: https://postmarketos.org/channels.cfg
@ -123,13 +123,11 @@ def parse_channels_cfg():
if pmb.helpers.other.cache[cache_key]: if pmb.helpers.other.cache[cache_key]:
return pmb.helpers.other.cache[cache_key] return pmb.helpers.other.cache[cache_key]
context = get_context()
# Read with configparser # Read with configparser
cfg = configparser.ConfigParser() cfg = configparser.ConfigParser()
remote = get_upstream_remote(context, "pmaports") remote = get_upstream_remote(aports)
command = ["git", "show", f"{remote}/master:channels.cfg"] command = ["git", "show", f"{remote}/master:channels.cfg"]
stdout = pmb.helpers.run.user_output(command, context.aports, stdout = pmb.helpers.run.user_output(command, aports,
check=False) check=False)
try: try:
cfg.read_string(stdout) cfg.read_string(stdout)
@ -162,7 +160,7 @@ def parse_channels_cfg():
return ret return ret
def get_branches_official(name_repo): def get_branches_official(repo: Path):
"""Get all branches that point to official release channels. """Get all branches that point to official release channels.
:returns: list of supported branches, e.g. ["master", "3.11"] :returns: list of supported branches, e.g. ["master", "3.11"]
@ -170,17 +168,17 @@ def get_branches_official(name_repo):
# This functions gets called with pmaports and aports_upstream, because # This functions gets called with pmaports and aports_upstream, because
# both are displayed in "pmbootstrap status". But it only makes sense # both are displayed in "pmbootstrap status". But it only makes sense
# to display pmaports there, related code will be refactored soon (#1903). # to display pmaports there, related code will be refactored soon (#1903).
if name_repo != "pmaports": if repo.parts[-1] != "pmaports":
return ["master"] return ["master"]
channels_cfg = parse_channels_cfg() channels_cfg = parse_channels_cfg(repo)
ret = [] ret = []
for channel, channel_data in channels_cfg["channels"].items(): for channel, channel_data in channels_cfg["channels"].items():
ret.append(channel_data["branch_pmaports"]) ret.append(channel_data["branch_pmaports"])
return ret return ret
def pull(name_repo): def pull(repo_name: str):
"""Check if on official branch and essentially try ``git pull --ff-only``. """Check if on official branch and essentially try ``git pull --ff-only``.
Instead of really doing ``git pull --ff-only``, do it in multiple steps Instead of really doing ``git pull --ff-only``, do it in multiple steps
@ -189,18 +187,17 @@ def pull(name_repo):
:returns: integer, >= 0 on success, < 0 on error :returns: integer, >= 0 on success, < 0 on error
""" """
branches_official = get_branches_official(name_repo) repo = get_path(repo_name)
context = get_context() branches_official = get_branches_official(repo)
# Skip if repo wasn't cloned # Skip if repo wasn't cloned
path = get_path(context, name_repo) if not os.path.exists(repo):
if not os.path.exists(path): logging.debug(repo_name + ": repo was not cloned, skipping pull!")
logging.debug(name_repo + ": repo was not cloned, skipping pull!")
return 1 return 1
# Skip if not on official branch # Skip if not on official branch
branch = rev_parse(path, extra_args=["--abbrev-ref"]) branch = rev_parse(repo, extra_args=["--abbrev-ref"])
msg_start = "{} (branch: {}):".format(name_repo, branch) msg_start = "{} (branch: {}):".format(repo_name, branch)
if branch not in branches_official: if branch not in branches_official:
logging.warning("{} not on one of the official branches ({}), skipping" logging.warning("{} not on one of the official branches ({}), skipping"
" pull!" " pull!"
@ -208,13 +205,13 @@ def pull(name_repo):
return -1 return -1
# Skip if workdir is not clean # Skip if workdir is not clean
if not clean_worktree(path): if not clean_worktree(repo):
logging.warning(msg_start + " workdir is not clean, skipping pull!") logging.warning(msg_start + " workdir is not clean, skipping pull!")
return -2 return -2
# Skip if branch is tracking different remote # Skip if branch is tracking different remote
branch_upstream = get_upstream_remote(context, name_repo) + "/" + branch branch_upstream = get_upstream_remote(repo) + "/" + branch
remote_ref = rev_parse(path, branch + "@{u}", ["--abbrev-ref"]) remote_ref = rev_parse(repo, branch + "@{u}", ["--abbrev-ref"])
if remote_ref != branch_upstream: if remote_ref != branch_upstream:
logging.warning("{} is tracking unexpected remote branch '{}' instead" logging.warning("{} is tracking unexpected remote branch '{}' instead"
" of '{}'".format(msg_start, remote_ref, " of '{}'".format(msg_start, remote_ref,
@ -223,16 +220,16 @@ def pull(name_repo):
# Fetch (exception on failure, meaning connection to server broke) # Fetch (exception on failure, meaning connection to server broke)
logging.info(msg_start + " git pull --ff-only") logging.info(msg_start + " git pull --ff-only")
if not context.offline: if not get_context().offline:
pmb.helpers.run.user(["git", "fetch"], path) pmb.helpers.run.user(["git", "fetch"], repo)
# Skip if already up to date # Skip if already up to date
if rev_parse(path, branch) == rev_parse(path, branch_upstream): if rev_parse(repo, branch) == rev_parse(repo, branch_upstream):
logging.info(msg_start + " already up to date") logging.info(msg_start + " already up to date")
return 2 return 2
# Skip if we can't fast-forward # Skip if we can't fast-forward
if not can_fast_forward(path, branch_upstream): if not can_fast_forward(repo, branch_upstream):
logging.warning("{} can't fast-forward to {}, looks like you changed" logging.warning("{} can't fast-forward to {}, looks like you changed"
" the git history of your local branch. Skipping pull!" " the git history of your local branch. Skipping pull!"
"".format(msg_start, branch_upstream)) "".format(msg_start, branch_upstream))
@ -241,21 +238,21 @@ def pull(name_repo):
# Fast-forward now (should not fail due to checks above, so it's fine to # Fast-forward now (should not fail due to checks above, so it's fine to
# throw an exception on error) # throw an exception on error)
command = ["git", "merge", "--ff-only", branch_upstream] command = ["git", "merge", "--ff-only", branch_upstream]
pmb.helpers.run.user(command, path, "stdout") pmb.helpers.run.user(command, repo, "stdout")
return 0 return 0
def get_topdir(path: Path): def get_topdir(repo: Path):
"""Get top-dir of git repo. """Get top-dir of git repo.
:returns: a string with the top dir of the git repository, :returns: a string with the top dir of the git repository,
or an empty string if it's not a git repository. or an empty string if it's not a git repository.
""" """
return pmb.helpers.run.user(["git", "rev-parse", "--show-toplevel"], return pmb.helpers.run.user(["git", "rev-parse", "--show-toplevel"],
path, output_return=True, check=False).rstrip() repo, output_return=True, check=False).rstrip()
def get_files(path): def get_files(repo: Path):
"""Get all files inside a git repository, that are either already in the git tree or are not in gitignore. """Get all files inside a git repository, that are either already in the git tree or are not in gitignore.
Do not list deleted files. To be used for creating a tarball of the git repository. Do not list deleted files. To be used for creating a tarball of the git repository.
@ -265,12 +262,12 @@ def get_files(path):
:returns: all files in a git repository as list, relative to path :returns: all files in a git repository as list, relative to path
""" """
ret = [] ret = []
files = pmb.helpers.run.user_output(["git", "ls-files"], path).split("\n") files = pmb.helpers.run.user_output(["git", "ls-files"], repo).split("\n")
files += pmb.helpers.run.user_output(["git", "ls-files", files += pmb.helpers.run.user_output(["git", "ls-files",
"--exclude-standard", "--other"], "--exclude-standard", "--other"],
path).split("\n") repo).split("\n")
for file in files: for file in files:
if os.path.exists(f"{path}/{file}"): if os.path.exists(f"{repo}/{file}"):
ret += [file] ret += [file]
return ret return ret

View file

@ -8,7 +8,8 @@ from pathlib import Path
import shutil import shutil
import urllib.request import urllib.request
from pmb.core.types import PmbArgs from pmb.core import get_context
from pmb.types import PmbArgs
import pmb.helpers.run import pmb.helpers.run
def cache_file(prefix: str, url: str) -> Path: def cache_file(prefix: str, url: str) -> Path:
@ -16,7 +17,7 @@ def cache_file(prefix: str, url: str) -> Path:
return Path(f"{prefix}_{hashlib.sha256(url.encode('utf-8')).hexdigest()}") return Path(f"{prefix}_{hashlib.sha256(url.encode('utf-8')).hexdigest()}")
def download(args: PmbArgs, url, prefix, cache=True, loglevel=logging.INFO, def download(url, prefix, cache=True, loglevel=logging.INFO,
allow_404=False): allow_404=False):
"""Download a file to disk. """Download a file to disk.
@ -34,18 +35,19 @@ def download(args: PmbArgs, url, prefix, cache=True, loglevel=logging.INFO,
:returns: path to the downloaded file in the cache or None on 404 :returns: path to the downloaded file in the cache or None on 404
""" """
# Create cache folder # Create cache folder
if not os.path.exists(pmb.config.work / "cache_http"): context = get_context()
pmb.helpers.run.user(["mkdir", "-p", pmb.config.work / "cache_http"]) if not os.path.exists(context.config.work / "cache_http"):
pmb.helpers.run.user(["mkdir", "-p", context.config.work / "cache_http"])
# Check if file exists in cache # Check if file exists in cache
path = pmb.config.work / "cache_http" / cache_file(prefix, url) path = context.config.work / "cache_http" / cache_file(prefix, url)
if os.path.exists(path): if os.path.exists(path):
if cache: if cache:
return path return path
pmb.helpers.run.user(["rm", path]) pmb.helpers.run.user(["rm", path])
# Offline and not cached # Offline and not cached
if args.offline: if context.offline:
raise RuntimeError("File not found in cache and offline flag is" raise RuntimeError("File not found in cache and offline flag is"
f" enabled: {url}") f" enabled: {url}")

View file

@ -7,7 +7,7 @@ import os
import pmb.chroot import pmb.chroot
import pmb.chroot.apk import pmb.chroot.apk
import pmb.build import pmb.build
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.run import pmb.helpers.run
import pmb.helpers.pmaports import pmb.helpers.pmaports
@ -17,7 +17,7 @@ def check(args: PmbArgs, pkgnames):
:param pkgnames: Names of the packages to lint :param pkgnames: Names of the packages to lint
""" """
pmb.chroot.apk.install(args, ["atools"]) pmb.chroot.apk.install(["atools"])
# Mount pmaports.git inside the chroot so that we don't have to copy the # Mount pmaports.git inside the chroot so that we don't have to copy the
# package folders # package folders
@ -28,7 +28,7 @@ def check(args: PmbArgs, pkgnames):
# root # root
apkbuilds = [] apkbuilds = []
for pkgname in pkgnames: for pkgname in pkgnames:
aport = pmb.helpers.pmaports.find(args, pkgname) aport = pmb.helpers.pmaports.find(pkgname)
if not (aport / "APKBUILD").exists(): if not (aport / "APKBUILD").exists():
raise ValueError(f"Path does not contain an APKBUILD file: {aport}") raise ValueError(f"Path does not contain an APKBUILD file: {aport}")
relpath = os.path.relpath(aport, args.aports) relpath = os.path.relpath(aport, args.aports)
@ -40,7 +40,7 @@ def check(args: PmbArgs, pkgnames):
pkgstr = ", ".join(pkgnames) pkgstr = ", ".join(pkgnames)
logging.info(f"(native) linting {pkgstr} with apkbuild-lint") logging.info(f"(native) linting {pkgstr} with apkbuild-lint")
options = pmb.config.apkbuild_custom_valid_options options = pmb.config.apkbuild_custom_valid_options
return pmb.chroot.root(args, ["apkbuild-lint"] + apkbuilds, return pmb.chroot.root(["apkbuild-lint"] + apkbuilds,
check=False, output="stdout", check=False, output="stdout",
output_return=True, output_return=True,
working_dir=pmaports, working_dir=pmaports,

View file

@ -7,7 +7,7 @@ from typing import TextIO
import pmb.config import pmb.config
from pmb.core import get_context from pmb.core import get_context
from pmb.core.context import Context from pmb.core.context import Context
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
logfd: TextIO logfd: TextIO
@ -108,7 +108,7 @@ def add_verbose_log_level():
def init(args: PmbArgs): def init(args: PmbArgs):
"""Set log format and add the log file descriptor to logfd, add the verbose log level.""" """Set log format and add the log file descriptor to logfd, add the verbose log level."""
global logfd global logfd
context = pmb.core.get_context() context = get_context()
# Set log file descriptor (logfd) # Set log file descriptor (logfd)
if context.details_to_stdout: if context.details_to_stdout:
logfd = sys.stdout logfd = sys.stdout

View file

@ -3,7 +3,7 @@
import os import os
from pathlib import Path, PurePath from pathlib import Path, PurePath
from typing import List from typing import List
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers import pmb.helpers
from pmb.core import Chroot from pmb.core import Chroot
import pmb.helpers.run import pmb.helpers.run

View file

@ -1,5 +1,6 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pmb.core import get_context
from pmb.helpers import logging from pmb.helpers import logging
import os import os
from pathlib import Path from pathlib import Path
@ -7,7 +8,7 @@ import re
import pmb.chroot import pmb.chroot
import pmb.config import pmb.config
import pmb.config.init import pmb.config.init
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.pmaports import pmb.helpers.pmaports
import pmb.helpers.run import pmb.helpers.run
from typing import Dict, Any from typing import Dict, Any
@ -15,7 +16,7 @@ from typing import Dict, Any
from typing import Any, Dict from typing import Any, Dict
def folder_size(args: PmbArgs, path: Path): def folder_size(path: Path):
"""Run `du` to calculate the size of a folder. """Run `du` to calculate the size of a folder.
(this is less code and faster than doing the same task in pure Python) (this is less code and faster than doing the same task in pure Python)
@ -47,7 +48,7 @@ def check_grsec():
" patchset. This is not supported.") " patchset. This is not supported.")
def check_binfmt_misc(args): def check_binfmt_misc():
"""Check if the 'binfmt_misc' module is loaded. """Check if the 'binfmt_misc' module is loaded.
This is done by checking, if /proc/sys/fs/binfmt_misc/ exists. This is done by checking, if /proc/sys/fs/binfmt_misc/ exists.
@ -71,16 +72,17 @@ def check_binfmt_misc(args):
raise RuntimeError(f"Failed to set up binfmt_misc, see: {link}") raise RuntimeError(f"Failed to set up binfmt_misc, see: {link}")
def migrate_success(args: PmbArgs, version): def migrate_success(work: Path, version):
logging.info("Migration to version " + str(version) + " done") logging.info("Migration to version " + str(version) + " done")
with open(pmb.config.work / "version", "w") as handle: with open(work / "version", "w") as handle:
handle.write(str(version) + "\n") handle.write(str(version) + "\n")
def migrate_work_folder(args: PmbArgs): def migrate_work_folder(args: PmbArgs):
# Read current version # Read current version
context = get_context()
current = 0 current = 0
path = pmb.config.work / "version" path = context.config.work / "version"
if os.path.exists(path): if os.path.exists(path):
with open(path, "r") as f: with open(path, "r") as f:
current = int(f.read().rstrip()) current = int(f.read().rstrip())
@ -100,18 +102,18 @@ def migrate_work_folder(args: PmbArgs):
logging.info("* Building chroots have a different username (#709)") logging.info("* Building chroots have a different username (#709)")
logging.info("Migration will do the following:") logging.info("Migration will do the following:")
logging.info("* Zap your chroots") logging.info("* Zap your chroots")
logging.info(f"* Adjust '{pmb.config.work / 'config_abuild/abuild.conf'}'") logging.info(f"* Adjust '{context.config.work / 'config_abuild/abuild.conf'}'")
if not pmb.helpers.cli.confirm(args): if not pmb.helpers.cli.confirm(args):
raise RuntimeError("Aborted.") raise RuntimeError("Aborted.")
# Zap and update abuild.conf # Zap and update abuild.conf
pmb.chroot.zap(args, False) pmb.chroot.zap(args, False)
conf = pmb.config.work / "config_abuild/abuild.conf" conf = context.config.work / "config_abuild/abuild.conf"
if os.path.exists(conf): if os.path.exists(conf):
pmb.helpers.run.root(["sed", "-i", pmb.helpers.run.root(["sed", "-i",
"s./home/user/./home/pmos/.g", conf]) "s./home/user/./home/pmos/.g", conf])
# Update version file # Update version file
migrate_success(args, 1) migrate_success(context.config.work, 1)
current = 1 current = 1
# 1 => 2 # 1 => 2
@ -120,7 +122,7 @@ def migrate_work_folder(args: PmbArgs):
logging.info("Changelog:") logging.info("Changelog:")
logging.info("* Fix: cache_distfiles was writable for everyone") logging.info("* Fix: cache_distfiles was writable for everyone")
logging.info("Migration will do the following:") logging.info("Migration will do the following:")
logging.info(f"* Fix permissions of '{pmb.config.work / 'cache_distfiles'}'") logging.info(f"* Fix permissions of '{context.config.work / 'cache_distfiles'}'")
if not pmb.helpers.cli.confirm(args): if not pmb.helpers.cli.confirm(args):
raise RuntimeError("Aborted.") raise RuntimeError("Aborted.")
@ -129,8 +131,8 @@ def migrate_work_folder(args: PmbArgs):
for cmd in [["chown", "-R", "root:abuild", dir], for cmd in [["chown", "-R", "root:abuild", dir],
["chmod", "-R", "664", dir], ["chmod", "-R", "664", dir],
["chmod", "a+X", dir]]: ["chmod", "a+X", dir]]:
pmb.chroot.root(args, cmd) pmb.chroot.root(cmd)
migrate_success(args, 2) migrate_success(context.config.work, 2)
current = 2 current = 2
if current == 2: if current == 2:
@ -146,12 +148,12 @@ def migrate_work_folder(args: PmbArgs):
pmb.chroot.zap(args, False) pmb.chroot.zap(args, False)
# Update version file # Update version file
migrate_success(args, 3) migrate_success(context.config.work, 3)
current = 3 current = 3
if current == 3: if current == 3:
# Ask for confirmation # Ask for confirmation
path = pmb.config.work / "cache_git" path = context.config.work / "cache_git"
logging.info("Changelog:") logging.info("Changelog:")
logging.info("* pmbootstrap clones repositories with host system's") logging.info("* pmbootstrap clones repositories with host system's")
logging.info(" 'git' instead of using it from an Alpine chroot") logging.info(" 'git' instead of using it from an Alpine chroot")
@ -170,7 +172,7 @@ def migrate_work_folder(args: PmbArgs):
os.makedirs(path, 0o700, True) os.makedirs(path, 0o700, True)
# Update version file # Update version file
migrate_success(args, 4) migrate_success(context.config.work, 4)
current = 4 current = 4
if current == 4: if current == 4:
@ -187,23 +189,23 @@ def migrate_work_folder(args: PmbArgs):
pmb.chroot.zap(args, False) pmb.chroot.zap(args, False)
# Move packages to edge subdir # Move packages to edge subdir
edge_path = pmb.config.work / "packages/edge" edge_path = context.config.work / "packages/edge"
pmb.helpers.run.root(["mkdir", "-p", edge_path]) pmb.helpers.run.root(["mkdir", "-p", edge_path])
for arch in pmb.config.build_device_architectures: for arch in pmb.config.build_device_architectures:
old_path = pmb.config.work / "packages" / arch old_path = context.config.work / "packages" / arch
new_path = edge_path / arch new_path = edge_path / arch
if old_path.exists(): if old_path.exists():
if new_path.exists(): if new_path.exists():
raise RuntimeError(f"Won't move '{old_path}' to" raise RuntimeError(f"Won't move '{old_path}' to"
f" '{new_path}', destination already" f" '{new_path}', destination already"
" exists! Consider 'pmbootstrap zap -p'" " exists! Consider 'pmbootstrap zap -p'"
f" to delete '{pmb.config.work}/packages'.") f" to delete '{context.config.work}/packages'.")
pmb.helpers.run.root(["mv", old_path, new_path]) pmb.helpers.run.root(["mv", old_path, new_path])
pmb.helpers.run.root(["chown", pmb.config.chroot_uid_user, pmb.helpers.run.root(["chown", pmb.config.chroot_uid_user,
edge_path]) edge_path])
# Update version file # Update version file
migrate_success(args, 5) migrate_success(context.config.work, 5)
current = 5 current = 5
if current == 5: if current == 5:
@ -214,7 +216,7 @@ def migrate_work_folder(args: PmbArgs):
logging.info("Migration will do the following:") logging.info("Migration will do the following:")
logging.info("* Zap your chroots") logging.info("* Zap your chroots")
logging.info("* Adjust subdirs of your locally built packages dir:") logging.info("* Adjust subdirs of your locally built packages dir:")
logging.info(f" {pmb.config.work}/packages") logging.info(f" {context.config.work}/packages")
logging.info(" stable => v20.05") logging.info(" stable => v20.05")
logging.info(" stable-next => v21.03") logging.info(" stable-next => v21.03")
if not pmb.helpers.cli.confirm(args): if not pmb.helpers.cli.confirm(args):
@ -225,13 +227,13 @@ def migrate_work_folder(args: PmbArgs):
pmb.chroot.zap(args, False) pmb.chroot.zap(args, False)
# Migrate # Migrate
packages_dir = f"{pmb.config.work}/packages" packages_dir = f"{context.config.work}/packages"
for old, new in pmb.config.pmaports_channels_legacy.items(): for old, new in pmb.config.pmaports_channels_legacy.items():
if os.path.exists(f"{packages_dir}/{old}"): if os.path.exists(f"{packages_dir}/{old}"):
pmb.helpers.run.root(["mv", old, new], packages_dir) pmb.helpers.run.root(["mv", old, new], packages_dir)
# Update version file # Update version file
migrate_success(args, 6) migrate_success(context.config.work, 6)
current = 6 current = 6
# Can't migrate, user must delete it # Can't migrate, user must delete it
@ -239,7 +241,7 @@ def migrate_work_folder(args: PmbArgs):
raise RuntimeError("Sorry, we can't migrate that automatically. Please" raise RuntimeError("Sorry, we can't migrate that automatically. Please"
" run 'pmbootstrap shutdown', then delete your" " run 'pmbootstrap shutdown', then delete your"
" current work folder manually ('sudo rm -rf " " current work folder manually ('sudo rm -rf "
f"{pmb.config.work}') and start over with 'pmbootstrap" f"{context.config.work}') and start over with 'pmbootstrap"
" init'. All your binary packages and caches will" " init'. All your binary packages and caches will"
" be lost.") " be lost.")

View file

@ -12,7 +12,7 @@ import copy
from typing import Any, Dict from typing import Any, Dict
from pmb.helpers import logging from pmb.helpers import logging
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.pmaports import pmb.helpers.pmaports
import pmb.helpers.repo import pmb.helpers.repo
@ -25,7 +25,7 @@ def remove_operators(package):
return package return package
def get(args: PmbArgs, pkgname, arch, replace_subpkgnames=False, must_exist=True): def get(pkgname, arch, replace_subpkgnames=False, must_exist=True):
"""Find a package in pmaports, and as fallback in the APKINDEXes of the binary packages. """Find a package in pmaports, and as fallback in the APKINDEXes of the binary packages.
:param pkgname: package name (e.g. "hello-world") :param pkgname: package name (e.g. "hello-world")
@ -59,7 +59,7 @@ def get(args: PmbArgs, pkgname, arch, replace_subpkgnames=False, must_exist=True
# Find in pmaports # Find in pmaports
ret: Dict[str, Any] = {} ret: Dict[str, Any] = {}
pmaport = pmb.helpers.pmaports.get(args, pkgname, False) pmaport = pmb.helpers.pmaports.get(pkgname, False)
if pmaport: if pmaport:
ret = {"arch": pmaport["arch"], ret = {"arch": pmaport["arch"],
"depends": pmb.build._package.get_depends(args, pmaport), "depends": pmb.build._package.get_depends(args, pmaport),
@ -69,8 +69,8 @@ def get(args: PmbArgs, pkgname, arch, replace_subpkgnames=False, must_exist=True
# Find in APKINDEX (given arch) # Find in APKINDEX (given arch)
if not ret or not pmb.helpers.pmaports.check_arches(ret["arch"], arch): if not ret or not pmb.helpers.pmaports.check_arches(ret["arch"], arch):
pmb.helpers.repo.update(args, arch) pmb.helpers.repo.update(arch)
ret_repo = pmb.parse.apkindex.package(args, pkgname, arch, False) ret_repo = pmb.parse.apkindex.package(pkgname, arch, False)
# Save as result if there was no pmaport, or if the pmaport can not be # Save as result if there was no pmaport, or if the pmaport can not be
# built for the given arch, but there is a binary package for that arch # built for the given arch, but there is a binary package for that arch
@ -80,10 +80,10 @@ def get(args: PmbArgs, pkgname, arch, replace_subpkgnames=False, must_exist=True
# Find in APKINDEX (other arches) # Find in APKINDEX (other arches)
if not ret: if not ret:
pmb.helpers.repo.update(args) pmb.helpers.repo.update()
for arch_i in pmb.config.build_device_architectures: for arch_i in pmb.config.build_device_architectures:
if arch_i != arch: if arch_i != arch:
ret = pmb.parse.apkindex.package(args, pkgname, arch_i, False) ret = pmb.parse.apkindex.package(pkgname, arch_i, False)
if ret: if ret:
break break
@ -100,7 +100,7 @@ def get(args: PmbArgs, pkgname, arch, replace_subpkgnames=False, must_exist=True
if replace_subpkgnames: if replace_subpkgnames:
depends_new = [] depends_new = []
for depend in ret["depends"]: for depend in ret["depends"]:
depend_data = get(args, depend, arch, must_exist=False) depend_data = get(depend, arch, must_exist=False)
if not depend_data: if not depend_data:
logging.warning(f"WARNING: {pkgname}: failed to resolve" logging.warning(f"WARNING: {pkgname}: failed to resolve"
f" dependency '{depend}'") f" dependency '{depend}'")
@ -131,7 +131,7 @@ def get(args: PmbArgs, pkgname, arch, replace_subpkgnames=False, must_exist=True
" could not find this package in any APKINDEX!") " could not find this package in any APKINDEX!")
def depends_recurse(args: PmbArgs, pkgname, arch): def depends_recurse(pkgname, arch):
"""Recursively resolve all of the package's dependencies. """Recursively resolve all of the package's dependencies.
:param pkgname: name of the package (e.g. "device-samsung-i9100") :param pkgname: name of the package (e.g. "device-samsung-i9100")
@ -151,7 +151,7 @@ def depends_recurse(args: PmbArgs, pkgname, arch):
ret = [] ret = []
while len(queue): while len(queue):
pkgname_queue = queue.pop() pkgname_queue = queue.pop()
package = get(args, pkgname_queue, arch) package = get(pkgname_queue, arch)
# Add its depends to the queue # Add its depends to the queue
for depend in package["depends"]: for depend in package["depends"]:
@ -170,7 +170,7 @@ def depends_recurse(args: PmbArgs, pkgname, arch):
return ret return ret
def check_arch(args: PmbArgs, pkgname, arch, binary=True): def check_arch(pkgname, arch, binary=True):
"""Check if a package be built for a certain architecture, or is there a binary package for it. """Check if a package be built for a certain architecture, or is there a binary package for it.
:param pkgname: name of the package :param pkgname: name of the package
@ -181,7 +181,7 @@ def check_arch(args: PmbArgs, pkgname, arch, binary=True):
:returns: True when the package can be built, or there is a binary package, False otherwise :returns: True when the package can be built, or there is a binary package, False otherwise
""" """
if binary: if binary:
arches = get(args, pkgname, arch)["arch"] arches = get(pkgname, arch)["arch"]
else: else:
arches = pmb.helpers.pmaports.get(args, pkgname)["arch"] arches = pmb.helpers.pmaports.get(pkgname)["arch"]
return pmb.helpers.pmaports.check_arches(arches, arch) return pmb.helpers.pmaports.check_arches(arches, arch)

View file

@ -2,7 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pmb.helpers import logging from pmb.helpers import logging
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.file import pmb.helpers.file
import pmb.helpers.pmaports import pmb.helpers.pmaports
import pmb.helpers.repo import pmb.helpers.repo
@ -18,7 +18,7 @@ def package(args: PmbArgs, pkgname, reason="", dry=False):
:param dry: don't modify the APKBUILD, just print the message :param dry: don't modify the APKBUILD, just print the message
""" """
# Current and new pkgrel # Current and new pkgrel
path = pmb.helpers.pmaports.find(args, pkgname) / "APKBUILD" path = pmb.helpers.pmaports.find(pkgname) / "APKBUILD"
apkbuild = pmb.parse.apkbuild(path) apkbuild = pmb.parse.apkbuild(path)
pkgrel = int(apkbuild["pkgrel"]) pkgrel = int(apkbuild["pkgrel"])
pkgrel_new = pkgrel + 1 pkgrel_new = pkgrel + 1
@ -84,7 +84,7 @@ def auto_apkindex_package(args: PmbArgs, arch, aport, apk, dry=False):
# Ignore conflict-dependencies # Ignore conflict-dependencies
continue continue
providers = pmb.parse.apkindex.providers(args, depend, arch, providers = pmb.parse.apkindex.providers(depend, arch,
must_exist=False) must_exist=False)
if providers == {}: if providers == {}:
# We're only interested in missing depends starting with "so:" # We're only interested in missing depends starting with "so:"

View file

@ -12,7 +12,7 @@ from pmb.helpers import logging
from pathlib import Path from pathlib import Path
from typing import Optional, Sequence, Dict from typing import Optional, Sequence, Dict
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.parse import pmb.parse
def _find_apkbuilds() -> Dict[str, Path]: def _find_apkbuilds() -> Dict[str, Path]:
@ -23,7 +23,7 @@ def _find_apkbuilds() -> Dict[str, Path]:
return apkbuilds return apkbuilds
apkbuilds = {} apkbuilds = {}
for apkbuild in glob.iglob(f"{get_context().aports}/**/*/APKBUILD", recursive=True): for apkbuild in glob.iglob(f"{get_context().config.aports}/**/*/APKBUILD", recursive=True):
package = Path(apkbuild).parent.name package = Path(apkbuild).parent.name
if package in apkbuilds: if package in apkbuilds:
raise RuntimeError(f"Package {package} found in multiple aports " raise RuntimeError(f"Package {package} found in multiple aports "
@ -199,7 +199,7 @@ def find_optional(package: str) -> Optional[Path]:
return None return None
def get(args: PmbArgs, pkgname, must_exist=True, subpackages=True): def get(pkgname, must_exist=True, subpackages=True):
"""Find and parse an APKBUILD file. """Find and parse an APKBUILD file.
Run 'pmbootstrap apkbuild_parse hello-world' for a full output example. Run 'pmbootstrap apkbuild_parse hello-world' for a full output example.

View file

@ -9,12 +9,13 @@ See also:
""" """
import os import os
import hashlib import hashlib
from pmb.core import get_context
from pmb.helpers import logging from pmb.helpers import logging
from pathlib import Path from pathlib import Path
from typing import List from typing import List
import pmb.config.pmaports import pmb.config.pmaports
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.http import pmb.helpers.http
import pmb.helpers.run import pmb.helpers.run
import pmb.helpers.other import pmb.helpers.other
@ -47,7 +48,7 @@ def apkindex_hash(url: str, length: int=8) -> Path:
return Path(f"APKINDEX.{ret}.tar.gz") return Path(f"APKINDEX.{ret}.tar.gz")
def urls(args: PmbArgs, user_repository=True, postmarketos_mirror=True, alpine=True): def urls(user_repository=True, postmarketos_mirror=True, alpine=True):
"""Get a list of repository URLs, as they are in /etc/apk/repositories. """Get a list of repository URLs, as they are in /etc/apk/repositories.
:param user_repository: add /mnt/pmbootstrap/packages :param user_repository: add /mnt/pmbootstrap/packages
@ -60,18 +61,19 @@ def urls(args: PmbArgs, user_repository=True, postmarketos_mirror=True, alpine=T
# Get mirrordirs from channels.cfg (postmarketOS mirrordir is the same as # Get mirrordirs from channels.cfg (postmarketOS mirrordir is the same as
# the pmaports branch of the channel, no need to make it more complicated) # the pmaports branch of the channel, no need to make it more complicated)
channel_cfg = pmb.config.pmaports.read_config_channel(args) channel_cfg = pmb.config.pmaports.read_config_channel()
mirrordir_pmos = channel_cfg["branch_pmaports"] mirrordir_pmos = channel_cfg["branch_pmaports"]
mirrordir_alpine = channel_cfg["mirrordir_alpine"] mirrordir_alpine = channel_cfg["mirrordir_alpine"]
# Local user repository (for packages compiled with pmbootstrap) # Local user repository (for packages compiled with pmbootstrap)
if user_repository: if user_repository:
channel = pmb.config.pmaports.read_config(args)["channel"] channel = pmb.config.pmaports.read_config()["channel"]
ret.append(str(pmb.config.work / "packages" / channel)) ret.append(str(get_context().config.work / "packages" / channel))
context = get_context()
# Upstream postmarketOS binary repository # Upstream postmarketOS binary repository
if postmarketos_mirror: if postmarketos_mirror:
for mirror in args.mirrors_postmarketos: for mirror in context.config.mirrors_postmarketos:
# Remove "master" mirrordir to avoid breakage until bpo is adjusted # Remove "master" mirrordir to avoid breakage until bpo is adjusted
# (build.postmarketos.org#63) and to give potential other users of # (build.postmarketos.org#63) and to give potential other users of
# this flag a heads up. # this flag a heads up.
@ -88,12 +90,12 @@ def urls(args: PmbArgs, user_repository=True, postmarketos_mirror=True, alpine=T
if mirrordir_alpine == "edge": if mirrordir_alpine == "edge":
directories.append("testing") directories.append("testing")
for dir in directories: for dir in directories:
ret.append(f"{args.mirror_alpine}{mirrordir_alpine}/{dir}") ret.append(f"{context.config.mirror_alpine}{mirrordir_alpine}/{dir}")
return ret return ret
def apkindex_files(args: PmbArgs, arch=None, user_repository=True, pmos=True, def apkindex_files(arch=None, user_repository=True, pmos=True,
alpine=True) -> List[Path]: alpine=True) -> List[Path]:
"""Get a list of outside paths to all resolved APKINDEX.tar.gz files for a specific arch. """Get a list of outside paths to all resolved APKINDEX.tar.gz files for a specific arch.
@ -109,17 +111,17 @@ def apkindex_files(args: PmbArgs, arch=None, user_repository=True, pmos=True,
ret = [] ret = []
# Local user repository (for packages compiled with pmbootstrap) # Local user repository (for packages compiled with pmbootstrap)
if user_repository: if user_repository:
channel = pmb.config.pmaports.read_config(args)["channel"] channel = pmb.config.pmaports.read_config()["channel"]
ret = [pmb.config.work / "packages" / channel / arch / "APKINDEX.tar.gz"] ret = [get_context().config.work / "packages" / channel / arch / "APKINDEX.tar.gz"]
# Resolve the APKINDEX.$HASH.tar.gz files # Resolve the APKINDEX.$HASH.tar.gz files
for url in urls(args, False, pmos, alpine): for url in urls(False, pmos, alpine):
ret.append(pmb.config.work / f"cache_apk_{arch}" / apkindex_hash(url)) ret.append(get_context().config.work / f"cache_apk_{arch}" / apkindex_hash(url))
return ret return ret
def update(args: PmbArgs, arch=None, force=False, existing_only=False): def update(arch=None, force=False, existing_only=False):
"""Download the APKINDEX files for all URLs depending on the architectures. """Download the APKINDEX files for all URLs depending on the architectures.
:param arch: * one Alpine architecture name ("x86_64", "armhf", ...) :param arch: * one Alpine architecture name ("x86_64", "armhf", ...)
@ -132,7 +134,7 @@ def update(args: PmbArgs, arch=None, force=False, existing_only=False):
""" """
# Skip in offline mode, only show once # Skip in offline mode, only show once
cache_key = "pmb.helpers.repo.update" cache_key = "pmb.helpers.repo.update"
if args.offline: if get_context().offline:
if not pmb.helpers.other.cache[cache_key]["offline_msg_shown"]: if not pmb.helpers.other.cache[cache_key]["offline_msg_shown"]:
logging.info("NOTE: skipping package index update (offline mode)") logging.info("NOTE: skipping package index update (offline mode)")
pmb.helpers.other.cache[cache_key]["offline_msg_shown"] = True pmb.helpers.other.cache[cache_key]["offline_msg_shown"] = True
@ -148,11 +150,11 @@ def update(args: PmbArgs, arch=None, force=False, existing_only=False):
# outdated_arches: ["armhf", "x86_64", ... ] # outdated_arches: ["armhf", "x86_64", ... ]
outdated = {} outdated = {}
outdated_arches = [] outdated_arches = []
for url in urls(args, False): for url in urls(False):
for arch in architectures: for arch in architectures:
# APKINDEX file name from the URL # APKINDEX file name from the URL
url_full = url + "/" + arch + "/APKINDEX.tar.gz" url_full = url + "/" + arch + "/APKINDEX.tar.gz"
cache_apk_outside = pmb.config.work / f"cache_apk_{arch}" cache_apk_outside = get_context().config.work / f"cache_apk_{arch}"
apkindex = cache_apk_outside / f"APKINDEX.{apkindex_hash(url)}.tar.gz" apkindex = cache_apk_outside / f"APKINDEX.{apkindex_hash(url)}.tar.gz"
# Find update reason, possibly skip non-existing or known 404 files # Find update reason, possibly skip non-existing or known 404 files
@ -186,8 +188,8 @@ def update(args: PmbArgs, arch=None, force=False, existing_only=False):
# Download and move to right location # Download and move to right location
for (i, (url, target)) in enumerate(outdated.items()): for (i, (url, target)) in enumerate(outdated.items()):
pmb.helpers.cli.progress_print(args, i / len(outdated)) pmb.helpers.cli.progress_print(i / len(outdated))
temp = pmb.helpers.http.download(args, url, "APKINDEX", False, temp = pmb.helpers.http.download(url, "APKINDEX", False,
logging.DEBUG, True) logging.DEBUG, True)
if not temp: if not temp:
pmb.helpers.other.cache[cache_key]["404"].append(url) pmb.helpers.other.cache[cache_key]["404"].append(url)
@ -196,12 +198,12 @@ def update(args: PmbArgs, arch=None, force=False, existing_only=False):
if not os.path.exists(target_folder): if not os.path.exists(target_folder):
pmb.helpers.run.root(["mkdir", "-p", target_folder]) pmb.helpers.run.root(["mkdir", "-p", target_folder])
pmb.helpers.run.root(["cp", temp, target]) pmb.helpers.run.root(["cp", temp, target])
pmb.helpers.cli.progress_flush(args) pmb.helpers.cli.progress_flush()
return True return True
def alpine_apkindex_path(args: PmbArgs, repo="main", arch=None): def alpine_apkindex_path(repo="main", arch=None):
"""Get the path to a specific Alpine APKINDEX file on disk and download it if necessary. """Get the path to a specific Alpine APKINDEX file on disk and download it if necessary.
:param repo: Alpine repository name (e.g. "main") :param repo: Alpine repository name (e.g. "main")
@ -214,10 +216,10 @@ def alpine_apkindex_path(args: PmbArgs, repo="main", arch=None):
# Download the file # Download the file
arch = arch or pmb.config.arch_native arch = arch or pmb.config.arch_native
update(args, arch) update(arch)
# Find it on disk # Find it on disk
channel_cfg = pmb.config.pmaports.read_config_channel(args) channel_cfg = pmb.config.pmaports.read_config_channel()
repo_link = f"{args.mirror_alpine}{channel_cfg['mirrordir_alpine']}/{repo}" repo_link = f"{get_context().config.mirror_alpine}{channel_cfg['mirrordir_alpine']}/{repo}"
cache_folder = pmb.config.work / (f"cache_apk_{arch}") cache_folder = get_context().config.work / (f"cache_apk_{arch}")
return cache_folder / apkindex_hash(repo_link) return cache_folder / apkindex_hash(repo_link)

View file

@ -6,7 +6,8 @@ import glob
import pmb.config.pmaports import pmb.config.pmaports
import pmb.helpers.repo import pmb.helpers.repo
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
from pmb.core import get_context
progress_done = 0 progress_done = 0
@ -25,7 +26,7 @@ def get_arch(args: PmbArgs):
def check_repo_arg(args: PmbArgs): def check_repo_arg(args: PmbArgs):
cfg = pmb.config.pmaports.read_config_repos(args) cfg = pmb.config.pmaports.read_config_repos()
repo = args.repository repo = args.repository
if repo in cfg: if repo in cfg:
@ -41,8 +42,8 @@ def check_repo_arg(args: PmbArgs):
def check_existing_pkgs(args: PmbArgs, arch): def check_existing_pkgs(args: PmbArgs, arch):
channel = pmb.config.pmaports.read_config(args)["channel"] channel = pmb.config.pmaports.read_config()["channel"]
path = pmb.config.work / "packages" / channel / arch path = get_context().config.work / "packages" / channel / arch
if glob.glob(f"{path}/*"): if glob.glob(f"{path}/*"):
logging.info(f"Packages path: {path}") logging.info(f"Packages path: {path}")
@ -57,7 +58,7 @@ def check_existing_pkgs(args: PmbArgs, arch):
def get_steps(args: PmbArgs): def get_steps(args: PmbArgs):
cfg = pmb.config.pmaports.read_config_repos(args) cfg = pmb.config.pmaports.read_config_repos()
prev_step = 0 prev_step = 0
ret = {} ret = {}
@ -132,11 +133,11 @@ def run_steps(args: PmbArgs, steps, arch, chroot: Chroot):
if chroot != Chroot.native(): if chroot != Chroot.native():
log_progress(f"initializing native chroot (merge /usr: {usr_merge.name})") log_progress(f"initializing native chroot (merge /usr: {usr_merge.name})")
# Native chroot needs pmOS binary package repo for cross compilers # Native chroot needs pmOS binary package repo for cross compilers
pmb.chroot.init(args, Chroot.native(), usr_merge) pmb.chroot.init(Chroot.native(), usr_merge)
log_progress(f"initializing {chroot} chroot (merge /usr: {usr_merge.name})") log_progress(f"initializing {chroot} chroot (merge /usr: {usr_merge.name})")
# Initialize without pmOS binary package repo # Initialize without pmOS binary package repo
pmb.chroot.init(args, chroot, usr_merge, postmarketos_mirror=False) pmb.chroot.init(chroot, usr_merge, postmarketos_mirror=False)
for package in get_packages(bootstrap_line): for package in get_packages(bootstrap_line):
log_progress(f"building {package}") log_progress(f"building {package}")
@ -181,9 +182,9 @@ def require_bootstrap(args: PmbArgs, arch, trigger_str):
:param arch: for which architecture :param arch: for which architecture
:param trigger_str: message for the user to understand what caused this :param trigger_str: message for the user to understand what caused this
""" """
if pmb.config.other.is_systemd_selected(args): if pmb.config.other.is_systemd_selected(get_context().config):
pmb.helpers.repo.update(args, arch) pmb.helpers.repo.update(arch)
pkg = pmb.parse.apkindex.package(args, "postmarketos-base-systemd", pkg = pmb.parse.apkindex.package("postmarketos-base-systemd",
arch, False) arch, False)
if not pkg: if not pkg:
require_bootstrap_error("systemd", arch, trigger_str) require_bootstrap_error("systemd", arch, trigger_str)

View file

@ -3,12 +3,12 @@
from pmb.helpers import logging from pmb.helpers import logging
import pmb.build import pmb.build
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.package import pmb.helpers.package
import pmb.helpers.pmaports import pmb.helpers.pmaports
def filter_missing_packages(args: PmbArgs, arch, pkgnames): def filter_missing_packages(arch, pkgnames):
"""Create a subset of pkgnames with missing or outdated binary packages. """Create a subset of pkgnames with missing or outdated binary packages.
:param arch: architecture (e.g. "armhf") :param arch: architecture (e.g. "armhf")
@ -17,15 +17,15 @@ def filter_missing_packages(args: PmbArgs, arch, pkgnames):
""" """
ret = [] ret = []
for pkgname in pkgnames: for pkgname in pkgnames:
binary = pmb.parse.apkindex.package(args, pkgname, arch, False) binary = pmb.parse.apkindex.package(pkgname, arch, False)
must_exist = False if binary else True must_exist = False if binary else True
pmaport = pmb.helpers.pmaports.get(args, pkgname, must_exist) pmaport = pmb.helpers.pmaports.get(pkgname, must_exist)
if pmaport and pmb.build.is_necessary(args, arch, pmaport): if pmaport and pmb.build.is_necessary(arch, pmaport):
ret.append(pkgname) ret.append(pkgname)
return ret return ret
def filter_aport_packages(args: PmbArgs, arch, pkgnames): def filter_aport_packages(pkgnames):
"""Create a subset of pkgnames where each one has an aport. """Create a subset of pkgnames where each one has an aport.
:param arch: architecture (e.g. "armhf") :param arch: architecture (e.g. "armhf")
@ -34,12 +34,12 @@ def filter_aport_packages(args: PmbArgs, arch, pkgnames):
""" """
ret = [] ret = []
for pkgname in pkgnames: for pkgname in pkgnames:
if pmb.helpers.pmaports.find_optional(args, pkgname): if pmb.helpers.pmaports.find_optional(pkgname):
ret += [pkgname] ret += [pkgname]
return ret return ret
def filter_arch_packages(args: PmbArgs, arch, pkgnames): def filter_arch_packages(arch, pkgnames):
"""Create a subset of pkgnames with packages removed that can not be built for a certain arch. """Create a subset of pkgnames with packages removed that can not be built for a certain arch.
:param arch: architecture (e.g. "armhf") :param arch: architecture (e.g. "armhf")
@ -48,12 +48,12 @@ def filter_arch_packages(args: PmbArgs, arch, pkgnames):
""" """
ret = [] ret = []
for pkgname in pkgnames: for pkgname in pkgnames:
if pmb.helpers.package.check_arch(args, pkgname, arch, False): if pmb.helpers.package.check_arch(pkgname, arch, False):
ret += [pkgname] ret += [pkgname]
return ret return ret
def get_relevant_packages(args: PmbArgs, arch, pkgname=None, built=False): def get_relevant_packages(arch, pkgname=None, built=False):
"""Get all packages that can be built for the architecture in question. """Get all packages that can be built for the architecture in question.
:param arch: architecture (e.g. "armhf") :param arch: architecture (e.g. "armhf")
@ -63,20 +63,20 @@ def get_relevant_packages(args: PmbArgs, arch, pkgname=None, built=False):
["devicepkg-dev", "hello-world", "osk-sdl"] ["devicepkg-dev", "hello-world", "osk-sdl"]
""" """
if pkgname: if pkgname:
if not pmb.helpers.package.check_arch(args, pkgname, arch, False): if not pmb.helpers.package.check_arch(pkgname, arch, False):
raise RuntimeError(pkgname + " can't be built for " + arch + ".") raise RuntimeError(pkgname + " can't be built for " + arch + ".")
ret = pmb.helpers.package.depends_recurse(args, pkgname, arch) ret = pmb.helpers.package.depends_recurse(pkgname, arch)
else: else:
ret = pmb.helpers.pmaports.get_list(args) ret = pmb.helpers.pmaports.get_list()
ret = filter_arch_packages(args, arch, ret) ret = filter_arch_packages(arch, ret)
if built: if built:
ret = filter_aport_packages(args, arch, ret) ret = filter_aport_packages(ret)
if not len(ret): if not len(ret):
logging.info("NOTE: no aport found for any package in the" logging.info("NOTE: no aport found for any package in the"
" dependency tree, it seems they are all provided by" " dependency tree, it seems they are all provided by"
" upstream (Alpine).") " upstream (Alpine).")
else: else:
ret = filter_missing_packages(args, arch, ret) ret = filter_missing_packages(arch, ret)
if not len(ret): if not len(ret):
logging.info("NOTE: all relevant packages are up to date, use" logging.info("NOTE: all relevant packages are up to date, use"
" --built to include the ones that have already been" " --built to include the ones that have already been"
@ -87,7 +87,7 @@ def get_relevant_packages(args: PmbArgs, arch, pkgname=None, built=False):
return ret return ret
def generate_output_format(args: PmbArgs, arch, pkgnames): def generate_output_format(arch, pkgnames):
"""Generate the detailed output format. """Generate the detailed output format.
:param arch: architecture :param arch: architecture
@ -105,15 +105,15 @@ def generate_output_format(args: PmbArgs, arch, pkgnames):
""" """
ret = [] ret = []
for pkgname in pkgnames: for pkgname in pkgnames:
entry = pmb.helpers.package.get(args, pkgname, arch, True) entry = pmb.helpers.package.get(pkgname, arch, True)
ret += [{"pkgname": entry["pkgname"], ret += [{"pkgname": entry["pkgname"],
"repo": pmb.helpers.pmaports.get_repo(args, pkgname), "repo": pmb.helpers.pmaports.get_repo(pkgname),
"version": entry["version"], "version": entry["version"],
"depends": entry["depends"]}] "depends": entry["depends"]}]
return ret return ret
def generate(args: PmbArgs, arch, overview, pkgname=None, built=False): def generate(arch, overview, pkgname=None, built=False):
"""Get packages that need to be built, with all their dependencies. """Get packages that need to be built, with all their dependencies.
:param arch: architecture (e.g. "armhf") :param arch: architecture (e.g. "armhf")
@ -129,9 +129,9 @@ def generate(args: PmbArgs, arch, overview, pkgname=None, built=False):
"".format(packages_str, arch)) "".format(packages_str, arch))
# Order relevant packages # Order relevant packages
ret = get_relevant_packages(args, arch, pkgname, built) ret = get_relevant_packages(arch, pkgname, built)
# Output format # Output format
if overview: if overview:
return ret return ret
return generate_output_format(args, arch, ret) return generate_output_format(arch, ret)

View file

@ -5,7 +5,7 @@ from pathlib import Path
import subprocess import subprocess
import pmb.helpers.run_core import pmb.helpers.run_core
from typing import Any, Dict, List, Optional, Sequence from typing import Any, Dict, List, Optional, Sequence
from pmb.core.types import Env, PathString, PmbArgs from pmb.types import Env, PathString, PmbArgs
def user(cmd: Sequence[PathString], working_dir: Optional[Path] = None, output: str = "log", output_return: bool = False, def user(cmd: Sequence[PathString], working_dir: Optional[Path] = None, output: str = "log", output_return: bool = False,

View file

@ -2,7 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import fcntl import fcntl
from pmb.core import get_context from pmb.core import get_context
from pmb.core.types import PathString, Env from pmb.types import PathString, Env
from pmb.helpers import logging from pmb.helpers import logging
import os import os
from pathlib import Path from pathlib import Path

View file

@ -3,7 +3,8 @@
import pmb.config import pmb.config
import pmb.config.workdir import pmb.config.workdir
import pmb.helpers.git import pmb.helpers.git
from pmb.core.types import PmbArgs from pmb.types import Config, PmbArgs
from pmb.core import get_context
from typing import List, Tuple from typing import List, Tuple
@ -15,8 +16,8 @@ def print_status_line(key: str, value: str):
print(f"{key.ljust(padding)} {value}") print(f"{key.ljust(padding)} {value}")
def print_channel(args: PmbArgs) -> None: def print_channel(config: Config) -> None:
pmaports_cfg = pmb.config.pmaports.read_config(args) pmaports_cfg = pmb.config.pmaports.read_config()
channel = pmaports_cfg["channel"] channel = pmaports_cfg["channel"]
# Get branch name (if on branch) or current commit # Get branch name (if on branch) or current commit
@ -32,28 +33,29 @@ def print_channel(args: PmbArgs) -> None:
print_status_line("Channel", value) print_status_line("Channel", value)
def print_device(args: PmbArgs) -> None: def print_device(args: PmbArgs, config: Config) -> None:
kernel = "" kernel = ""
if pmb.parse._apkbuild.kernels(args, args.device): if pmb.parse._apkbuild.kernels(config.device):
kernel = f", kernel: {args.kernel}" kernel = f", kernel: {config.kernel}"
value = f"{args.device} ({args.deviceinfo['arch']}{kernel})" value = f"{config.device} ({args.deviceinfo['arch']}{kernel})"
print_status_line("Device", value) print_status_line("Device", value)
def print_ui(args: PmbArgs) -> None: def print_ui(config: Config) -> None:
print_status_line("UI", args.ui) print_status_line("UI", config.ui)
def print_systemd(args: PmbArgs) -> None: def print_systemd(config: Config) -> None:
yesno, reason = pmb.config.other.systemd_selected_str(args) yesno, reason = pmb.config.other.systemd_selected_str(config)
print_status_line("systemd", f"{yesno} ({reason})") print_status_line("systemd", f"{yesno} ({reason})")
def print_status(args: PmbArgs) -> None: def print_status(args: PmbArgs) -> None:
""" :param details: if True, print each passing check instead of a summary """ :param details: if True, print each passing check instead of a summary
:returns: True if all checks passed, False otherwise """ :returns: True if all checks passed, False otherwise """
print_channel(args) config = get_context().config
print_device(args) print_channel(config)
print_ui(args) print_device(args, config)
print_systemd(args) print_ui(config)
print_systemd(config)

View file

@ -3,7 +3,7 @@
import os import os
import glob import glob
from pmb.core import get_context from pmb.core import get_context
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.pmaports import pmb.helpers.pmaports
import pmb.helpers.package import pmb.helpers.package
import pmb.parse import pmb.parse
@ -19,7 +19,7 @@ def list_ui(args: PmbArgs, arch):
" customization. The \"console\" UI should be selected if" " customization. The \"console\" UI should be selected if"
" a graphical UI is not desired.")] " a graphical UI is not desired.")]
context = get_context() # noqa: F821 context = get_context() # noqa: F821
for path in sorted(context.aports.glob("main/postmarketos-ui-*")): for path in sorted(context.config.aports.glob("main/postmarketos-ui-*")):
apkbuild = pmb.parse.apkbuild(path) apkbuild = pmb.parse.apkbuild(path)
ui = os.path.basename(path).split("-", 2)[2] ui = os.path.basename(path).split("-", 2)[2]
if pmb.helpers.package.check_arch(args, apkbuild["pkgname"], arch): if pmb.helpers.package.check_arch(args, apkbuild["pkgname"], arch):
@ -27,10 +27,10 @@ def list_ui(args: PmbArgs, arch):
return ret return ret
def check_option(args: PmbArgs, ui, option): def check_option(ui, option):
""" """
Check if an option, such as pmb:systemd, is inside an UI's APKBUILD. Check if an option, such as pmb:systemd, is inside an UI's APKBUILD.
""" """
pkgname = f"postmarketos-ui-{ui}" pkgname = f"postmarketos-ui-{ui}"
apkbuild = pmb.helpers.pmaports.get(args, pkgname, subpackages=False) apkbuild = pmb.helpers.pmaports.get(pkgname, subpackages=False)
return option in apkbuild["options"] return option in apkbuild["options"]

View file

@ -9,13 +9,14 @@ import sys
from typing import Dict, List, Optional, Sequence, TypedDict from typing import Dict, List, Optional, Sequence, TypedDict
from pathlib import Path from pathlib import Path
import pmb.build
import pmb.chroot import pmb.chroot
import pmb.chroot.apk import pmb.chroot.apk
import pmb.chroot.other import pmb.chroot.other
import pmb.chroot.initfs import pmb.chroot.initfs
import pmb.config import pmb.config
import pmb.config.pmaports import pmb.config.pmaports
from pmb.core.types import PartitionLayout, PmbArgs from pmb.types import Config, PartitionLayout, PmbArgs
import pmb.helpers.devices import pmb.helpers.devices
from pmb.helpers.mount import mount_device_rootfs from pmb.helpers.mount import mount_device_rootfs
import pmb.helpers.run import pmb.helpers.run
@ -24,7 +25,7 @@ import pmb.install.blockdevice
import pmb.install.recovery import pmb.install.recovery
import pmb.install.ui import pmb.install.ui
import pmb.install import pmb.install
from pmb.core import Chroot, ChrootType from pmb.core import Chroot, ChrootType, get_context
# Keep track of the packages we already visited in get_recommends() to avoid # Keep track of the packages we already visited in get_recommends() to avoid
# infinite recursion # infinite recursion
@ -32,7 +33,7 @@ get_recommends_visited: List[str] = []
get_selected_providers_visited: List[str] = [] get_selected_providers_visited: List[str] = []
def get_subpartitions_size(args: PmbArgs, chroot: Chroot): def get_subpartitions_size(chroot: Chroot):
""" """
Calculate the size of the boot and root subpartition. Calculate the size of the boot and root subpartition.
@ -40,18 +41,19 @@ def get_subpartitions_size(args: PmbArgs, chroot: Chroot):
:returns: (boot, root) the size of the boot and root :returns: (boot, root) the size of the boot and root
partition as integer in MiB partition as integer in MiB
""" """
boot = int(args.boot_size) config = get_context().config
boot = int(config.boot_size)
# Estimate root partition size, then add some free space. The size # Estimate root partition size, then add some free space. The size
# calculation is not as trivial as one may think, and depending on the # calculation is not as trivial as one may think, and depending on the
# file system etc it seems to be just impossible to get it right. # file system etc it seems to be just impossible to get it right.
root = pmb.helpers.other.folder_size(args, chroot.path) / 1024 root = pmb.helpers.other.folder_size(chroot.path) / 1024
root *= 1.20 root *= 1.20
root += 50 + int(args.extra_space) root += 50 + int(config.extra_space)
return (boot, root) return (boot, root)
def get_nonfree_packages(args: PmbArgs, device): def get_nonfree_packages(device):
""" """
Get any legacy non-free subpackages in the APKBUILD. Get any legacy non-free subpackages in the APKBUILD.
Also see: https://postmarketos.org/edge/2024/02/15/default-nonfree-fw/ Also see: https://postmarketos.org/edge/2024/02/15/default-nonfree-fw/
@ -60,7 +62,7 @@ def get_nonfree_packages(args: PmbArgs, device):
["device-nokia-n900-nonfree-firmware"] ["device-nokia-n900-nonfree-firmware"]
""" """
# Read subpackages # Read subpackages
device_path = pmb.helpers.devices.find_path(args, device, 'APKBUILD') device_path = pmb.helpers.devices.find_path(device, 'APKBUILD')
if not device_path: if not device_path:
raise RuntimeError(f"Device package not found for {device}") raise RuntimeError(f"Device package not found for {device}")
@ -134,15 +136,15 @@ def copy_files_from_chroot(args: PmbArgs, chroot: Chroot):
# Update or copy all files # Update or copy all files
if args.rsync: if args.rsync:
pmb.chroot.apk.install(args, ["rsync"], Chroot.native()) pmb.chroot.apk.install(["rsync"], Chroot.native())
rsync_flags = "-a" rsync_flags = "-a"
if args.verbose: if args.verbose:
rsync_flags += "vP" rsync_flags += "vP"
pmb.chroot.root(args, ["rsync", rsync_flags, "--delete"] + folders + pmb.chroot.root(["rsync", rsync_flags, "--delete"] + folders +
["/mnt/install/"], working_dir=mountpoint) ["/mnt/install/"], working_dir=mountpoint)
pmb.chroot.root(args, ["rm", "-rf", "/mnt/install/home"]) pmb.chroot.root(["rm", "-rf", "/mnt/install/home"])
else: else:
pmb.chroot.root(args, ["cp", "-a"] + folders + ["/mnt/install/"], pmb.chroot.root(["cp", "-a"] + folders + ["/mnt/install/"],
working_dir=mountpoint) working_dir=mountpoint)
@ -174,7 +176,7 @@ def configure_apk(args: PmbArgs):
# Official keys + local keys # Official keys + local keys
if args.install_local_pkgs: if args.install_local_pkgs:
keys_dir = pmb.config.work / "config_apk_keys" keys_dir = get_context().config.work / "config_apk_keys"
# Copy over keys # Copy over keys
rootfs = (Chroot.native() / "mnt/install") rootfs = (Chroot.native() / "mnt/install")
@ -194,7 +196,7 @@ def configure_apk(args: PmbArgs):
pmb.helpers.run.user(["cat", rootfs / "etc/apk/repositories"]) pmb.helpers.run.user(["cat", rootfs / "etc/apk/repositories"])
def set_user(args: PmbArgs): def set_user(config: Config):
""" """
Create user with UID 10000 if it doesn't exist. Create user with UID 10000 if it doesn't exist.
Usually the ID for the first user created is 1000, but higher ID is Usually the ID for the first user created is 1000, but higher ID is
@ -202,21 +204,21 @@ def set_user(args: PmbArgs):
this was done to avoid conflict with Android UIDs/GIDs, but pmOS has since this was done to avoid conflict with Android UIDs/GIDs, but pmOS has since
dropped support for hybris/Halium. dropped support for hybris/Halium.
""" """
chroot = Chroot.rootfs(args.device) chroot = Chroot.rootfs(config.device)
if not pmb.chroot.user_exists(args, args.user, chroot): if not pmb.chroot.user_exists(config.user, chroot):
pmb.chroot.root(args, ["adduser", "-D", "-u", "10000", args.user], pmb.chroot.root(["adduser", "-D", "-u", "10000", config.user],
chroot) chroot)
pmaports_cfg = pmb.config.pmaports.read_config(args) pmaports_cfg = pmb.config.pmaports.read_config()
groups = [] groups = []
groups += pmaports_cfg.get("install_user_groups", groups += pmaports_cfg.get("install_user_groups",
"audio,input,netdev,plugdev,video,wheel").split(",") "audio,input,netdev,plugdev,video,wheel").split(",")
groups += pmb.install.ui.get_groups(args) groups += pmb.install.ui.get_groups(config)
for group in groups: for group in groups:
pmb.chroot.root(args, ["addgroup", "-S", group], chroot, pmb.chroot.root(["addgroup", "-S", group], chroot,
check=False) check=False)
pmb.chroot.root(args, ["addgroup", args.user, group], chroot) pmb.chroot.root(["addgroup", config.user, group], chroot)
def setup_login_chpasswd_user_from_arg(args: PmbArgs, chroot: Chroot): def setup_login_chpasswd_user_from_arg(args: PmbArgs, chroot: Chroot):
@ -237,7 +239,7 @@ def setup_login_chpasswd_user_from_arg(args: PmbArgs, chroot: Chroot):
with open(path_outside, "w", encoding="utf-8") as handle: with open(path_outside, "w", encoding="utf-8") as handle:
handle.write(f"{args.user}:{args.password}") handle.write(f"{args.user}:{args.password}")
pmb.chroot.root(args, ["sh", "-c", f"cat {shlex.quote(path)} | chpasswd"], pmb.chroot.root(["sh", "-c", f"cat {shlex.quote(path)} | chpasswd"],
chroot) chroot)
os.unlink(path_outside) os.unlink(path_outside)
@ -251,7 +253,7 @@ def is_root_locked(args: PmbArgs, chroot: Chroot):
:param suffix: either rootfs_{args.device} or installer_{args.device} :param suffix: either rootfs_{args.device} or installer_{args.device}
""" """
shadow_root = pmb.chroot.root(args, ["grep", "^root:!:", "/etc/shadow"], shadow_root = pmb.chroot.root(["grep", "^root:!:", "/etc/shadow"],
chroot, output_return=True, check=False) chroot, output_return=True, check=False)
return shadow_root.startswith("root:!:") return shadow_root.startswith("root:!:")
@ -272,7 +274,7 @@ def setup_login(args: PmbArgs, chroot: Chroot):
else: else:
while True: while True:
try: try:
pmb.chroot.root(args, ["passwd", args.user], chroot, pmb.chroot.root(["passwd", args.user], chroot,
output="interactive") output="interactive")
break break
except RuntimeError: except RuntimeError:
@ -284,17 +286,17 @@ def setup_login(args: PmbArgs, chroot: Chroot):
logging.debug(f"({chroot}) root is already locked") logging.debug(f"({chroot}) root is already locked")
else: else:
logging.debug(f"({chroot}) locking root") logging.debug(f"({chroot}) locking root")
pmb.chroot.root(args, ["passwd", "-l", "root"], chroot) pmb.chroot.root(["passwd", "-l", "root"], chroot)
def copy_ssh_keys(args: PmbArgs): def copy_ssh_keys(config: Config):
""" """
If requested, copy user's SSH public keys to the device if they exist If requested, copy user's SSH public keys to the device if they exist
""" """
if not args.ssh_keys: if not config.ssh_keys:
return return
keys = [] keys = []
for key in glob.glob(os.path.expanduser(args.ssh_key_glob)): for key in glob.glob(os.path.expanduser(config.ssh_key_glob)):
with open(key, "r") as infile: with open(key, "r") as infile:
keys += infile.readlines() keys += infile.readlines()
@ -310,7 +312,7 @@ def copy_ssh_keys(args: PmbArgs):
outfile.write("%s" % key) outfile.write("%s" % key)
outfile.close() outfile.close()
target = Chroot.native() / "mnt/install/home/" / args.user / ".ssh" target = Chroot.native() / "mnt/install/home/" / config.user / ".ssh"
pmb.helpers.run.root(["mkdir", target]) pmb.helpers.run.root(["mkdir", target])
pmb.helpers.run.root(["chmod", "700", target]) pmb.helpers.run.root(["chmod", "700", target])
pmb.helpers.run.root(["cp", authorized_keys, target / "authorized_keys"]) pmb.helpers.run.root(["cp", authorized_keys, target / "authorized_keys"])
@ -318,30 +320,30 @@ def copy_ssh_keys(args: PmbArgs):
pmb.helpers.run.root(["chown", "-R", "10000:10000", target]) pmb.helpers.run.root(["chown", "-R", "10000:10000", target])
def setup_keymap(args: PmbArgs): def setup_keymap(config: Config):
""" """
Set the keymap with the setup-keymap utility if the device requires it Set the keymap with the setup-keymap utility if the device requires it
""" """
chroot = Chroot(ChrootType.ROOTFS, args.device) chroot = Chroot(ChrootType.ROOTFS, config.device)
info = pmb.parse.deviceinfo(args, device=args.device) info = pmb.parse.deviceinfo(device=config.device)
if "keymaps" not in info or info["keymaps"].strip() == "": if "keymaps" not in info or info["keymaps"].strip() == "":
logging.info("NOTE: No valid keymap specified for device") logging.info("NOTE: No valid keymap specified for device")
return return
options = info["keymaps"].split(' ') options = info["keymaps"].split(' ')
if (args.keymap != "" and if (config.keymap != "" and
args.keymap is not None and config.keymap is not None and
args.keymap in options): config.keymap in options):
layout, variant = args.keymap.split("/") layout, variant = config.keymap.split("/")
pmb.chroot.root(args, ["setup-keymap", layout, variant], chroot, pmb.chroot.root(["setup-keymap", layout, variant], chroot,
output="interactive") output="interactive")
# Check xorg config # Check xorg config
config = None xconfig = None
if (chroot / "etc/X11/xorg.conf.d").exists(): if (chroot / "etc/X11/xorg.conf.d").exists():
config = pmb.chroot.root(args, ["grep", "-rl", "XkbLayout", xconfig = pmb.chroot.root(["grep", "-rl", "XkbLayout",
"/etc/X11/xorg.conf.d/"], "/etc/X11/xorg.conf.d/"],
chroot, check=False, output_return=True) chroot, check=False, output_return=True)
if config: if xconfig:
# Nokia n900 (RX-51) randomly merges some keymaps so we # Nokia n900 (RX-51) randomly merges some keymaps so we
# have to specify a composite keymap for a few countries. See: # have to specify a composite keymap for a few countries. See:
# https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config/-/blob/master/symbols/nokia_vndr/rx-51 # https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config/-/blob/master/symbols/nokia_vndr/rx-51
@ -352,17 +354,17 @@ def setup_keymap(args: PmbArgs):
if variant == "rx51_pt" or variant == "rx51_es": if variant == "rx51_pt" or variant == "rx51_es":
layout = "ptes" layout = "ptes"
# Multiple files can contain the keyboard layout, take last # Multiple files can contain the keyboard layout, take last
config = config.splitlines()[-1] xconfig = xconfig.splitlines()[-1]
old_text = "Option *\\\"XkbLayout\\\" *\\\".*\\\"" old_text = "Option *\\\"XkbLayout\\\" *\\\".*\\\""
new_text = "Option \\\"XkbLayout\\\" \\\"" + layout + "\\\"" new_text = "Option \\\"XkbLayout\\\" \\\"" + layout + "\\\""
pmb.chroot.root(args, ["sed", "-i", "s/" + old_text + "/" + pmb.chroot.root(["sed", "-i", "s/" + old_text + "/" +
new_text + "/", config], chroot) new_text + "/", xconfig], chroot)
else: else:
logging.info("NOTE: No valid keymap specified for device") logging.info("NOTE: No valid keymap specified for device")
def setup_timezone(args: PmbArgs): def setup_timezone(config: Config):
suffix = Chroot(ChrootType.ROOTFS, args.device) suffix = Chroot(ChrootType.ROOTFS, config.device)
arch = args.deviceinfo["arch"] arch = args.deviceinfo["arch"]
alpine_conf = pmb.helpers.package.get(args, "alpine-conf", arch) alpine_conf = pmb.helpers.package.get(args, "alpine-conf", arch)
@ -378,7 +380,7 @@ def setup_timezone(args: PmbArgs):
if not pmb.parse.version.check_string(version, ">=3.14.0"): if not pmb.parse.version.check_string(version, ">=3.14.0"):
setup_tz_cmd += ["-z"] setup_tz_cmd += ["-z"]
setup_tz_cmd += [args.timezone] setup_tz_cmd += [args.timezone]
pmb.chroot.root(args, setup_tz_cmd, suffix) pmb.chroot.root(setup_tz_cmd, suffix)
def setup_hostname(args: PmbArgs): def setup_hostname(args: PmbArgs):
@ -402,12 +404,12 @@ def setup_hostname(args: PmbArgs):
suffix = Chroot(ChrootType.ROOTFS, args.device) suffix = Chroot(ChrootType.ROOTFS, args.device)
# Generate /etc/hostname # Generate /etc/hostname
pmb.chroot.root(args, ["sh", "-c", "echo " + shlex.quote(hostname) + pmb.chroot.root(["sh", "-c", "echo " + shlex.quote(hostname) +
" > /etc/hostname"], suffix) " > /etc/hostname"], suffix)
# Update /etc/hosts # Update /etc/hosts
regex = (r"s/^127\.0\.0\.1.*/127.0.0.1\t" + re.escape(hostname) + regex = (r"s/^127\.0\.0\.1.*/127.0.0.1\t" + re.escape(hostname) +
" localhost.localdomain localhost/") " localhost.localdomain localhost/")
pmb.chroot.root(args, ["sed", "-i", "-e", regex, "/etc/hosts"], suffix) pmb.chroot.root(["sed", "-i", "-e", regex, "/etc/hosts"], suffix)
def setup_appstream(args: PmbArgs): def setup_appstream(args: PmbArgs):
@ -421,10 +423,10 @@ def setup_appstream(args: PmbArgs):
if "alpine-appstream-downloader" not in installed_pkgs or args.offline: if "alpine-appstream-downloader" not in installed_pkgs or args.offline:
return return
if not pmb.chroot.root(args, ["alpine-appstream-downloader", if not pmb.chroot.root(["alpine-appstream-downloader",
"/mnt/appstream-data"], suffix, check=False): "/mnt/appstream-data"], suffix, check=False):
pmb.chroot.root(args, ["mkdir", "-p", "/var/lib/swcatalog"], suffix) pmb.chroot.root(["mkdir", "-p", "/var/lib/swcatalog"], suffix)
pmb.chroot.root(args, ["cp", "-r", "/mnt/appstream-data/icons", pmb.chroot.root(["cp", "-r", "/mnt/appstream-data/icons",
"/mnt/appstream-data/xml", "/mnt/appstream-data/xml",
"-t", "/var/lib/swcatalog"], suffix) "-t", "/var/lib/swcatalog"], suffix)
@ -435,7 +437,7 @@ def disable_sshd(args: PmbArgs):
# check=False: rc-update doesn't exit with 0 if already disabled # check=False: rc-update doesn't exit with 0 if already disabled
chroot = Chroot(ChrootType.ROOTFS, args.device) chroot = Chroot(ChrootType.ROOTFS, args.device)
pmb.chroot.root(args, ["rc-update", "del", "sshd", "default"], chroot, pmb.chroot.root(["rc-update", "del", "sshd", "default"], chroot,
check=False) check=False)
# Verify that it's gone # Verify that it's gone
@ -473,7 +475,7 @@ def disable_firewall(args: PmbArgs):
# check=False: rc-update doesn't exit with 0 if already disabled # check=False: rc-update doesn't exit with 0 if already disabled
chroot = Chroot(ChrootType.ROOTFS, args.device) chroot = Chroot(ChrootType.ROOTFS, args.device)
pmb.chroot.root(args, ["rc-update", "del", "nftables", "default"], chroot, pmb.chroot.root(["rc-update", "del", "nftables", "default"], chroot,
check=False) check=False)
# Verify that it's gone # Verify that it's gone
@ -485,7 +487,7 @@ def disable_firewall(args: PmbArgs):
def print_firewall_info(args: PmbArgs): def print_firewall_info(args: PmbArgs):
pmaports_cfg = pmb.config.pmaports.read_config(args) pmaports_cfg = pmb.config.pmaports.read_config()
pmaports_ok = pmaports_cfg.get("supported_firewall", None) == "nftables" pmaports_ok = pmaports_cfg.get("supported_firewall", None) == "nftables"
# Find kernel pmaport (will not be found if Alpine kernel is used) # Find kernel pmaport (will not be found if Alpine kernel is used)
@ -495,8 +497,7 @@ def print_firewall_info(args: PmbArgs):
arch = args.deviceinfo["arch"] arch = args.deviceinfo["arch"]
kernel = get_kernel_package(args, args.device) kernel = get_kernel_package(args, args.device)
if kernel: if kernel:
kernel_apkbuild = pmb.build._package.get_apkbuild(args, kernel[0], kernel_apkbuild = pmb.build._package.get_apkbuild(kernel[0], arch)
arch)
if kernel_apkbuild: if kernel_apkbuild:
opts = kernel_apkbuild["options"] opts = kernel_apkbuild["options"]
apkbuild_has_opt = "pmb:kconfigcheck-nftables" in opts apkbuild_has_opt = "pmb:kconfigcheck-nftables" in opts
@ -607,7 +608,7 @@ def embed_firmware(args: PmbArgs, suffix: Chroot):
logging.info("Embed firmware {} in the SD card image at offset {} with" logging.info("Embed firmware {} in the SD card image at offset {} with"
" step size {}".format(binary, offset, step)) " step size {}".format(binary, offset, step))
filename = os.path.join(device_rootfs, binary_file.lstrip("/")) filename = os.path.join(device_rootfs, binary_file.lstrip("/"))
pmb.chroot.root(args, ["dd", "if=" + filename, "of=/dev/install", pmb.chroot.root(["dd", "if=" + filename, "of=/dev/install",
"bs=" + str(step), "seek=" + str(offset)]) "bs=" + str(step), "seek=" + str(offset)])
@ -627,12 +628,13 @@ def write_cgpt_kpart(args: PmbArgs, layout, suffix: Chroot):
args, ["dd", f"if={filename}", f"of=/dev/installp{layout['kernel']}"]) args, ["dd", f"if={filename}", f"of=/dev/installp{layout['kernel']}"])
def sanity_check_boot_size(args: PmbArgs): def sanity_check_boot_size():
default = pmb.config.defaults["boot_size"] default = Config().boot_size
if isinstance(default, str) and int(args.boot_size) >= int(default): config = get_context().config
if int(config.boot_size) >= int(default):
return return
logging.error("ERROR: your pmbootstrap has a small/invalid boot_size of" logging.error("ERROR: your pmbootstrap has a small/invalid boot_size of"
f" {args.boot_size} configured, probably because the config" f" {config.boot_size} configured, probably because the config"
" has been created with an old version.") " has been created with an old version.")
logging.error("This can lead to problems later on, we recommend setting it" logging.error("This can lead to problems later on, we recommend setting it"
f" to {default} MiB.") f" to {default} MiB.")
@ -727,7 +729,6 @@ def get_uuid(args: PmbArgs, partition: Path):
:param partition: block device for getting UUID from :param partition: block device for getting UUID from
""" """
return pmb.chroot.root( return pmb.chroot.root(
args,
[ [
"blkid", "blkid",
"-s", "UUID", "-s", "UUID",
@ -751,7 +752,7 @@ def create_crypttab(args: PmbArgs, layout, chroot: Chroot):
crypttab = f"root UUID={luks_uuid} none luks\n" crypttab = f"root UUID={luks_uuid} none luks\n"
(chroot / "tmp/crypttab").open("w").write(crypttab) (chroot / "tmp/crypttab").open("w").write(crypttab)
pmb.chroot.root(args, ["mv", "/tmp/crypttab", "/etc/crypttab"], chroot) pmb.chroot.root(["mv", "/tmp/crypttab", "/etc/crypttab"], chroot)
def create_fstab(args: PmbArgs, layout, chroot: Chroot): def create_fstab(args: PmbArgs, layout, chroot: Chroot):
@ -804,7 +805,7 @@ def create_fstab(args: PmbArgs, layout, chroot: Chroot):
with (chroot / "tmp/fstab").open("w") as f: with (chroot / "tmp/fstab").open("w") as f:
f.write(fstab) f.write(fstab)
pmb.chroot.root(args, ["mv", "/tmp/fstab", "/etc/fstab"], chroot) pmb.chroot.root(["mv", "/tmp/fstab", "/etc/fstab"], chroot)
def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, steps, def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, steps,
@ -824,7 +825,7 @@ def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, step
# Partition and fill image file/disk block device # Partition and fill image file/disk block device
logging.info(f"*** ({step}/{steps}) PREPARE INSTALL BLOCKDEVICE ***") logging.info(f"*** ({step}/{steps}) PREPARE INSTALL BLOCKDEVICE ***")
pmb.chroot.shutdown(args, True) pmb.chroot.shutdown(args, True)
(size_boot, size_root) = get_subpartitions_size(args, chroot) (size_boot, size_root) = get_subpartitions_size(chroot)
layout = get_partition_layout(size_reserve, args.deviceinfo["cgpt_kpart"] \ layout = get_partition_layout(size_reserve, args.deviceinfo["cgpt_kpart"] \
and args.install_cgpt) and args.install_cgpt)
if not args.rsync: if not args.rsync:
@ -850,7 +851,7 @@ def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, step
# Run mkinitfs to pass UUIDs to cmdline # Run mkinitfs to pass UUIDs to cmdline
logging.info(f"({chroot}) mkinitfs") logging.info(f"({chroot}) mkinitfs")
pmb.chroot.root(args, ["mkinitfs"], chroot) pmb.chroot.root(["mkinitfs"], chroot)
# Clean up after running mkinitfs in chroot # Clean up after running mkinitfs in chroot
pmb.helpers.mount.umount_all(chroot.path) pmb.helpers.mount.umount_all(chroot.path)
@ -883,25 +884,25 @@ def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, step
if sparse and not split and not disk: if sparse and not split and not disk:
workdir = Path("/home/pmos/rootfs") workdir = Path("/home/pmos/rootfs")
logging.info("(native) make sparse rootfs") logging.info("(native) make sparse rootfs")
pmb.chroot.apk.install(args, ["android-tools"], Chroot.native()) pmb.chroot.apk.install(["android-tools"], Chroot.native())
sys_image = args.device + ".img" sys_image = args.device + ".img"
sys_image_sparse = args.device + "-sparse.img" sys_image_sparse = args.device + "-sparse.img"
pmb.chroot.user(args, ["img2simg", sys_image, sys_image_sparse], pmb.chroot.user(["img2simg", sys_image, sys_image_sparse],
working_dir=workdir) working_dir=workdir)
pmb.chroot.user(args, ["mv", "-f", sys_image_sparse, sys_image], pmb.chroot.user(["mv", "-f", sys_image_sparse, sys_image],
working_dir=workdir) working_dir=workdir)
# patch sparse image for Samsung devices if specified # patch sparse image for Samsung devices if specified
samsungify_strategy = args.deviceinfo["flash_sparse_samsung_format"] samsungify_strategy = args.deviceinfo["flash_sparse_samsung_format"]
if samsungify_strategy: if samsungify_strategy:
logging.info("(native) convert sparse image into Samsung's sparse image format") logging.info("(native) convert sparse image into Samsung's sparse image format")
pmb.chroot.apk.install(args, ["sm-sparse-image-tool"], Chroot.native()) pmb.chroot.apk.install(["sm-sparse-image-tool"], Chroot.native())
sys_image = f"{args.device}.img" sys_image = f"{args.device}.img"
sys_image_patched = f"{args.device}-patched.img" sys_image_patched = f"{args.device}-patched.img"
pmb.chroot.user(args, ["sm_sparse_image_tool", "samsungify", "--strategy", pmb.chroot.user(["sm_sparse_image_tool", "samsungify", "--strategy",
samsungify_strategy, sys_image, sys_image_patched], samsungify_strategy, sys_image, sys_image_patched],
working_dir=workdir) working_dir=workdir)
pmb.chroot.user(args, ["mv", "-f", sys_image_patched, sys_image], pmb.chroot.user(["mv", "-f", sys_image_patched, sys_image],
working_dir=workdir) working_dir=workdir)
@ -1016,10 +1017,10 @@ def install_on_device_installer(args: PmbArgs, step, steps):
packages = ([f"device-{args.device}", packages = ([f"device-{args.device}",
"postmarketos-ondev"] + "postmarketos-ondev"] +
get_kernel_package(args, args.device) + get_kernel_package(args, args.device) +
get_nonfree_packages(args, args.device)) get_nonfree_packages(args.device))
chroot_installer = Chroot(ChrootType.INSTALLER, args.device) chroot_installer = Chroot(ChrootType.INSTALLER, args.device)
pmb.chroot.apk.install(args, packages, chroot_installer) pmb.chroot.apk.install(packages, chroot_installer)
# Move rootfs image into installer chroot # Move rootfs image into installer chroot
img_path_dest = chroot_installer / "var/lib/rootfs.img" img_path_dest = chroot_installer / "var/lib/rootfs.img"
@ -1035,7 +1036,7 @@ def install_on_device_installer(args: PmbArgs, step, steps):
# file into another format. This can all be done without pmbootstrap # file into another format. This can all be done without pmbootstrap
# changes in the postmarketos-ondev package. # changes in the postmarketos-ondev package.
logging.info(f"({chroot_installer}) ondev-prepare") logging.info(f"({chroot_installer}) ondev-prepare")
channel = pmb.config.pmaports.read_config(args)["channel"] channel = pmb.config.pmaports.read_config()["channel"]
channel_cfg = pmb.config.pmaports.read_config_channel(args) channel_cfg = pmb.config.pmaports.read_config_channel(args)
env = {"ONDEV_CHANNEL": channel, env = {"ONDEV_CHANNEL": channel,
"ONDEV_CHANNEL_BRANCH_APORTS": channel_cfg["branch_aports"], "ONDEV_CHANNEL_BRANCH_APORTS": channel_cfg["branch_aports"],
@ -1045,7 +1046,7 @@ def install_on_device_installer(args: PmbArgs, step, steps):
"ONDEV_CIPHER": args.cipher, "ONDEV_CIPHER": args.cipher,
"ONDEV_PMBOOTSTRAP_VERSION": pmb.__version__, "ONDEV_PMBOOTSTRAP_VERSION": pmb.__version__,
"ONDEV_UI": args.ui} "ONDEV_UI": args.ui}
pmb.chroot.root(args, ["ondev-prepare"], chroot_installer, env=env) pmb.chroot.root(["ondev-prepare"], chroot_installer, env=env)
# Copy files specified with 'pmbootstrap install --ondev --cp' # Copy files specified with 'pmbootstrap install --ondev --cp'
if args.ondev_cp: if args.ondev_cp:
@ -1061,14 +1062,14 @@ def install_on_device_installer(args: PmbArgs, step, steps):
if not args.ondev_no_rootfs: if not args.ondev_no_rootfs:
img_boot = f"{args.device}-boot.img" img_boot = f"{args.device}-boot.img"
logging.info(f"(native) rm {img_boot}") logging.info(f"(native) rm {img_boot}")
pmb.chroot.root(args, ["rm", f"/home/pmos/rootfs/{img_boot}"]) pmb.chroot.root(["rm", f"/home/pmos/rootfs/{img_boot}"])
# Disable root login # Disable root login
setup_login(args, chroot_installer) setup_login(args, chroot_installer)
# Generate installer image # Generate installer image
size_reserve = round(os.path.getsize(img_path_dest) / 1024 / 1024) + 200 size_reserve = round(os.path.getsize(img_path_dest) / 1024 / 1024) + 200
pmaports_cfg = pmb.config.pmaports.read_config(args) pmaports_cfg = pmb.config.pmaports.read_config()
boot_label = pmaports_cfg.get("supported_install_boot_label", boot_label = pmaports_cfg.get("supported_install_boot_label",
"pmOS_inst_boot") "pmOS_inst_boot")
install_system_image(args, size_reserve, chroot_installer, step, steps, install_system_image(args, size_reserve, chroot_installer, step, steps,
@ -1100,12 +1101,12 @@ def get_selected_providers(args: PmbArgs, packages):
# aren't in pmaports. This is fine, with the assumption that # aren't in pmaports. This is fine, with the assumption that
# installation will fail later in some other method if they truly don't # installation will fail later in some other method if they truly don't
# exist in any repo. # exist in any repo.
apkbuild = pmb.helpers.pmaports.get(args, package, subpackages=False, must_exist=False) apkbuild = pmb.helpers.pmaports.get(package, subpackages=False, must_exist=False)
if not apkbuild: if not apkbuild:
continue continue
for select in apkbuild['_pmb_select']: for select in apkbuild['_pmb_select']:
if select in args.selected_providers: if select in get_context().config.providers:
ret += [args.selected_providers[select]] ret += [get_context().config.providers[select]]
logging.debug(f"{package}: install selected_providers:" logging.debug(f"{package}: install selected_providers:"
f" {', '.join(ret)}") f" {', '.join(ret)}")
# Also iterate through dependencies to collect any providers they have # Also iterate through dependencies to collect any providers they have
@ -1148,7 +1149,7 @@ def get_recommends(args: PmbArgs, packages) -> Sequence[str]:
# aren't in pmaports. This is fine, with the assumption that # aren't in pmaports. This is fine, with the assumption that
# installation will fail later in some other method if they truly don't # installation will fail later in some other method if they truly don't
# exist in any repo. # exist in any repo.
apkbuild = pmb.helpers.pmaports.get(args, package, must_exist=False) apkbuild = pmb.helpers.pmaports.get(package, must_exist=False)
if not apkbuild: if not apkbuild:
continue continue
if package in apkbuild["subpackages"]: if package in apkbuild["subpackages"]:
@ -1178,44 +1179,47 @@ def get_recommends(args: PmbArgs, packages) -> Sequence[str]:
def create_device_rootfs(args: PmbArgs, step, steps): def create_device_rootfs(args: PmbArgs, step, steps):
# List all packages to be installed (including the ones specified by --add) # List all packages to be installed (including the ones specified by --add)
# and upgrade the installed packages/apkindexes # and upgrade the installed packages/apkindexes
logging.info(f'*** ({step}/{steps}) CREATE DEVICE ROOTFS ("{args.device}")' context = get_context()
config = context.config
device = context.config.device
logging.info(f'*** ({step}/{steps}) CREATE DEVICE ROOTFS ("{device}")'
' ***') ' ***')
suffix = Chroot(ChrootType.ROOTFS, args.device) suffix = Chroot(ChrootType.ROOTFS, device)
pmb.chroot.init(args, suffix) pmb.chroot.init(suffix)
# Create user before installing packages, so post-install scripts of # Create user before installing packages, so post-install scripts of
# pmaports can figure out the username (legacy reasons: pmaports#820) # pmaports can figure out the username (legacy reasons: pmaports#820)
set_user(args) set_user(context.config)
# Fill install_packages # Fill install_packages
install_packages = (pmb.config.install_device_packages + install_packages = (pmb.config.install_device_packages +
["device-" + args.device]) ["device-" + device])
if not args.install_base: if not args.install_base:
install_packages = [p for p in install_packages install_packages = [p for p in install_packages
if p != "postmarketos-base"] if p != "postmarketos-base"]
if args.ui.lower() != "none": if config.ui.lower() != "none":
install_packages += ["postmarketos-ui-" + args.ui] install_packages += ["postmarketos-ui-" + config.ui]
if pmb.config.other.is_systemd_selected(args): if pmb.config.other.is_systemd_selected(context.config):
install_packages += ["postmarketos-base-systemd"] install_packages += ["postmarketos-base-systemd"]
# Add additional providers of base/device/UI package # Add additional providers of base/device/UI package
install_packages += get_selected_providers(args, install_packages) install_packages += get_selected_providers(args, install_packages)
install_packages += get_kernel_package(args, args.device) install_packages += get_kernel_package(args, device)
install_packages += get_nonfree_packages(args, args.device) install_packages += get_nonfree_packages(device)
if args.ui.lower() != "none": if context.config.ui.lower() != "none":
if args.ui_extras: if context.config.ui_extras:
install_packages += ["postmarketos-ui-" + args.ui + "-extras"] install_packages += ["postmarketos-ui-" + config.ui + "-extras"]
if args.extra_packages.lower() != "none": if context.config.extra_packages.lower() != "none":
install_packages += args.extra_packages.split(",") install_packages += context.config.extra_packages.split(",")
if args.add: if args.add:
install_packages += args.add.split(",") install_packages += args.add.split(",")
locale_is_set = (args.locale != pmb.config.defaults["locale"]) locale_is_set = (config.locale != pmb.config.defaults["locale"])
if locale_is_set: if locale_is_set:
install_packages += ["lang", "musl-locales"] install_packages += ["lang", "musl-locales"]
pmaports_cfg = pmb.config.pmaports.read_config(args) pmaports_cfg = pmb.config.pmaports.read_config()
# postmarketos-base supports a dummy package for blocking unl0kr install # postmarketos-base supports a dummy package for blocking unl0kr install
# when not required # when not required
if pmaports_cfg.get("supported_base_nofde", None): if pmaports_cfg.get("supported_base_nofde", None):
@ -1233,7 +1237,7 @@ def create_device_rootfs(args: PmbArgs, step, steps):
else: else:
install_packages += ["postmarketos-base-nofde"] install_packages += ["postmarketos-base-nofde"]
pmb.helpers.repo.update(args, args.deviceinfo["arch"]) pmb.helpers.repo.update(args.deviceinfo["arch"])
# Install uninstallable "dependencies" by default # Install uninstallable "dependencies" by default
install_packages += get_recommends(args, install_packages) install_packages += get_recommends(args, install_packages)
@ -1242,12 +1246,12 @@ def create_device_rootfs(args: PmbArgs, step, steps):
# dependency, in case the version increased # dependency, in case the version increased
if args.build_pkgs_on_install: if args.build_pkgs_on_install:
for pkgname in install_packages: for pkgname in install_packages:
pmb.build.package(args, pkgname, args.deviceinfo["arch"]) pmb.build.package(pkgname, args.deviceinfo["arch"])
# Install all packages to device rootfs chroot (and rebuild the initramfs, # Install all packages to device rootfs chroot (and rebuild the initramfs,
# because that doesn't always happen automatically yet, e.g. when the user # because that doesn't always happen automatically yet, e.g. when the user
# installed a hook without pmbootstrap - see #69 for more info) # installed a hook without pmbootstrap - see #69 for more info)
pmb.chroot.apk.install(args, install_packages, suffix) pmb.chroot.apk.install(install_packages, suffix)
flavor = pmb.chroot.other.kernel_flavor_installed(args, suffix) flavor = pmb.chroot.other.kernel_flavor_installed(args, suffix)
pmb.chroot.initfs.build(args, flavor, suffix) pmb.chroot.initfs.build(args, flavor, suffix)
@ -1266,7 +1270,7 @@ def create_device_rootfs(args: PmbArgs, step, steps):
# alpine-baselayout by /etc/profile. Since they don't override the # alpine-baselayout by /etc/profile. Since they don't override the
# locale if it exists, it warranties we have preference # locale if it exists, it warranties we have preference
line = f"export LANG=${{LANG:-{shlex.quote(args.locale)}}}" line = f"export LANG=${{LANG:-{shlex.quote(args.locale)}}}"
pmb.chroot.root(args, ["sh", "-c", f"echo {shlex.quote(line)}" pmb.chroot.root(["sh", "-c", f"echo {shlex.quote(line)}"
" > /etc/profile.d/10locale-pmos.sh"], suffix) " > /etc/profile.d/10locale-pmos.sh"], suffix)
# Set the hostname as the device name # Set the hostname as the device name
@ -1280,7 +1284,7 @@ def create_device_rootfs(args: PmbArgs, step, steps):
def install(args: PmbArgs): def install(args: PmbArgs):
# Sanity checks # Sanity checks
sanity_check_boot_size(args) sanity_check_boot_size()
if not args.android_recovery_zip and args.disk: if not args.android_recovery_zip and args.disk:
sanity_check_disk(args) sanity_check_disk(args)
sanity_check_disk_size(args) sanity_check_disk_size(args)
@ -1303,8 +1307,8 @@ def install(args: PmbArgs):
# Install required programs in native chroot # Install required programs in native chroot
step = 1 step = 1
logging.info(f"*** ({step}/{steps}) PREPARE NATIVE CHROOT ***") logging.info(f"*** ({step}/{steps}) PREPARE NATIVE CHROOT ***")
pmb.chroot.init(args, Chroot.native()) pmb.chroot.init(Chroot.native())
pmb.chroot.apk.install(args, pmb.config.install_native_packages, Chroot.native(), pmb.chroot.apk.install(pmb.config.install_native_packages, Chroot.native(),
build=False) build=False)
step += 1 step += 1

View file

@ -5,12 +5,12 @@ from pmb.helpers import logging
import os import os
import glob import glob
from pathlib import Path from pathlib import Path
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.mount import pmb.helpers.mount
import pmb.install.losetup import pmb.install.losetup
import pmb.helpers.cli import pmb.helpers.cli
import pmb.config import pmb.config
from pmb.core import Chroot from pmb.core import Chroot, get_context
def previous_install(args: PmbArgs, path: Path): def previous_install(args: PmbArgs, path: Path):
@ -28,7 +28,7 @@ def previous_install(args: PmbArgs, path: Path):
pmb.helpers.mount.bind_file(blockdevice_outside, pmb.helpers.mount.bind_file(blockdevice_outside,
Chroot.native() / blockdevice_inside) Chroot.native() / blockdevice_inside)
try: try:
label = pmb.chroot.root(args, ["blkid", "-s", "LABEL", label = pmb.chroot.root(["blkid", "-s", "LABEL",
"-o", "value", "-o", "value",
blockdevice_inside], blockdevice_inside],
output_return=True) output_return=True)
@ -87,19 +87,19 @@ def create_and_mount_image(args: PmbArgs, size_boot, size_root, size_reserve,
outside = chroot / img_path outside = chroot / img_path
if os.path.exists(outside): if os.path.exists(outside):
pmb.helpers.mount.umount_all(chroot / "mnt") pmb.helpers.mount.umount_all(chroot / "mnt")
pmb.install.losetup.umount(args, img_path) pmb.install.losetup.umount(img_path)
pmb.chroot.root(args, ["rm", img_path]) pmb.chroot.root(["rm", img_path])
# Make sure there is enough free space # Make sure there is enough free space
size_mb = round(size_boot + size_reserve + size_root) size_mb = round(size_boot + size_reserve + size_root)
disk_data = os.statvfs(pmb.config.work) disk_data = os.statvfs(get_context().config.work)
free = round((disk_data.f_bsize * disk_data.f_bavail) / (1024**2)) free = round((disk_data.f_bsize * disk_data.f_bavail) / (1024**2))
if size_mb > free: if size_mb > free:
raise RuntimeError("Not enough free space to create rootfs image! " raise RuntimeError("Not enough free space to create rootfs image! "
f"(free: {free}M, required: {size_mb}M)") f"(free: {free}M, required: {size_mb}M)")
# Create empty image files # Create empty image files
pmb.chroot.user(args, ["mkdir", "-p", "/home/pmos/rootfs"]) pmb.chroot.user(["mkdir", "-p", "/home/pmos/rootfs"])
size_mb_full = str(size_mb) + "M" size_mb_full = str(size_mb) + "M"
size_mb_boot = str(round(size_boot)) + "M" size_mb_boot = str(round(size_boot)) + "M"
size_mb_root = str(round(size_root)) + "M" size_mb_root = str(round(size_root)) + "M"
@ -110,7 +110,7 @@ def create_and_mount_image(args: PmbArgs, size_boot, size_root, size_reserve,
for img_path, size_mb in images.items(): for img_path, size_mb in images.items():
logging.info(f"(native) create {img_path.name} " logging.info(f"(native) create {img_path.name} "
f"({size_mb})") f"({size_mb})")
pmb.chroot.root(args, ["truncate", "-s", size_mb, img_path]) pmb.chroot.root(["truncate", "-s", size_mb, img_path])
# Mount to /dev/install # Mount to /dev/install
mount_image_paths = {img_path_full: "/dev/install"} mount_image_paths = {img_path_full: "/dev/install"}

View file

@ -3,7 +3,7 @@
from pmb.helpers import logging from pmb.helpers import logging
import pmb.chroot import pmb.chroot
from pmb.core import Chroot from pmb.core import Chroot
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
def install_fsprogs(args: PmbArgs, filesystem): def install_fsprogs(args: PmbArgs, filesystem):
@ -11,7 +11,7 @@ def install_fsprogs(args: PmbArgs, filesystem):
fsprogs = pmb.config.filesystems.get(filesystem) fsprogs = pmb.config.filesystems.get(filesystem)
if not fsprogs: if not fsprogs:
raise RuntimeError(f"Unsupported filesystem: {filesystem}") raise RuntimeError(f"Unsupported filesystem: {filesystem}")
pmb.chroot.apk.install(args, [fsprogs]) pmb.chroot.apk.install([fsprogs])
def format_and_mount_boot(args: PmbArgs, device, boot_label): def format_and_mount_boot(args: PmbArgs, device, boot_label):
@ -28,21 +28,21 @@ def format_and_mount_boot(args: PmbArgs, device, boot_label):
logging.info(f"(native) format {device} (boot, {filesystem}), mount to" logging.info(f"(native) format {device} (boot, {filesystem}), mount to"
f" {mountpoint}") f" {mountpoint}")
if filesystem == "fat16": if filesystem == "fat16":
pmb.chroot.root(args, ["mkfs.fat", "-F", "16", "-n", boot_label, pmb.chroot.root(["mkfs.fat", "-F", "16", "-n", boot_label,
device]) device])
elif filesystem == "fat32": elif filesystem == "fat32":
pmb.chroot.root(args, ["mkfs.fat", "-F", "32", "-n", boot_label, pmb.chroot.root(["mkfs.fat", "-F", "32", "-n", boot_label,
device]) device])
elif filesystem == "ext2": elif filesystem == "ext2":
pmb.chroot.root(args, ["mkfs.ext2", "-F", "-q", "-L", boot_label, pmb.chroot.root(["mkfs.ext2", "-F", "-q", "-L", boot_label,
device]) device])
elif filesystem == "btrfs": elif filesystem == "btrfs":
pmb.chroot.root(args, ["mkfs.btrfs", "-f", "-q", "-L", boot_label, pmb.chroot.root(["mkfs.btrfs", "-f", "-q", "-L", boot_label,
device]) device])
else: else:
raise RuntimeError("Filesystem " + filesystem + " is not supported!") raise RuntimeError("Filesystem " + filesystem + " is not supported!")
pmb.chroot.root(args, ["mkdir", "-p", mountpoint]) pmb.chroot.root(["mkdir", "-p", mountpoint])
pmb.chroot.root(args, ["mount", device, mountpoint]) pmb.chroot.root(["mount", device, mountpoint])
def format_luks_root(args: PmbArgs, device): def format_luks_root(args: PmbArgs, device):
@ -56,15 +56,15 @@ def format_luks_root(args: PmbArgs, device):
logging.info(" *** TYPE IN THE FULL DISK ENCRYPTION PASSWORD (TWICE!) ***") logging.info(" *** TYPE IN THE FULL DISK ENCRYPTION PASSWORD (TWICE!) ***")
# Avoid cryptsetup warning about missing locking directory # Avoid cryptsetup warning about missing locking directory
pmb.chroot.root(args, ["mkdir", "-p", "/run/cryptsetup"]) pmb.chroot.root(["mkdir", "-p", "/run/cryptsetup"])
pmb.chroot.root(args, ["cryptsetup", "luksFormat", pmb.chroot.root(["cryptsetup", "luksFormat",
"-q", "-q",
"--cipher", args.cipher, "--cipher", args.cipher,
"--iter-time", args.iter_time, "--iter-time", args.iter_time,
"--use-random", "--use-random",
device], output="interactive") device], output="interactive")
pmb.chroot.root(args, ["cryptsetup", "luksOpen", device, "pm_crypt"], pmb.chroot.root(["cryptsetup", "luksOpen", device, "pm_crypt"],
output="interactive") output="interactive")
if not (Chroot.native() / mountpoint).exists(): if not (Chroot.native() / mountpoint).exists():
@ -73,7 +73,7 @@ def format_luks_root(args: PmbArgs, device):
def get_root_filesystem(args: PmbArgs): def get_root_filesystem(args: PmbArgs):
ret = args.filesystem or args.deviceinfo["root_filesystem"] or "ext4" ret = args.filesystem or args.deviceinfo["root_filesystem"] or "ext4"
pmaports_cfg = pmb.config.pmaports.read_config(args) pmaports_cfg = pmb.config.pmaports.read_config()
supported = pmaports_cfg.get("supported_root_filesystems", "ext4") supported = pmaports_cfg.get("supported_root_filesystems", "ext4")
supported_list = supported.split(",") supported_list = supported.split(",")
@ -116,9 +116,9 @@ def prepare_btrfs_subvolumes(args: PmbArgs, device, mountpoint):
["btrfs", "subvol", "set-default", f"{mountpoint}/@"]) ["btrfs", "subvol", "set-default", f"{mountpoint}/@"])
# Make directories to mount subvols onto # Make directories to mount subvols onto
pmb.chroot.root(args, ["umount", mountpoint]) pmb.chroot.root(["umount", mountpoint])
pmb.chroot.root(args, ["mount", device, mountpoint]) pmb.chroot.root(["mount", device, mountpoint])
pmb.chroot.root(args, ["mkdir", pmb.chroot.root(["mkdir",
f"{mountpoint}/home", f"{mountpoint}/home",
f"{mountpoint}/root", f"{mountpoint}/root",
f"{mountpoint}/.snapshots", f"{mountpoint}/.snapshots",
@ -127,8 +127,8 @@ def prepare_btrfs_subvolumes(args: PmbArgs, device, mountpoint):
# snapshots contain sensitive information, # snapshots contain sensitive information,
# and should only be readable by root. # and should only be readable by root.
pmb.chroot.root(args, ["chmod", "700", f"{mountpoint}/root"]) pmb.chroot.root(["chmod", "700", f"{mountpoint}/root"])
pmb.chroot.root(args, ["chmod", "700", f"{mountpoint}/.snapshots"]) pmb.chroot.root(["chmod", "700", f"{mountpoint}/.snapshots"])
# Mount subvols # Mount subvols
pmb.chroot.root(args, pmb.chroot.root(args,
@ -149,7 +149,7 @@ def prepare_btrfs_subvolumes(args: PmbArgs, device, mountpoint):
# Disable CoW for /var, to avoid write multiplication # Disable CoW for /var, to avoid write multiplication
# and slowdown on databases, containers and VM images. # and slowdown on databases, containers and VM images.
pmb.chroot.root(args, ["chattr", "+C", f"{mountpoint}/var"]) pmb.chroot.root(["chattr", "+C", f"{mountpoint}/var"])
def format_and_mount_root(args: PmbArgs, device, root_label, disk): def format_and_mount_root(args: PmbArgs, device, root_label, disk):
@ -182,13 +182,13 @@ def format_and_mount_root(args: PmbArgs, device, root_label, disk):
install_fsprogs(args, filesystem) install_fsprogs(args, filesystem)
logging.info(f"(native) format {device} (root, {filesystem})") logging.info(f"(native) format {device} (root, {filesystem})")
pmb.chroot.root(args, mkfs_root_args + [device]) pmb.chroot.root(mkfs_root_args + [device])
# Mount # Mount
mountpoint = "/mnt/install" mountpoint = "/mnt/install"
logging.info("(native) mount " + device + " to " + mountpoint) logging.info("(native) mount " + device + " to " + mountpoint)
pmb.chroot.root(args, ["mkdir", "-p", mountpoint]) pmb.chroot.root(["mkdir", "-p", mountpoint])
pmb.chroot.root(args, ["mount", device, mountpoint]) pmb.chroot.root(["mount", device, mountpoint])
if not args.rsync and filesystem == "btrfs": if not args.rsync and filesystem == "btrfs":
# Make flat btrfs subvolume layout # Make flat btrfs subvolume layout

View file

@ -8,7 +8,7 @@ from pmb.helpers import logging
import os import os
import time import time
from pmb.core.types import PathString, PmbArgs from pmb.types import PathString, PmbArgs
import pmb.helpers.mount import pmb.helpers.mount
import pmb.helpers.run import pmb.helpers.run
import pmb.chroot import pmb.chroot
@ -46,7 +46,7 @@ def mount(args: PmbArgs, img_path: Path):
if sector_size: if sector_size:
losetup_cmd += ["-b", str(int(sector_size))] losetup_cmd += ["-b", str(int(sector_size))]
pmb.chroot.root(args, losetup_cmd, check=False) pmb.chroot.root(losetup_cmd, check=False)
try: try:
device_by_back_file(args, img_path) device_by_back_file(args, img_path)
return return
@ -57,13 +57,13 @@ def mount(args: PmbArgs, img_path: Path):
raise RuntimeError(f"Failed to mount loop device: {img_path}") raise RuntimeError(f"Failed to mount loop device: {img_path}")
def device_by_back_file(args: PmbArgs, back_file: Path) -> Path: def device_by_back_file(back_file: Path) -> Path:
""" """
Get the /dev/loopX device that points to a specific image file. Get the /dev/loopX device that points to a specific image file.
""" """
# Get list from losetup # Get list from losetup
losetup_output = pmb.chroot.root(args, ["losetup", "--json", "--list"], losetup_output = pmb.chroot.root(["losetup", "--json", "--list"],
output_return=True) output_return=True)
if not losetup_output: if not losetup_output:
raise RuntimeError("losetup failed") raise RuntimeError("losetup failed")
@ -76,14 +76,14 @@ def device_by_back_file(args: PmbArgs, back_file: Path) -> Path:
raise RuntimeError(f"Failed to find loop device for {back_file}") raise RuntimeError(f"Failed to find loop device for {back_file}")
def umount(args: PmbArgs, img_path: Path): def umount(img_path: Path):
""" """
:param img_path: Path to the img file inside native chroot. :param img_path: Path to the img file inside native chroot.
""" """
device: Path device: Path
try: try:
device = device_by_back_file(args, img_path) device = device_by_back_file(img_path)
except RuntimeError: except RuntimeError:
return return
logging.debug(f"(native) umount {device}") logging.debug(f"(native) umount {device}")
pmb.chroot.root(args, ["losetup", "-d", device]) pmb.chroot.root(["losetup", "-d", device])

View file

@ -7,7 +7,7 @@ import os
import time import time
import pmb.chroot import pmb.chroot
import pmb.config import pmb.config
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.install.losetup import pmb.install.losetup
from pmb.core import Chroot from pmb.core import Chroot
@ -114,7 +114,7 @@ def partition(args: PmbArgs, layout, size_boot, size_reserve):
commands += [["set", str(layout["boot"]), "esp", "on"]] commands += [["set", str(layout["boot"]), "esp", "on"]]
for command in commands: for command in commands:
pmb.chroot.root(args, ["parted", "-s", "/dev/install"] + pmb.chroot.root(["parted", "-s", "/dev/install"] +
command, check=False) command, check=False)
@ -128,7 +128,7 @@ def partition_cgpt(args: PmbArgs, layout, size_boot, size_reserve):
:param size_reserve: empty partition between root and boot in MiB (pma#463) :param size_reserve: empty partition between root and boot in MiB (pma#463)
""" """
pmb.chroot.apk.install(args, ["cgpt"], build=False) pmb.chroot.apk.install(["cgpt"], build=False)
cgpt = { cgpt = {
'kpart_start': args.deviceinfo["cgpt_kpart_start"], 'kpart_start': args.deviceinfo["cgpt_kpart_start"],
@ -196,4 +196,4 @@ def partition_cgpt(args: PmbArgs, layout, size_boot, size_reserve):
] ]
for command in commands: for command in commands:
pmb.chroot.root(args, command, check=False) pmb.chroot.root(command, check=False)

View file

@ -5,7 +5,7 @@ from pmb.helpers import logging
import pmb.chroot import pmb.chroot
import pmb.config.pmaports import pmb.config.pmaports
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.flasher import pmb.flasher
import pmb.helpers.frontend import pmb.helpers.frontend
@ -45,7 +45,7 @@ def create_zip(args: PmbArgs, suffix):
} }
# Backwards compatibility with old mkinitfs (pma#660) # Backwards compatibility with old mkinitfs (pma#660)
pmaports_cfg = pmb.config.pmaports.read_config(args) pmaports_cfg = pmb.config.pmaports.read_config()
if pmaports_cfg.get("supported_mkinitfs_without_flavors", False): if pmaports_cfg.get("supported_mkinitfs_without_flavors", False):
options["FLAVOR"] = "" options["FLAVOR"] = ""
else: else:
@ -71,4 +71,4 @@ def create_zip(args: PmbArgs, suffix):
["gzip", "-f1", "rootfs.tar"], ["gzip", "-f1", "rootfs.tar"],
["build-recovery-zip", args.device]] ["build-recovery-zip", args.device]]
for command in commands: for command in commands:
pmb.chroot.root(args, command, suffix, working_dir=zip_root) pmb.chroot.root(command, suffix, working_dir=zip_root)

View file

@ -3,23 +3,23 @@
from typing import List from typing import List
from pmb.helpers import logging from pmb.helpers import logging
from pmb.core.types import PmbArgs from pmb.types import Config
import pmb.helpers.pmaports import pmb.helpers.pmaports
def get_groups(args: PmbArgs) -> List[str]: def get_groups(config: Config) -> List[str]:
""" Get all groups to which the user additionally must be added. """ Get all groups to which the user additionally must be added.
The list of groups are listed in _pmb_groups of the UI and The list of groups are listed in _pmb_groups of the UI and
UI-extras package. UI-extras package.
:returns: list of groups, e.g. ["feedbackd", "udev"] """ :returns: list of groups, e.g. ["feedbackd", "udev"] """
ret: List[str] = [] ret: List[str] = []
if args.ui == "none": if config.ui == "none":
return ret return ret
# UI package # UI package
meta = f"postmarketos-ui-{args.ui}" meta = f"postmarketos-ui-{config.ui}"
apkbuild = pmb.helpers.pmaports.get(args, meta) apkbuild = pmb.helpers.pmaports.get(meta)
groups = apkbuild["_pmb_groups"] groups = apkbuild["_pmb_groups"]
if groups: if groups:
logging.debug(f"{meta}: install _pmb_groups:" logging.debug(f"{meta}: install _pmb_groups:"
@ -28,7 +28,7 @@ def get_groups(args: PmbArgs) -> List[str]:
# UI-extras subpackage # UI-extras subpackage
meta_extras = f"{meta}-extras" meta_extras = f"{meta}-extras"
if args.ui_extras and meta_extras in apkbuild["subpackages"]: if config.ui_extras and meta_extras in apkbuild["subpackages"]:
groups = apkbuild["subpackages"][meta_extras]["_pmb_groups"] groups = apkbuild["subpackages"][meta_extras]["_pmb_groups"]
if groups: if groups:
logging.debug(f"{meta_extras}: install _pmb_groups:" logging.debug(f"{meta_extras}: install _pmb_groups:"

View file

@ -6,7 +6,7 @@ import socket
import time import time
import pmb.chroot.run import pmb.chroot.run
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.run import pmb.helpers.run
from pmb.core import Chroot from pmb.core import Chroot
@ -18,7 +18,7 @@ def start_nbd_server(args: PmbArgs, ip="172.16.42.2", port=9999):
:param port: port of nbd server :param port: port of nbd server
""" """
pmb.chroot.apk.install(args, ['nbd']) pmb.chroot.apk.install(['nbd'])
chroot = Chroot.native() chroot = Chroot.native()
@ -34,7 +34,7 @@ def start_nbd_server(args: PmbArgs, ip="172.16.42.2", port=9999):
f"{args.device}?"): f"{args.device}?"):
return return
pmb.chroot.run(args, ["cp", rootfs_path2, rootfs_path]) pmb.chroot.run(args, ["cp", rootfs_path2, rootfs_path])
logging.info(f"NOTE: Copied device image to {pmb.config.work}" logging.info(f"NOTE: Copied device image to {get_context().config.work}"
f"/images_netboot/. The image will persist \"pmbootstrap " f"/images_netboot/. The image will persist \"pmbootstrap "
f"zap\" for your convenience. Use \"pmbootstrap netboot " f"zap\" for your convenience. Use \"pmbootstrap netboot "
f"serve --help\" for more options.") f"serve --help\" for more options.")

View file

@ -8,7 +8,7 @@ import re
from collections import OrderedDict from collections import OrderedDict
import pmb.config import pmb.config
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.devices import pmb.helpers.devices
import pmb.parse.version import pmb.parse.version
@ -330,7 +330,7 @@ def apkbuild(path: Path, check_pkgver=True, check_pkgname=True):
path = path / "APKBUILD" path = path / "APKBUILD"
if not path.exists(): if not path.exists():
raise FileNotFoundError(f"{path.relative_to(pmb.config.work)} not found!") raise FileNotFoundError(f"{path.relative_to(get_context().config.work)} not found!")
# Try to get a cached result first (we assume that the aports don't change # Try to get a cached result first (we assume that the aports don't change
# in one pmbootstrap call) # in one pmbootstrap call)
@ -367,7 +367,7 @@ def apkbuild(path: Path, check_pkgver=True, check_pkgname=True):
return ret return ret
def kernels(args: PmbArgs, device): def kernels(device: str):
""" """
Get the possible kernels from a device-* APKBUILD. Get the possible kernels from a device-* APKBUILD.
@ -379,7 +379,7 @@ def kernels(args: PmbArgs, device):
"downstream": "Downstream description"} "downstream": "Downstream description"}
""" """
# Read the APKBUILD # Read the APKBUILD
apkbuild_path = pmb.helpers.devices.find_path(args, device, 'APKBUILD') apkbuild_path = pmb.helpers.devices.find_path(device, 'APKBUILD')
if apkbuild_path is None: if apkbuild_path is None:
return None return None
subpackages = apkbuild(apkbuild_path)["subpackages"] subpackages = apkbuild(apkbuild_path)["subpackages"]

View file

@ -6,7 +6,6 @@ from pmb.helpers import logging
from pathlib import Path from pathlib import Path
import tarfile import tarfile
import pmb.chroot.apk import pmb.chroot.apk
from pmb.core.types import PmbArgs
import pmb.helpers.package import pmb.helpers.package
import pmb.helpers.repo import pmb.helpers.repo
import pmb.parse.version import pmb.parse.version
@ -261,7 +260,7 @@ def clear_cache(path: Path):
return False return False
def providers(args: PmbArgs, package, arch=None, must_exist=True, indexes=None): def providers(package, arch=None, must_exist=True, indexes=None):
""" """
Get all packages, which provide one package. Get all packages, which provide one package.
@ -277,7 +276,7 @@ def providers(args: PmbArgs, package, arch=None, must_exist=True, indexes=None):
""" """
if not indexes: if not indexes:
arch = arch or pmb.config.arch_native arch = arch or pmb.config.arch_native
indexes = pmb.helpers.repo.apkindex_files(args, arch) indexes = pmb.helpers.repo.apkindex_files(arch)
package = pmb.helpers.package.remove_operators(package) package = pmb.helpers.package.remove_operators(package)
@ -353,7 +352,7 @@ def provider_shortest(providers, pkgname):
return providers[ret] return providers[ret]
def package(args: PmbArgs, package, arch=None, must_exist=True, indexes=None): def package(package, arch=None, must_exist=True, indexes=None):
""" """
Get a specific package's data from an apkindex. Get a specific package's data from an apkindex.
@ -372,7 +371,7 @@ def package(args: PmbArgs, package, arch=None, must_exist=True, indexes=None):
or None when the package was not found. or None when the package was not found.
""" """
# Provider with the same package # Provider with the same package
package_providers = providers(args, package, arch, must_exist, indexes) package_providers = providers(package, arch, must_exist, indexes)
if package in package_providers: if package in package_providers:
return package_providers[package] return package_providers[package]

View file

@ -5,9 +5,6 @@ import platform
import pmb.config import pmb.config
import pmb.parse.arch import pmb.parse.arch
from pmb.core.types import PmbArgs
from pmb.core import Chroot, ChrootType
def alpine_native(): def alpine_native():
machine = platform.machine() machine = platform.machine()

View file

@ -6,7 +6,7 @@ import os
from pathlib import Path from pathlib import Path
import sys import sys
from pmb.core.types import PmbArgs from pmb.types import Config, PmbArgs
try: try:
import argcomplete # type:ignore[import-untyped] import argcomplete # type:ignore[import-untyped]
@ -637,7 +637,8 @@ def get_parser():
parser = argparse.ArgumentParser(prog="pmbootstrap") parser = argparse.ArgumentParser(prog="pmbootstrap")
arch_native = pmb.config.arch_native arch_native = pmb.config.arch_native
arch_choices = set(pmb.config.build_device_architectures + [arch_native]) arch_choices = set(pmb.config.build_device_architectures + [arch_native])
mirrors_pmos_default = pmb.config.defaults["mirrors_postmarketos"] default_config = Config()
mirrors_pmos_default = ",".join(default_config.mirrors_postmarketos)
# Other # Other
parser.add_argument("-V", "--version", action="version", parser.add_argument("-V", "--version", action="version",
@ -653,7 +654,7 @@ def get_parser():
metavar="URL", action="append", default=[]) metavar="URL", action="append", default=[])
parser.add_argument("-m", "--mirror-alpine", dest="mirror_alpine", parser.add_argument("-m", "--mirror-alpine", dest="mirror_alpine",
help="Alpine Linux mirror, default: " help="Alpine Linux mirror, default: "
f"{pmb.config.defaults['mirror_alpine']}", f"{default_config.mirror_alpine}",
metavar="URL") metavar="URL")
parser.add_argument("-j", "--jobs", help="parallel jobs when compiling") parser.add_argument("-j", "--jobs", help="parallel jobs when compiling")
parser.add_argument("-E", "--extra-space", parser.add_argument("-E", "--extra-space",

View file

@ -3,7 +3,7 @@
import os import os
from pmb.helpers import logging from pmb.helpers import logging
from pathlib import Path from pathlib import Path
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.run import pmb.helpers.run
import pmb.chroot.run import pmb.chroot.run
import pmb.chroot.other import pmb.chroot.other
@ -79,7 +79,7 @@ def bootimg(args: PmbArgs, path: Path):
logging.info("NOTE: You will be prompted for your sudo/doas password, so" logging.info("NOTE: You will be prompted for your sudo/doas password, so"
" we can set up a chroot to extract and analyze your" " we can set up a chroot to extract and analyze your"
" boot.img file") " boot.img file")
pmb.chroot.apk.install(args, ["file", "unpackbootimg"], Chroot.native()) pmb.chroot.apk.install(["file", "unpackbootimg"], Chroot.native())
temp_path = pmb.chroot.other.tempfolder(args, Path("/tmp/bootimg_parser")) temp_path = pmb.chroot.other.tempfolder(args, Path("/tmp/bootimg_parser"))
bootimg_path = Chroot.native() / temp_path / "boot.img" bootimg_path = Chroot.native() / temp_path / "boot.img"
@ -89,7 +89,7 @@ def bootimg(args: PmbArgs, path: Path):
pmb.helpers.run.root(["cp", path, bootimg_path]) pmb.helpers.run.root(["cp", path, bootimg_path])
pmb.helpers.run.root(["chmod", "a+r", bootimg_path]) pmb.helpers.run.root(["chmod", "a+r", bootimg_path])
file_output = pmb.chroot.user(args, ["file", "-b", "boot.img"], file_output = pmb.chroot.user(["file", "-b", "boot.img"],
working_dir=temp_path, working_dir=temp_path,
output_return=True).rstrip() output_return=True).rstrip()
if "android bootimg" not in file_output.lower(): if "android bootimg" not in file_output.lower():
@ -111,7 +111,7 @@ def bootimg(args: PmbArgs, path: Path):
file_output + ")") file_output + ")")
# Extract all the files # Extract all the files
pmb.chroot.user(args, ["unpackbootimg", "-i", "boot.img"], pmb.chroot.user(["unpackbootimg", "-i", "boot.img"],
working_dir=temp_path) working_dir=temp_path)
output = {} output = {}

View file

@ -4,20 +4,20 @@ from typing import Dict, List, Sequence, Set
from pmb.helpers import logging from pmb.helpers import logging
import pmb.chroot import pmb.chroot
import pmb.chroot.apk import pmb.chroot.apk
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.pmaports import pmb.helpers.pmaports
import pmb.parse.apkindex import pmb.parse.apkindex
import pmb.parse.arch import pmb.parse.arch
from pmb.core import Chroot from pmb.core import Chroot, get_context
def package_from_aports(args: PmbArgs, pkgname_depend): def package_from_aports(pkgname_depend):
""" """
:returns: None when there is no aport, or a dict with the keys pkgname, :returns: None when there is no aport, or a dict with the keys pkgname,
depends, version. The version is the combined pkgver and pkgrel. depends, version. The version is the combined pkgver and pkgrel.
""" """
# Get the aport # Get the aport
aport = pmb.helpers.pmaports.find_optional(args, pkgname_depend) aport = pmb.helpers.pmaports.find_optional(pkgname_depend)
if not aport: if not aport:
return None return None
@ -34,7 +34,7 @@ def package_from_aports(args: PmbArgs, pkgname_depend):
"version": version} "version": version}
def package_provider(args: PmbArgs, pkgname, pkgnames_install, suffix: Chroot=Chroot.native()): def package_provider(pkgname, pkgnames_install, suffix: Chroot=Chroot.native()):
""" """
:param pkgnames_install: packages to be installed :param pkgnames_install: packages to be installed
:returns: a block from the apkindex: {"pkgname": "...", ...} :returns: a block from the apkindex: {"pkgname": "...", ...}
@ -42,7 +42,7 @@ def package_provider(args: PmbArgs, pkgname, pkgnames_install, suffix: Chroot=Ch
""" """
# Get all providers # Get all providers
arch = suffix.arch arch = suffix.arch
providers = pmb.parse.apkindex.providers(args, pkgname, arch, False) providers = pmb.parse.apkindex.providers(pkgname, arch, False)
# 0. No provider # 0. No provider
if len(providers) == 0: if len(providers) == 0:
@ -67,7 +67,7 @@ def package_provider(args: PmbArgs, pkgname, pkgnames_install, suffix: Chroot=Ch
return provider return provider
# 4. Pick a package that is already installed # 4. Pick a package that is already installed
installed = pmb.chroot.apk.installed(args, suffix) installed = pmb.chroot.apk.installed(suffix)
for provider_pkgname, provider in providers.items(): for provider_pkgname, provider in providers.items():
if provider_pkgname in installed: if provider_pkgname in installed:
logging.verbose(f"{pkgname}: choosing provider '{provider_pkgname}" logging.verbose(f"{pkgname}: choosing provider '{provider_pkgname}"
@ -76,7 +76,7 @@ def package_provider(args: PmbArgs, pkgname, pkgnames_install, suffix: Chroot=Ch
return provider return provider
# 5. Pick an explicitly selected provider # 5. Pick an explicitly selected provider
provider_pkgname = args.selected_providers.get(pkgname, "") provider_pkgname = get_context().config.providers.get(pkgname, "")
if provider_pkgname in providers: if provider_pkgname in providers:
logging.verbose(f"{pkgname}: choosing provider '{provider_pkgname}', " logging.verbose(f"{pkgname}: choosing provider '{provider_pkgname}', "
"because it was explicitly selected.") "because it was explicitly selected.")
@ -92,7 +92,7 @@ def package_provider(args: PmbArgs, pkgname, pkgnames_install, suffix: Chroot=Ch
return pmb.parse.apkindex.provider_shortest(providers, pkgname) return pmb.parse.apkindex.provider_shortest(providers, pkgname)
def package_from_index(args: PmbArgs, pkgname_depend, pkgnames_install, package_aport, def package_from_index(pkgname_depend, pkgnames_install, package_aport,
suffix: Chroot=Chroot.native()): suffix: Chroot=Chroot.native()):
""" """
:returns: None when there is no aport and no binary package, or a dict with :returns: None when there is no aport and no binary package, or a dict with
@ -100,7 +100,7 @@ def package_from_index(args: PmbArgs, pkgname_depend, pkgnames_install, package_
binary package provider. binary package provider.
""" """
# No binary package # No binary package
provider = package_provider(args, pkgname_depend, pkgnames_install, suffix) provider = package_provider(pkgname_depend, pkgnames_install, suffix)
if not provider: if not provider:
return package_aport return package_aport
@ -118,7 +118,7 @@ def package_from_index(args: PmbArgs, pkgname_depend, pkgnames_install, package_
return provider return provider
def recurse(args: PmbArgs, pkgnames, suffix: Chroot=Chroot.native()) -> Sequence[str]: def recurse(pkgnames, suffix: Chroot=Chroot.native()) -> Sequence[str]:
""" """
Find all dependencies of the given pkgnames. Find all dependencies of the given pkgnames.
@ -148,8 +148,8 @@ def recurse(args: PmbArgs, pkgnames, suffix: Chroot=Chroot.native()) -> Sequence
# Get depends and pkgname from aports # Get depends and pkgname from aports
pkgnames_install = list(ret) + todo pkgnames_install = list(ret) + todo
package = package_from_aports(args, pkgname_depend) package = package_from_aports(pkgname_depend)
package = package_from_index(args, pkgname_depend, pkgnames_install, package = package_from_index(pkgname_depend, pkgnames_install,
package, suffix) package, suffix)
# Nothing found # Nothing found

View file

@ -2,10 +2,11 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import copy import copy
from typing import Dict from typing import Dict
from pmb.core import get_context
from pmb.helpers import logging from pmb.helpers import logging
import os import os
import pmb.config import pmb.config
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.devices import pmb.helpers.devices
@ -72,7 +73,7 @@ def sanity_check(info, path):
f" and try again: {path}") f" and try again: {path}")
def _parse_kernel_suffix(args: PmbArgs, info, device, kernel): def _parse_kernel_suffix(info, device, kernel):
""" """
Remove the kernel suffix (as selected in 'pmbootstrap init') from Remove the kernel suffix (as selected in 'pmbootstrap init') from
deviceinfo variables. Related: deviceinfo variables. Related:
@ -92,7 +93,7 @@ def _parse_kernel_suffix(args: PmbArgs, info, device, kernel):
# Do nothing if the configured kernel isn't available in the kernel (e.g. # Do nothing if the configured kernel isn't available in the kernel (e.g.
# after switching from device with multiple kernels to device with only one # after switching from device with multiple kernels to device with only one
# kernel) # kernel)
kernels = pmb.parse._apkbuild.kernels(args, device) kernels = pmb.parse._apkbuild.kernels(device)
if not kernels or kernel not in kernels: if not kernels or kernel not in kernels:
logging.verbose(f"parse_kernel_suffix: {kernel} not in {kernels}") logging.verbose(f"parse_kernel_suffix: {kernel} not in {kernels}")
return info return info
@ -114,23 +115,25 @@ def _parse_kernel_suffix(args: PmbArgs, info, device, kernel):
# FIXME (#2324): Make deviceinfo a type! (class!!!) # FIXME (#2324): Make deviceinfo a type! (class!!!)
def deviceinfo(args: PmbArgs, device=None, kernel=None) -> Dict[str, str]: def deviceinfo(device=None, kernel=None) -> Dict[str, str]:
""" """
:param device: defaults to args.device :param device: defaults to args.device
:param kernel: defaults to args.kernel :param kernel: defaults to args.kernel
""" """
context = get_context()
if not device: if not device:
device = args.device device = context.config.device
if not kernel: if not kernel:
kernel = args.kernel kernel = context.config.kernel
if not os.path.exists(args.aports): aports = context.config.aports
logging.fatal(f"Aports directory is missing, expected: {args.aports}") if not aports.exists():
logging.fatal(f"Aports directory is missing, expected: {aports}")
logging.fatal("Please provide a path to the aports directory using the" logging.fatal("Please provide a path to the aports directory using the"
" -p flag") " -p flag")
raise RuntimeError("Aports directory missing") raise RuntimeError("Aports directory missing")
path = pmb.helpers.devices.find_path(args, device, 'deviceinfo') path = pmb.helpers.devices.find_path(device, 'deviceinfo')
if not path: if not path:
raise RuntimeError( raise RuntimeError(
"Device '" + device + "' not found. Run 'pmbootstrap init' to" "Device '" + device + "' not found. Run 'pmbootstrap init' to"
@ -154,6 +157,6 @@ def deviceinfo(args: PmbArgs, device=None, kernel=None) -> Dict[str, str]:
if key not in ret: if key not in ret:
ret[key] = "" ret[key] = ""
ret = _parse_kernel_suffix(args, ret, device, kernel) ret = _parse_kernel_suffix(ret, device, kernel)
sanity_check(ret, path) sanity_check(ret, path)
return ret return ret

View file

@ -7,7 +7,7 @@ import os
import pmb.build import pmb.build
import pmb.config import pmb.config
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.parse import pmb.parse
import pmb.helpers.pmaports import pmb.helpers.pmaports
from pmb.helpers.exceptions import NonBugError from pmb.helpers.exceptions import NonBugError
@ -255,7 +255,7 @@ def check(args: PmbArgs, pkgname, components_list=[], details=False, must_exist=
ret = True ret = True
aport: Path aport: Path
try: try:
aport = pmb.helpers.pmaports.find(args, "linux-" + flavor) aport = pmb.helpers.pmaports.find("linux-" + flavor)
except RuntimeError as e: except RuntimeError as e:
if must_exist: if must_exist:
raise e raise e

View file

@ -17,7 +17,7 @@ import pmb.chroot.other
import pmb.chroot.initfs import pmb.chroot.initfs
import pmb.config import pmb.config
import pmb.config.pmaports import pmb.config.pmaports
from pmb.core.types import PathString, PmbArgs from pmb.types import PathString, PmbArgs
import pmb.helpers.run import pmb.helpers.run
import pmb.parse.arch import pmb.parse.arch
import pmb.parse.cpuinfo import pmb.parse.cpuinfo
@ -79,11 +79,11 @@ def create_gdk_loader_cache(args: PmbArgs) -> Path:
if not (chroot_native / cache_path).is_file(): if not (chroot_native / cache_path).is_file():
raise RuntimeError(f"gdk pixbuf cache file not found: {cache_path}") raise RuntimeError(f"gdk pixbuf cache file not found: {cache_path}")
pmb.chroot.root(args, ["cp", cache_path, custom_cache_path]) pmb.chroot.root(["cp", cache_path, custom_cache_path])
cmd: Sequence[PathString] = ["sed", "-i", "-e", cmd: Sequence[PathString] = ["sed", "-i", "-e",
f"s@\"{gdk_cache_dir}@\"{chroot_native / gdk_cache_dir}@", f"s@\"{gdk_cache_dir}@\"{chroot_native / gdk_cache_dir}@",
custom_cache_path] custom_cache_path]
pmb.chroot.root(args, cmd) pmb.chroot.root(cmd)
return chroot_native / custom_cache_path return chroot_native / custom_cache_path
@ -107,7 +107,7 @@ def command_qemu(args: PmbArgs, arch, img_path, img_path_2nd=None):
flavor = pmb.chroot.other.kernel_flavor_installed(args, chroot) flavor = pmb.chroot.other.kernel_flavor_installed(args, chroot)
flavor_suffix = f"-{flavor}" flavor_suffix = f"-{flavor}"
# Backwards compatibility with old mkinitfs (pma#660) # Backwards compatibility with old mkinitfs (pma#660)
pmaports_cfg = pmb.config.pmaports.read_config(args) pmaports_cfg = pmb.config.pmaports.read_config()
if pmaports_cfg.get("supported_mkinitfs_without_flavors", False): if pmaports_cfg.get("supported_mkinitfs_without_flavors", False):
flavor_suffix = "" flavor_suffix = ""
@ -325,7 +325,7 @@ def install_depends(args: PmbArgs, arch):
if args.efi: if args.efi:
depends.append("ovmf") depends.append("ovmf")
pmb.chroot.apk.install(args, depends, Chroot.native()) pmb.chroot.apk.install(depends, Chroot.native())
def run(args: PmbArgs): def run(args: PmbArgs):

View file

@ -5,13 +5,14 @@ from typing import List
from pmb.helpers import logging from pmb.helpers import logging
import shlex import shlex
from pmb.core.types import PathString, PmbArgs from pmb.types import PathString, PmbArgs
import pmb.helpers.run import pmb.helpers.run
import pmb.helpers.run_core import pmb.helpers.run_core
import pmb.parse.apkindex import pmb.parse.apkindex
import pmb.parse.arch import pmb.parse.arch
import pmb.config.pmaports import pmb.config.pmaports
import pmb.build import pmb.build
from pmb.core import get_context
def scp_abuild_key(args: PmbArgs, user: str, host: str, port: str): def scp_abuild_key(args: PmbArgs, user: str, host: str, port: str):
@ -21,7 +22,7 @@ def scp_abuild_key(args: PmbArgs, user: str, host: str, port: str):
:param host: target device ssh hostname :param host: target device ssh hostname
:param port: target device ssh port """ :param port: target device ssh port """
keys = list((pmb.config.work / "config_abuild").glob("*.pub")) keys = list((get_context().config.work / "config_abuild").glob("*.pub"))
key = keys[0] key = keys[0]
key_name = os.path.basename(key) key_name = os.path.basename(key)
@ -91,17 +92,18 @@ def sideload(args: PmbArgs, user: str, host: str, port: str, arch: str, copy_key
:param pkgnames: list of pkgnames to be built """ :param pkgnames: list of pkgnames to be built """
paths = [] paths = []
channel: str = pmb.config.pmaports.read_config(args)["channel"] channel: str = pmb.config.pmaports.read_config()["channel"]
if arch is None: if arch is None:
arch = ssh_find_arch(args, user, host, port) arch = ssh_find_arch(args, user, host, port)
context = get_context()
for pkgname in pkgnames: for pkgname in pkgnames:
data_repo = pmb.parse.apkindex.package(args, pkgname, arch, True) data_repo = pmb.parse.apkindex.package(pkgname, arch, True)
apk_file = f"{pkgname}-{data_repo['version']}.apk" apk_file = f"{pkgname}-{data_repo['version']}.apk"
host_path = pmb.config.work / "packages" / channel / arch / apk_file host_path = context.config.work / "packages" / channel / arch / apk_file
if not host_path.is_file(): if not host_path.is_file():
pmb.build.package(args, pkgname, arch, force=True) pmb.build.package(context, pkgname, arch, force=True)
if not host_path.is_file(): if not host_path.is_file():
raise RuntimeError(f"The package '{pkgname}' could not be built") raise RuntimeError(f"The package '{pkgname}' could not be built")

View file

@ -2,6 +2,8 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from argparse import Namespace from argparse import Namespace
import multiprocessing
import os
from pathlib import Path from pathlib import Path
from typing import Dict, List, Optional, Tuple, TypedDict, Union from typing import Dict, List, Optional, Tuple, TypedDict, Union
@ -24,7 +26,7 @@ class AportGenEntry(TypedDict):
# Property list generated with: # Property list generated with:
# $ rg --vimgrep "((^|\s)args\.\w+)" --only-matching | cut -d"." -f3 | sort | uniq # $ rg --vimgrep "((^|\s)args\.\w+)" --only-matching | cut -d"." -f3 | sort | uniq
class PmbArgs(Namespace): class PmbArgs():
action_flasher: str action_flasher: str
action_initfs: str action_initfs: str
action_kconfig: str action_kconfig: str
@ -52,9 +54,9 @@ class PmbArgs(Namespace):
cmdline: str cmdline: str
command: str command: str
config: Path config: Path
cross: bool
details: bool details: bool
details_to_stdout: bool details_to_stdout: bool
device: str
deviceinfo: Dict[str, str] deviceinfo: Dict[str, str]
deviceinfo_parse_kernel: str deviceinfo_parse_kernel: str
devices: str devices: str
@ -63,7 +65,6 @@ class PmbArgs(Namespace):
efi: str efi: str
envkernel: str envkernel: str
export_folder: Path export_folder: Path
extra_packages: str
extra_space: str extra_space: str
fast: str fast: str
file: str file: str
@ -148,8 +149,6 @@ class PmbArgs(Namespace):
suffix: str suffix: str
systemd: str systemd: str
timeout: float timeout: float
ui: str
ui_extras: str
user: str user: str
value: str value: str
verbose: str verbose: str
@ -157,3 +156,39 @@ class PmbArgs(Namespace):
work: Path work: Path
xauth: str xauth: str
zap: str zap: str
class Config():
aports: Path = Path(os.path.expanduser("~") +
"/.local/var/pmbootstrap/cache_git/pmaports")
boot_size: int = 256
build_default_device_arch: bool = False
build_pkgs_on_install: bool = True
ccache_size: str = "5G" # yeahhhh this one has a suffix
device: str = "qemu-amd64"
extra_packages: str = "none"
extra_space: int = 0
hostname: str = ""
is_default_channel: bool = True
jobs: str = str(multiprocessing.cpu_count() + 1)
kernel: str = "stable"
keymap: str = ""
locale: str = "en_US.UTF-8"
# NOTE: mirrors use http by default to leverage caching
mirror_alpine: str = "http://dl-cdn.alpinelinux.org/alpine/"
# NOTE: mirrors_postmarketos variable type is supposed to be
# comma-separated string, not a python list or any other type!
mirrors_postmarketos: List[str] = ["http://mirror.postmarketos.org/postmarketos/"]
qemu_redir_stdio: bool = False
ssh_key_glob: str = "~/.ssh/id_*.pub"
ssh_keys: bool = False
sudo_timer: bool = False
systemd: str = "default"
timezone: str = "GMT"
ui: str = "console"
ui_extras: bool = False
user: str = "user"
work: Path = Path(os.path.expanduser("~") + "/.local/var/pmbootstrap")
providers: Dict[str, str] = { }

View file

@ -3,11 +3,11 @@
""" Common code for git tests """ """ Common code for git tests """
import os import os
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.git import pmb.helpers.git
import pmb.helpers.run import pmb.helpers.run
import shutil import shutil
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
def prepare_tmpdir(args: PmbArgs, monkeypatch, tmpdir): def prepare_tmpdir(args: PmbArgs, monkeypatch, tmpdir):

View file

@ -5,7 +5,7 @@ from typing import List
import pytest import pytest
import sys import sys
from pmb.core.types import PathString from pmb.types import PathString
import pmb_test # noqa import pmb_test # noqa
import pmb.build import pmb.build
import pmb.chroot.apk import pmb.chroot.apk
@ -19,7 +19,7 @@ def args(tmpdir, request):
import pmb.parse import pmb.parse
sys.argv = ["pmbootstrap.py", "init"] sys.argv = ["pmbootstrap.py", "init"]
args = pmb.parse.arguments() args = pmb.parse.arguments()
args.log = pmb.config.work / "log_testsuite.txt" args.log = get_context().config.work / "log_testsuite.txt"
pmb.helpers.logging.init(args) pmb.helpers.logging.init(args)
request.addfinalizer(pmb.helpers.logging.logfd.close) request.addfinalizer(pmb.helpers.logging.logfd.close)
return args return args

View file

@ -13,7 +13,7 @@ import pmb.chroot.apk_static
import pmb.config import pmb.config
import pmb.parse.apkindex import pmb.parse.apkindex
import pmb.helpers.logging import pmb.helpers.logging
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
@pytest.fixture @pytest.fixture
@ -21,7 +21,7 @@ def args(request):
import pmb.parse import pmb.parse
sys.argv = ["pmbootstrap.py", "chroot"] sys.argv = ["pmbootstrap.py", "chroot"]
args = pmb.parse.arguments() args = pmb.parse.arguments()
args.log = pmb.config.work / "log_testsuite.txt" args.log = get_context().config.work / "log_testsuite.txt"
pmb.helpers.logging.init(args) pmb.helpers.logging.init(args)
request.addfinalizer(pmb.helpers.logging.logfd.close) request.addfinalizer(pmb.helpers.logging.logfd.close)
return args return args
@ -30,13 +30,13 @@ def args(request):
def test_read_signature_info(args: PmbArgs): def test_read_signature_info(args: PmbArgs):
# Tempfolder inside chroot for fake apk files # Tempfolder inside chroot for fake apk files
tmp_path = Path("/tmp/test_read_signature_info") tmp_path = Path("/tmp/test_read_signature_info")
tmp_path_outside = pmb.config.work / "chroot_native" / tmp_path tmp_path_outside = get_context().config.work / "chroot_native" / tmp_path
if os.path.exists(tmp_path_outside): if os.path.exists(tmp_path_outside):
pmb.chroot.root(args, ["rm", "-r", tmp_path]) pmb.chroot.root(["rm", "-r", tmp_path])
pmb.chroot.user(args, ["mkdir", "-p", tmp_path]) pmb.chroot.user(["mkdir", "-p", tmp_path])
# No signature found # No signature found
pmb.chroot.user(args, ["tar", "-czf", tmp_path / "no_sig.apk", pmb.chroot.user(["tar", "-czf", tmp_path / "no_sig.apk",
"/etc/issue"]) "/etc/issue"])
with tarfile.open(tmp_path_outside / "no_sig.apk", "r:gz") as tar: with tarfile.open(tmp_path_outside / "no_sig.apk", "r:gz") as tar:
with pytest.raises(RuntimeError) as e: with pytest.raises(RuntimeError) as e:
@ -44,10 +44,10 @@ def test_read_signature_info(args: PmbArgs):
assert "Could not find signature" in str(e.value) assert "Could not find signature" in str(e.value)
# Signature file with invalid name # Signature file with invalid name
pmb.chroot.user(args, ["mkdir", "-p", tmp_path / "sbin"]) pmb.chroot.user(["mkdir", "-p", tmp_path / "sbin"])
pmb.chroot.user(args, ["cp", "/etc/issue", tmp_path / pmb.chroot.user(["cp", "/etc/issue", tmp_path /
"sbin/apk.static.SIGN.RSA.invalid.pub"]) "sbin/apk.static.SIGN.RSA.invalid.pub"])
pmb.chroot.user(args, ["tar", "-czf", tmp_path / "invalid_sig.apk", pmb.chroot.user(["tar", "-czf", tmp_path / "invalid_sig.apk",
"sbin/apk.static.SIGN.RSA.invalid.pub"], "sbin/apk.static.SIGN.RSA.invalid.pub"],
working_dir=tmp_path) working_dir=tmp_path)
with tarfile.open(tmp_path_outside / "invalid_sig.apk", "r:gz") as tar: with tarfile.open(tmp_path_outside / "invalid_sig.apk", "r:gz") as tar:
@ -59,10 +59,10 @@ def test_read_signature_info(args: PmbArgs):
path = list(pmb.config.apk_keys_path.glob("/*.pub"))[0] path = list(pmb.config.apk_keys_path.glob("/*.pub"))[0]
name = os.path.basename(path) name = os.path.basename(path)
path_archive = "sbin/apk.static.SIGN.RSA." + name path_archive = "sbin/apk.static.SIGN.RSA." + name
pmb.chroot.user(args, ["mv", pmb.chroot.user(["mv",
tmp_path / "sbin/apk.static.SIGN.RSA.invalid.pub", tmp_path / "sbin/apk.static.SIGN.RSA.invalid.pub",
tmp_path / path_archive]) tmp_path / path_archive])
pmb.chroot.user(args, ["tar", "-czf", tmp_path / "realistic_name_sig.apk", pmb.chroot.user(["tar", "-czf", tmp_path / "realistic_name_sig.apk",
path_archive], working_dir=tmp_path) path_archive], working_dir=tmp_path)
with tarfile.open(tmp_path_outside / "realistic_name_sig.apk", "r:gz")\ with tarfile.open(tmp_path_outside / "realistic_name_sig.apk", "r:gz")\
as tar: as tar:
@ -72,23 +72,23 @@ def test_read_signature_info(args: PmbArgs):
assert sigkey_path == path assert sigkey_path == path
# Clean up # Clean up
pmb.chroot.user(args, ["rm", "-r", tmp_path]) pmb.chroot.user(["rm", "-r", tmp_path])
def test_successful_extraction(args: PmbArgs, tmpdir): def test_successful_extraction(args: PmbArgs, tmpdir):
if os.path.exists(pmb.config.work / "apk.static"): if os.path.exists(get_context().config.work / "apk.static"):
os.remove(pmb.config.work / "apk.static") os.remove(get_context().config.work / "apk.static")
pmb.chroot.apk_static.init(args) pmb.chroot.apk_static.init(args)
assert os.path.exists(pmb.config.work / "apk.static") assert os.path.exists(get_context().config.work / "apk.static")
os.remove(pmb.config.work / "apk.static") os.remove(get_context().config.work / "apk.static")
def test_signature_verification(args: PmbArgs, tmpdir): def test_signature_verification(args: PmbArgs, tmpdir):
if os.path.exists(pmb.config.work / "apk.static"): if os.path.exists(get_context().config.work / "apk.static"):
os.remove(pmb.config.work / "apk.static") os.remove(get_context().config.work / "apk.static")
version = pmb.parse.apkindex.package(args, "apk-tools-static")["version"] version = pmb.parse.apkindex.package("apk-tools-static")["version"]
apk_path = pmb.chroot.apk_static.download( apk_path = pmb.chroot.apk_static.download(
args, f"apk-tools-static-{version}.apk") args, f"apk-tools-static-{version}.apk")
@ -119,8 +119,8 @@ def test_signature_verification(args: PmbArgs, tmpdir):
def test_outdated_version(args: PmbArgs, monkeypatch): def test_outdated_version(args: PmbArgs, monkeypatch):
if os.path.exists(pmb.config.work / "apk.static"): if os.path.exists(get_context().config.work / "apk.static"):
os.remove(pmb.config.work / "apk.static") os.remove(get_context().config.work / "apk.static")
# Change min version for all branches # Change min version for all branches
min_copy = copy.copy(pmb.config.apk_tools_min_version) min_copy = copy.copy(pmb.config.apk_tools_min_version)

View file

@ -2,7 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import os import os
import sys import sys
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pytest import pytest
import shutil import shutil
import filecmp import filecmp
@ -22,7 +22,7 @@ def args(tmpdir, request):
cfg = f"{pmb_test.const.testdata}/channels.cfg" cfg = f"{pmb_test.const.testdata}/channels.cfg"
sys.argv = ["pmbootstrap.py", "--config-channels", cfg, "chroot"] sys.argv = ["pmbootstrap.py", "--config-channels", cfg, "chroot"]
args = pmb.parse.arguments() args = pmb.parse.arguments()
args.log = pmb.config.work / "log_testsuite.txt" args.log = get_context().config.work / "log_testsuite.txt"
args.fork_alpine = False args.fork_alpine = False
pmb.helpers.logging.init(args) pmb.helpers.logging.init(args)
request.addfinalizer(pmb.helpers.logging.logfd.close) request.addfinalizer(pmb.helpers.logging.logfd.close)
@ -85,7 +85,7 @@ def test_aportgen(args: PmbArgs, tmpdir):
os.mkdir(tmpdir + "/cross") os.mkdir(tmpdir + "/cross")
# Create aportgen folder -> code path where it still exists # Create aportgen folder -> code path where it still exists
pmb.helpers.run.user(["mkdir", "-p", pmb.config.work / "aportgen"]) pmb.helpers.run.user(["mkdir", "-p", get_context().config.work / "aportgen"])
# Generate all valid packages (gcc twice -> different code path) # Generate all valid packages (gcc twice -> different code path)
pkgnames = ["musl-armv7", pkgnames = ["musl-armv7",
@ -116,7 +116,7 @@ def test_aportgen_get_upstream_aport(args: PmbArgs, monkeypatch):
# Equal version # Equal version
func = pmb.aportgen.core.get_upstream_aport func = pmb.aportgen.core.get_upstream_aport
upstream = "gcc" upstream = "gcc"
upstream_full = pmb.config.work / "cache_git/aports_upstream/main/" + upstream upstream_full = get_context().config.work / "cache_git/aports_upstream/main/" + upstream
apkbuild = {"pkgver": "2.0", "pkgrel": "0"} apkbuild = {"pkgver": "2.0", "pkgrel": "0"}
package = {"version": "2.0-r0"} package = {"version": "2.0-r0"}
assert func(args, upstream) == upstream_full assert func(args, upstream) == upstream_full

View file

@ -1,7 +1,8 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pmb.core import get_context
from pmb.helpers import logging from pmb.helpers import logging
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pytest import pytest
import sys import sys
import shutil import shutil
@ -21,7 +22,7 @@ def args(tmpdir, request):
sys.argv = ["pmbootstrap.py", "--config-channels", cfg, "build", "-i", sys.argv = ["pmbootstrap.py", "--config-channels", cfg, "build", "-i",
"device-testsuite-testdevice"] "device-testsuite-testdevice"]
args = pmb.parse.arguments() args = pmb.parse.arguments()
args.log = pmb.config.work / "log_testsuite.txt" args.log = get_context().config.work / "log_testsuite.txt"
pmb.helpers.logging.init(args) pmb.helpers.logging.init(args)
request.addfinalizer(pmb.helpers.logging.logfd.close) request.addfinalizer(pmb.helpers.logging.logfd.close)
@ -73,9 +74,10 @@ def generate(args: PmbArgs, monkeypatch, answers):
pmb.aportgen.generate(args, "linux-testsuite-testdevice") pmb.aportgen.generate(args, "linux-testsuite-testdevice")
monkeypatch.undo() monkeypatch.undo()
apkbuild_path = (f"{args.aports}/device/testing/" aports = get_context().config.aports
apkbuild_path = (aports / "device/testing/"
"device-testsuite-testdevice/APKBUILD") "device-testsuite-testdevice/APKBUILD")
apkbuild_path_linux = (args.aports + "/device/testing/" apkbuild_path_linux = (aports / "device/testing/"
"linux-testsuite-testdevice/APKBUILD") "linux-testsuite-testdevice/APKBUILD")
# The build fails if the email is not a valid email, so remove them just # The build fails if the email is not a valid email, so remove them just
@ -88,11 +90,11 @@ def generate(args: PmbArgs, monkeypatch, answers):
apkbuild = pmb.parse.apkbuild(apkbuild_path) apkbuild = pmb.parse.apkbuild(apkbuild_path)
apkbuild_linux = pmb.parse.apkbuild(apkbuild_path_linux, apkbuild_linux = pmb.parse.apkbuild(apkbuild_path_linux,
check_pkgver=False) check_pkgver=False)
deviceinfo = pmb.parse.deviceinfo(args, "testsuite-testdevice") deviceinfo = pmb.parse.deviceinfo("testsuite-testdevice")
return (deviceinfo, apkbuild, apkbuild_linux) return (deviceinfo, apkbuild, apkbuild_linux)
def remove_contributor_maintainer_lines(args: PmbArgs, path): def remove_contributor_maintainer_lines(path):
with open(path, "r+", encoding="utf-8") as handle: with open(path, "r+", encoding="utf-8") as handle:
lines_new = [] lines_new = []
for line in handle.readlines(): for line in handle.readlines():

View file

@ -9,6 +9,7 @@ import pmb.chroot.apk_static
import pmb.parse.apkindex import pmb.parse.apkindex
import pmb.helpers.logging import pmb.helpers.logging
import pmb.parse.bootimg import pmb.parse.bootimg
from pmb.core import get_context
@pytest.fixture @pytest.fixture
@ -16,7 +17,7 @@ def args(request):
import pmb.parse import pmb.parse
sys.argv = ["pmbootstrap.py", "chroot"] sys.argv = ["pmbootstrap.py", "chroot"]
args = pmb.parse.arguments() args = pmb.parse.arguments()
args.log = pmb.config.work / "log_testsuite.txt" args.log = get_context().config.work / "log_testsuite.txt"
pmb.helpers.logging.init(args) pmb.helpers.logging.init(args)
request.addfinalizer(pmb.helpers.logging.logfd.close) request.addfinalizer(pmb.helpers.logging.logfd.close)
return args return args

View file

@ -2,7 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import os import os
import sys import sys
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pytest import pytest
import pmb_test # noqa import pmb_test # noqa
@ -15,7 +15,7 @@ def args(request, tmpdir):
import pmb.parse import pmb.parse
sys.argv = ["pmbootstrap.py", "chroot"] sys.argv = ["pmbootstrap.py", "chroot"]
args = pmb.parse.arguments() args = pmb.parse.arguments()
args.log = pmb.config.work / "log_testsuite.txt" args.log = get_context().config.work / "log_testsuite.txt"
pmb.helpers.logging.init(args) pmb.helpers.logging.init(args)
request.addfinalizer(pmb.helpers.logging.logfd.close) request.addfinalizer(pmb.helpers.logging.logfd.close)
@ -44,7 +44,7 @@ def cache_apkindex(version):
def test_build_is_necessary(args: PmbArgs): def test_build_is_necessary(args: PmbArgs):
# Prepare APKBUILD and APKINDEX data # Prepare APKBUILD and APKINDEX data
aport = pmb.helpers.pmaports.find(args, "hello-world") aport = pmb.helpers.pmaports.find("hello-world")
apkbuild = pmb.parse.apkbuild(f"{aport}/APKBUILD") apkbuild = pmb.parse.apkbuild(f"{aport}/APKBUILD")
apkbuild["pkgver"] = "1" apkbuild["pkgver"] = "1"
apkbuild["pkgrel"] = "2" apkbuild["pkgrel"] = "2"
@ -73,7 +73,7 @@ def test_build_is_necessary_no_binary_available(args: PmbArgs):
hello-world package has not been built yet. hello-world package has not been built yet.
""" """
indexes = list(pmb.helpers.other.cache["apkindex"].keys()) indexes = list(pmb.helpers.other.cache["apkindex"].keys())
aport = pmb.helpers.pmaports.find(args, "hello-world") aport = pmb.helpers.pmaports.find("hello-world")
apkbuild = pmb.parse.apkbuild(f"{aport}/APKBUILD") apkbuild = pmb.parse.apkbuild(f"{aport}/APKBUILD")
assert pmb.build.is_necessary(args, None, apkbuild, indexes) is True assert pmb.build.is_necessary(args, None, apkbuild, indexes) is True

View file

@ -4,7 +4,7 @@
import datetime import datetime
import glob import glob
import os import os
from pmb.core.types import PmbArgs from pmb.types import PmbArgs
import pytest import pytest
import shutil import shutil
import sys import sys
@ -23,7 +23,7 @@ def args(tmpdir, request):
import pmb.parse import pmb.parse
sys.argv = ["pmbootstrap", "init"] sys.argv = ["pmbootstrap", "init"]
args = pmb.parse.arguments() args = pmb.parse.arguments()
args.log = pmb.config.work / "log_testsuite.txt" args.log = get_context().config.work / "log_testsuite.txt"
pmb.helpers.logging.init(args) pmb.helpers.logging.init(args)
request.addfinalizer(pmb.helpers.logging.logfd.close) request.addfinalizer(pmb.helpers.logging.logfd.close)
return args return args
@ -351,9 +351,9 @@ def test_build_depends_high_level(args: PmbArgs, monkeypatch):
fake_build_is_necessary) fake_build_is_necessary)
# Build hello-world to get its full output path # Build hello-world to get its full output path
channel = pmb.config.pmaports.read_config(args)["channel"] channel = pmb.config.pmaports.read_config()["channel"]
output_hello = pmb.build.package(args, "hello-world") output_hello = pmb.build.package(args, "hello-world")
output_hello_outside = pmb.config.work / "packages" / channel / output_hello output_hello_outside = get_context().config.work / "packages" / channel / output_hello
assert output_hello_outside.exists() assert output_hello_outside.exists()
# Make sure the wrapper exists # Make sure the wrapper exists
@ -386,7 +386,7 @@ def test_build_local_source_high_level(args: PmbArgs, tmpdir):
aports = tmpdir + "/aports" aports = tmpdir + "/aports"
aport = aports + "/device/testing/device-" + args.device aport = aports + "/device/testing/device-" + args.device
os.makedirs(aport) os.makedirs(aport)
path_original = pmb.helpers.pmaports.find(args, f"device-{args.device}") path_original = pmb.helpers.pmaports.find(f"device-{args.device}")
shutil.copy(f"{path_original}/deviceinfo", aport) shutil.copy(f"{path_original}/deviceinfo", aport)
# aports: Add modified hello-world aport (source="", uses $builddir) # aports: Add modified hello-world aport (source="", uses $builddir)
@ -412,7 +412,7 @@ def test_build_local_source_high_level(args: PmbArgs, tmpdir):
pmb.helpers.run.root(["chmod", "500", unreadable]) pmb.helpers.run.root(["chmod", "500", unreadable])
# Test native arch and foreign arch chroot # Test native arch and foreign arch chroot
channel = pmb.config.pmaports.read_config(args)["channel"] channel = pmb.config.pmaports.read_config()["channel"]
# TODO: test disabled, seems to *only* fail on gitlab runners and nowhere else. # TODO: test disabled, seems to *only* fail on gitlab runners and nowhere else.
# See: https://gitlab.com/postmarketOS/pmbootstrap/-/issues/2346 # See: https://gitlab.com/postmarketOS/pmbootstrap/-/issues/2346
# for arch in [pmb.config.arch_native, "armhf"]: # for arch in [pmb.config.arch_native, "armhf"]:
@ -449,7 +449,7 @@ def test_build_abuild_leftovers(args: PmbArgs, tmpdir):
aports = f"{tmpdir}/aports" aports = f"{tmpdir}/aports"
aport = f"{aports}/device/testing/device-{args.device}" aport = f"{aports}/device/testing/device-{args.device}"
os.makedirs(aport) os.makedirs(aport)
path_original = pmb.helpers.pmaports.find(args, f"device-{args.device}") path_original = pmb.helpers.pmaports.find(f"device-{args.device}")
shutil.copy(f"{path_original}/deviceinfo", aport) shutil.copy(f"{path_original}/deviceinfo", aport)
# aports: Add modified hello-world aport (source="", uses $builddir) # aports: Add modified hello-world aport (source="", uses $builddir)
@ -468,7 +468,7 @@ def test_build_abuild_leftovers(args: PmbArgs, tmpdir):
f"{src}/broken-tarball-symlink.tar.gz") f"{src}/broken-tarball-symlink.tar.gz")
# Delete all hello-world packages # Delete all hello-world packages
channel = pmb.config.pmaports.read_config(args)["channel"] channel = pmb.config.pmaports.read_config()["channel"]
pattern = f"{args.work}/packages/{channel}/*/hello-world-*_p*.apk" pattern = f"{args.work}/packages/{channel}/*/hello-world-*_p*.apk"
for path in glob.glob(pattern): for path in glob.glob(pattern):
pmb.helpers.run.root(["rm", path]) pmb.helpers.run.root(["rm", path])

Some files were not shown because too many files have changed in this diff Show more