builder_peppermint_void/builder/core/bootstrap/bootstrap.py
2025-04-27 16:44:09 +00:00

159 lines
7.9 KiB
Python

# builder/core/bootstrap.py
import os
import logging
import platform
import sys # Added import
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, BASE_DIR)
try:
# Assuming paths and logger_config are available through your project's structure
# If paths is a dict defined elsewhere, you might need to pass it directly
# from builder.core.bootstrap.paths import paths # Removed direct import, paths should be passed
from builder.configs import logger_config
# Assuming run_command is in builder.core.command_runner.py
from buider.core.command_runner import run_command
except ImportError as e:
# Log the error before exiting
logging.error(f"Error importing necessary modules: {e}. Ensure your environment is set up correctly.")
# Provide more specific details if possible, though 'e' usually has them
# logging.error(f"Details: {e}") # 'e' is already in the f-string above
sys.exit(1) # Exit if essential modules can't be imported
# Setup logger specifically for this module
logger = logger_config.setup_logger('bootstrap')
def filter_repositories_by_architecture(repositories_data, target_architecture):
"""
Filters the list of repositories to include only those supporting the target architecture.
Args:
repositories_data (list): A list of repository dictionaries,
each potentially having an 'architectures' key.
target_architecture (str): The target architecture (e.g., 'aarch64', 'i686', 'x86_64').
Returns:
list: A list of '-R <uri>' strings for the relevant repositories.
"""
filtered_repo_args = []
for repo in repositories_data:
# If 'architectures' key exists and target_architecture is in the list,
# or if 'architectures' key does not exist (assume it applies to all architectures).
# Also ensure 'uri' key exists.
if 'uri' in repo and ('architectures' not in repo or target_architecture in repo['architectures']):
filtered_repo_args.extend(["-R", repo['uri']])
logger.debug(f"Including repository {repo['uri']} for architecture {target_architecture}")
else:
if 'uri' not in repo:
logger.warning(f"Repository entry missing 'uri' key: {repo}. Skipping.")
else:
logger.debug(f"Excluding repository {repo['uri']} for architecture {target_architecture}")
return filtered_repo_args
def run_bootstrap_for_environment(env_name, paths, target_architecture, host_architecture, repositories_data, all_bootstrap_packages):
"""
Executes the xbps-install bootstrap command for a specific environment (rootfs, pep-host, pep-target).
Handles architecture-specific repository filtering and execution in a chroot
for cross-architecture builds.
Args:
env_name (str): The name of the environment to bootstrap ('rootfs', 'pep-host', 'pep-target').
paths (dict): Dictionary of build paths.
target_architecture (str): The target architecture for the bootstrap.
host_architecture (str): The architecture of the host system.
repositories_data (list): List of repository dictionaries from YAML config.
Expected structure: [{'name': '...', 'uri': '...', 'architectures': [...]}, ...]
all_bootstrap_packages (dict): Dictionary containing package lists for
different environments and architectures.
Expected structure: {'env_name': {'arch': ['pkg1', ...]}}
Raises:
subprocess.CalledProcessError: If the xbps-install command fails.
ValueError: If an unknown environment name is provided.
"""
logger.info(f"=> Executing bootstrap command for {env_name.upper()} ({target_architecture})...")
# Determine the target directory based on environment name
target_directory_path = None
if env_name == "rootfs":
target_directory_path = paths.get("ROOTFS")
elif env_name == "pep-host":
target_directory_path = paths.get("PEPHOSTDIR")
elif env_name == "pep-target":
target_directory_path = paths.get("PEPTARGETDIR")
else:
logger.error(f"Unknown bootstrap environment: {env_name}")
raise ValueError(f"Unknown bootstrap environment: {env_name}")
if not target_directory_path:
logger.error(f"Path for environment '{env_name}' not found in paths dictionary.")
raise KeyError(f"Path for environment '{env_name}' not found.")
# Get the list of packages for this environment and target architecture
# Assumes all_bootstrap_packages has structure {'env_name': {'arch': [...]}}
packages_to_install = all_bootstrap_packages.get(env_name, {}).get(target_architecture, [])
if not packages_to_install:
logger.warning(f"No packages defined for {env_name} on architecture {target_architecture}. Skipping bootstrap.")
return
# Filter repository URLs for the target architecture
filtered_repo_args = filter_repositories_by_architecture(repositories_data, target_architecture)
if not filtered_repo_args:
logger.error(f"No repositories found supporting target architecture: {target_architecture}. Cannot bootstrap {env_name}.")
raise ValueError(f"No repositories found for architecture {target_architecture}")
# Get the XBPS cache directory for the target architecture (on the host)
# Assumes paths['XBPS_CACHEDIR_<ARCH_UPPER>'] is defined or we construct it
# Construct based on target arch as it's the target's packages being cached
host_cachedir = os.path.join(os.getcwd(), "xbps_package_cache", target_architecture)
# Ensure the cache directory exists
os.makedirs(host_cachedir, exist_ok=True)
logger.debug(f"Using XBPS cache directory (on host): {host_cachedir}")
# Construct the base xbps-install command arguments
xbps_command_args = [
"-S", "-y", # Sync and auto-yes
] + filtered_repo_args + [
"-r", target_directory_path, # Install to the target rootfs/directory (path on host)
"-c", host_cachedir,
] + packages_to_install
# Determine the command execution method (direct or via chroot)
command_list = []
# Execute inside a chroot for ALL architectures for consistency and to handle
# INSTALL scripts correctly, leveraging QEMU on foreign arches.
logger.info(f"-> Running xbps-install for {env_name} in chroot...")
# Note: The chroot command path is just 'chroot', relying on it being in the host's PATH
# '/usr/bin/chroot' could be used for absolute path.
command_list = [
"sudo", "chroot", target_directory_path, # Execute command inside this directory (path on host)
"/usr/bin/xbps-install", # The xbps-install binary *inside* the chroot (path inside chroot)
# Pass the rest of the xbps_command_args as arguments to xbps-install inside the chroot
# Arguments like -r and -c need paths that are correct from the *host's* perspective,
# which run_command handles by passing them literally.
# The chroot environment needs /proc, /sys, /dev mounted for xbps and scripts.
# The main script's mount_essential_filesystems_in_chroot should handle this *before*
# calling this bootstrap function for rootfs. For pep-host/pep-target, manual mounts
# inside this function or ensuring they are handled before might be needed if
# they rely on those filesystems. Let's assume essential mounts are handled elsewhere
# or not strictly needed for these specific xbps-install steps.
] + xbps_command_args
# Execute the command
# The run_command function should handle subprocess execution and error checking
logger.info(f"Executing command: {' '.join(command_list)}")
# Assuming run_command handles potential CalledProcessError and raises it
run_command(command_list)
logger.info(f"=> Void Linux {env_name.upper()} bootstrap COMPLETE.")