forked from Mirror/pmbootstrap
Introduce a Deviceinfo class and use it rather than the dictionary. This gives us sweet sweet autocomplete, and lays the foundation for having a proper deviceinfo validator in the future. Additionally, continue refactoring out args... Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
188 lines
5.6 KiB
Python
188 lines
5.6 KiB
Python
# Copyright 2024 Oliver Smith
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
from typing import Optional
|
|
from pmb.core.chroot import Chroot
|
|
from pmb.helpers import logging
|
|
import glob
|
|
|
|
import pmb.config.pmaports
|
|
import pmb.helpers.repo
|
|
from pmb.types import Config
|
|
from pmb.core import get_context
|
|
|
|
|
|
progress_done = 0
|
|
progress_total = 0
|
|
progress_step: str
|
|
|
|
|
|
def get_arch(config: Config):
|
|
if config.build_default_device_arch:
|
|
return pmb.parse.deviceinfo().arch
|
|
|
|
return pmb.config.arch_native
|
|
|
|
|
|
def check_repo_arg(repo: str):
|
|
cfg = pmb.config.pmaports.read_config_repos()
|
|
|
|
if repo in cfg:
|
|
return
|
|
|
|
if not cfg:
|
|
raise ValueError("pmaports.cfg of current branch does not have any"
|
|
" sections starting with 'repo:'")
|
|
|
|
logging.info(f"Valid repositories: {', '.join(cfg.keys())}")
|
|
raise ValueError(f"Couldn't find section 'repo:{repo}' in pmaports.cfg of"
|
|
" current branch")
|
|
|
|
|
|
def check_existing_pkgs(config: Config, arch):
|
|
channel = pmb.config.pmaports.read_config()["channel"]
|
|
path = config.work / "packages" / channel / arch
|
|
|
|
if glob.glob(f"{path}/*"):
|
|
logging.info(f"Packages path: {path}")
|
|
|
|
msg = f"Found previously built packages for {channel}/{arch}, run" \
|
|
" 'pmbootstrap zap -p' first"
|
|
if pmb.parse.arch.cpu_emulation_required(arch):
|
|
msg += " or remove the path manually (to keep cross compilers if" \
|
|
" you just built them)"
|
|
|
|
raise RuntimeError(f"{msg}!")
|
|
|
|
|
|
def get_steps(repo: str):
|
|
cfg = pmb.config.pmaports.read_config_repos()
|
|
prev_step = 0
|
|
ret = {}
|
|
|
|
for key, packages in cfg[repo].items():
|
|
if not key.startswith("bootstrap_"):
|
|
continue
|
|
|
|
step = int(key.split("bootstrap_", 1)[1])
|
|
assert step == prev_step + 1, (f"{key}: wrong order of steps, expected"
|
|
f" bootstrap_{prev_step + 1} (previous: bootstrap_{prev_step})")
|
|
prev_step = step
|
|
|
|
ret[key] = packages
|
|
|
|
return ret
|
|
|
|
|
|
def get_suffix(arch):
|
|
if pmb.parse.arch.cpu_emulation_required(arch):
|
|
return f"buildroot_{arch}"
|
|
return "native"
|
|
|
|
|
|
def get_packages(bootstrap_line):
|
|
ret = []
|
|
for word in bootstrap_line.split(" "):
|
|
if word.startswith("["):
|
|
continue
|
|
ret += [word]
|
|
return ret
|
|
|
|
|
|
def set_progress_total(steps, arch):
|
|
global progress_total
|
|
|
|
progress_total = 0
|
|
|
|
# Add one progress point per package
|
|
for step, bootstrap_line in steps.items():
|
|
progress_total += len(get_packages(bootstrap_line))
|
|
|
|
# Add progress points per bootstrap step
|
|
progress_total += len(steps) * 2
|
|
|
|
# Foreign arch: need to initialize one additional chroot each step
|
|
if pmb.parse.arch.cpu_emulation_required(arch):
|
|
progress_total += len(steps)
|
|
|
|
|
|
def log_progress(msg):
|
|
global progress_done
|
|
|
|
percent = int(100 * progress_done / progress_total)
|
|
logging.info(f"*** {percent}% [{progress_step}] {msg} ***")
|
|
|
|
progress_done += 1
|
|
|
|
|
|
def run_steps(steps, arch, chroot: Chroot):
|
|
global progress_step
|
|
|
|
for step, bootstrap_line in steps.items():
|
|
progress_step = step.replace("bootstrap_", "BOOTSTRAP=")
|
|
|
|
log_progress("zapping")
|
|
pmb.chroot.zap(confirm=False)
|
|
|
|
usr_merge = pmb.chroot.UsrMerge.OFF
|
|
if "[usr_merge]" in bootstrap_line:
|
|
usr_merge = pmb.chroot.UsrMerge.ON
|
|
|
|
if chroot != Chroot.native():
|
|
log_progress(f"initializing native chroot (merge /usr: {usr_merge.name})")
|
|
# Native chroot needs pmOS binary package repo for cross compilers
|
|
pmb.chroot.init(Chroot.native(), usr_merge)
|
|
|
|
log_progress(f"initializing {chroot} chroot (merge /usr: {usr_merge.name})")
|
|
# Initialize without pmOS binary package repo
|
|
pmb.chroot.init(chroot, usr_merge, postmarketos_mirror=False)
|
|
|
|
for package in get_packages(bootstrap_line):
|
|
log_progress(f"building {package}")
|
|
bootstrap_stage = int(step.split("bootstrap_", 1)[1])
|
|
pmb.build.package(package, arch, force=True,
|
|
strict=True, bootstrap_stage=bootstrap_stage)
|
|
|
|
log_progress("bootstrap complete!")
|
|
|
|
|
|
def main(arch: Optional[str], repository: str): # noqa: F821
|
|
config = get_context().config
|
|
check_repo_arg(repository)
|
|
|
|
arch = arch or get_arch(config)
|
|
check_existing_pkgs(config, arch)
|
|
|
|
steps = get_steps(repository)
|
|
suffix = get_suffix(arch)
|
|
|
|
set_progress_total(steps, arch)
|
|
run_steps(steps, arch, suffix)
|
|
|
|
|
|
def require_bootstrap_error(repo, arch, trigger_str):
|
|
"""
|
|
Tell the user that they need to do repo_bootstrap, with some context.
|
|
|
|
:param repo: which repository
|
|
:param arch: for which architecture
|
|
:param trigger_str: message for the user to understand what caused this
|
|
"""
|
|
logging.info(f"ERROR: Trying to {trigger_str} with {repo} enabled, but the"
|
|
f" {repo} repo needs to be bootstrapped first.")
|
|
raise RuntimeError(f"Run 'pmbootstrap repo_bootstrap {repo} --arch={arch}'"
|
|
" and then try again.")
|
|
|
|
|
|
def require_bootstrap(arch, trigger_str):
|
|
"""
|
|
Check if repo_bootstrap was done, if any is needed.
|
|
|
|
:param arch: for which architecture
|
|
:param trigger_str: message for the user to understand what caused this
|
|
"""
|
|
if pmb.config.other.is_systemd_selected(get_context().config):
|
|
pmb.helpers.repo.update(arch)
|
|
pkg = pmb.parse.apkindex.package("postmarketos-base-systemd",
|
|
arch, False)
|
|
if not pkg:
|
|
require_bootstrap_error("systemd", arch, trigger_str)
|