kupferbootstrap/chroot/build.py

171 lines
7.1 KiB
Python
Raw Normal View History

2022-02-17 21:49:24 +01:00
import logging
import os
import subprocess
2022-02-18 06:32:04 +01:00
from glob import glob
from typing import ClassVar, Optional
2022-02-17 21:49:24 +01:00
from config.state import config
2022-02-17 21:49:24 +01:00
from constants import Arch, GCC_HOSTSPECS, CROSSDIRECT_PKGS, CHROOT_PATHS
from distro.distro import get_kupfer_local
2022-08-15 17:41:23 +02:00
from exec.cmd import run_root_cmd
from exec.file import makedir, remove_file, root_makedir, root_write_file, symlink
2022-02-17 21:49:24 +01:00
from .abstract import Chroot, get_chroot
from .helpers import build_chroot_name
from .base import get_base_chroot
class BuildChroot(Chroot):
_copy_base: ClassVar[bool] = True
2022-02-17 21:49:24 +01:00
def create_rootfs(self, reset: bool, pacman_conf_target: str, active_previously: bool):
makedir(config.get_path('chroots'))
root_makedir(self.get_path())
2022-02-17 21:49:24 +01:00
if reset or not os.path.exists(self.get_path('usr/bin')):
base_chroot = get_base_chroot(self.arch)
if base_chroot == self:
raise Exception('base_chroot == self, bailing out. this is a bug')
base_chroot.initialize()
logging.info(f'Copying {base_chroot.name} chroot to {self.name}')
cmd = ['rsync', '-a', '--delete', '-q', '-W', '-x']
for mountpoint in CHROOT_PATHS.values():
cmd += ['--exclude', mountpoint.rstrip('/')]
cmd += [f'{base_chroot.path}/', f'{self.path}/']
logging.debug(f"running rsync: {cmd}")
2022-08-15 06:32:43 +02:00
result = run_root_cmd(cmd)
2022-02-17 21:49:24 +01:00
if result.returncode != 0:
raise Exception(f'Failed to copy {base_chroot.name} to {self.name}')
else:
logging.debug(f'{self.name}: Reusing existing installation')
if set(get_kupfer_local(self.arch).repos).intersection(set(self.extra_repos)):
self.mount_packages()
self.mount_pacman_cache()
self.write_pacman_conf()
self.initialized = True
self.activate()
self.try_install_packages(self.base_packages, refresh=True, allow_fail=False)
self.deactivate_core()
# patch makepkg
with open(self.get_path('/usr/bin/makepkg'), 'r') as file:
data = file.read()
data = data.replace('EUID == 0', 'EUID == -1')
root_write_file(self.get_path('/usr/bin/makepkg'), data)
2022-02-17 21:49:24 +01:00
# configure makepkg
self.write_makepkg_conf(self.arch, cross_chroot_relative=None, cross=False)
if active_previously:
self.activate()
2022-02-18 06:32:04 +01:00
def mount_crossdirect(self, native_chroot: Optional[Chroot] = None, fail_if_mounted: bool = False):
2022-02-17 21:49:24 +01:00
"""
mount `native_chroot` at `target_chroot`/native
returns the absolute path that `native_chroot` has been mounted at.
"""
target_arch = self.arch
if not native_chroot:
assert config.runtime.arch
native_chroot = get_build_chroot(config.runtime.arch)
2022-02-17 21:49:24 +01:00
host_arch = native_chroot.arch
hostspec = GCC_HOSTSPECS[host_arch][target_arch]
cc = f'{hostspec}-cc'
gcc = f'{hostspec}-gcc'
native_mount = os.path.join(self.path, 'native')
logging.debug(f'Activating crossdirect in {native_mount}')
native_chroot.initialize()
native_chroot.mount_pacman_cache()
native_chroot.mount_packages()
native_chroot.activate()
2022-02-18 06:32:04 +01:00
results = dict(native_chroot.try_install_packages(
CROSSDIRECT_PKGS + [gcc],
refresh=True,
allow_fail=False,
),)
res_gcc = results[gcc]
res_crossdirect = results['crossdirect']
assert isinstance(res_gcc, subprocess.CompletedProcess)
assert isinstance(res_crossdirect, subprocess.CompletedProcess)
if res_gcc.returncode != 0:
2022-02-17 21:49:24 +01:00
logging.debug('Failed to install cross-compiler package {gcc}')
2022-02-18 06:32:04 +01:00
if res_crossdirect.returncode != 0:
2022-02-17 21:49:24 +01:00
raise Exception('Failed to install crossdirect')
cc_path = os.path.join(native_chroot.path, 'usr', 'bin', cc)
target_lib_dir = os.path.join(self.path, 'lib64')
# TODO: crosscompiler weirdness, find proper fix for /include instead of /usr/include
target_include_dir = os.path.join(self.path, 'include')
for target, source in {cc_path: gcc, target_lib_dir: 'lib', target_include_dir: 'usr/include'}.items():
if not os.path.exists(target):
logging.debug(f'Symlinking {source} at {target}')
symlink(source, target)
2022-02-17 21:49:24 +01:00
ld_so = os.path.basename(glob(f"{os.path.join(native_chroot.path, 'usr', 'lib', 'ld-linux-')}*")[0])
ld_so_target = os.path.join(target_lib_dir, ld_so)
if not os.path.islink(ld_so_target):
symlink(os.path.join('/native', 'usr', 'lib', ld_so), ld_so_target)
2022-02-17 21:49:24 +01:00
else:
logging.debug(f'ld-linux.so symlink already exists, skipping for {self.name}')
# TODO: find proper fix
rustc = os.path.join(native_chroot.path, 'usr/lib/crossdirect', target_arch, 'rustc')
if os.path.exists(rustc):
logging.debug('Disabling crossdirect rustc')
remove_file(rustc)
2022-02-17 21:49:24 +01:00
root_makedir(native_mount)
2022-02-17 21:49:24 +01:00
logging.debug(f'Mounting {native_chroot.name} to {native_mount}')
self.mount(native_chroot.path, 'native', fail_if_mounted=fail_if_mounted)
return native_mount
def mount_crosscompile(self, foreign_chroot: Chroot, fail_if_mounted: bool = False):
mount_dest = os.path.join(CHROOT_PATHS['chroots'].lstrip('/'), os.path.basename(foreign_chroot.path))
return self.mount(
absolute_source=foreign_chroot.path,
relative_destination=mount_dest,
fail_if_mounted=fail_if_mounted,
)
def mount_ccache(self, user: str = 'kupfer', fail_if_mounted: bool = False):
mount_source = os.path.join(config.get_path('ccache'), self.arch)
mount_dest = os.path.join(f'/home/{user}' if user != 'root' else '/root', '.ccache')
uid = self.get_uid(user)
makedir(mount_source, user=uid)
return self.mount(
absolute_source=mount_source,
relative_destination=mount_dest,
fail_if_mounted=fail_if_mounted,
)
2022-08-29 04:43:12 +02:00
def mount_rust(self, user: str = 'kupfer', fail_if_mounted: bool = False) -> list[str]:
results = []
uid = self.get_uid(user)
mount_source_base = config.get_path('rust') # apparently arch-agnostic
2022-08-29 04:43:12 +02:00
for rust_dir in ['cargo', 'rustup']:
mount_source = os.path.join(mount_source_base, rust_dir)
mount_dest = os.path.join(f'/home/{user}' if user != 'root' else '/root', f'.{rust_dir}')
makedir(mount_source, user=uid)
2022-08-29 04:43:12 +02:00
results.append(self.mount(
absolute_source=mount_source,
relative_destination=mount_dest,
fail_if_mounted=fail_if_mounted,
))
return results
2022-02-17 21:49:24 +01:00
def get_build_chroot(arch: Arch, add_kupfer_repos: bool = True, **kwargs) -> BuildChroot:
name = build_chroot_name(arch)
if 'extra_repos' in kwargs:
raise Exception('extra_repos!')
repos = get_kupfer_local(arch).repos if add_kupfer_repos else {}
args = dict(arch=arch)
chroot = get_chroot(name, **kwargs, extra_repos=repos, chroot_class=BuildChroot, chroot_args=args)
assert isinstance(chroot, BuildChroot)
2022-02-17 21:49:24 +01:00
return chroot