mirror of
https://gitlab.com/kupfer/kupferbootstrap.git
synced 2025-02-23 13:45:45 -05:00
170 lines
7.1 KiB
Python
170 lines
7.1 KiB
Python
import logging
|
|
import os
|
|
import subprocess
|
|
from glob import glob
|
|
from typing import ClassVar, Optional
|
|
|
|
from config import config
|
|
from constants import Arch, GCC_HOSTSPECS, CROSSDIRECT_PKGS, CHROOT_PATHS
|
|
from distro.distro import get_kupfer_local
|
|
from exec.cmd import run_root_cmd
|
|
from exec.file import makedir, remove_file, root_makedir, root_write_file, symlink
|
|
|
|
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
|
|
|
|
def create_rootfs(self, reset: bool, pacman_conf_target: str, active_previously: bool):
|
|
makedir(config.get_path('chroots'))
|
|
root_makedir(self.get_path())
|
|
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}")
|
|
result = run_root_cmd(cmd)
|
|
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)
|
|
|
|
# configure makepkg
|
|
self.write_makepkg_conf(self.arch, cross_chroot_relative=None, cross=False)
|
|
|
|
if active_previously:
|
|
self.activate()
|
|
|
|
def mount_crossdirect(self, native_chroot: Optional[Chroot] = None, fail_if_mounted: bool = False):
|
|
"""
|
|
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)
|
|
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()
|
|
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:
|
|
logging.debug('Failed to install cross-compiler package {gcc}')
|
|
if res_crossdirect.returncode != 0:
|
|
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)
|
|
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)
|
|
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)
|
|
|
|
root_makedir(native_mount)
|
|
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,
|
|
)
|
|
|
|
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
|
|
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)
|
|
results.append(self.mount(
|
|
absolute_source=mount_source,
|
|
relative_destination=mount_dest,
|
|
fail_if_mounted=fail_if_mounted,
|
|
))
|
|
return results
|
|
|
|
|
|
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)
|
|
return chroot
|