builder_peppermint_void/builder/core/initramfs_builder.py
2025-04-29 16:39:59 +00:00

192 lines
8.1 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
SPDX-FileCopyrightText: 2023-2025 PeppermintOS Team
(peppermintosteam@proton.me)
SPDX-License-Identifier: GPL-3.0-or-later
This module provides functions to build an initramfs for a Linux system.
It includes functions for:
- Copying the kernel image to the boot directory.
- Generating the initramfs using dracut.
- Retrieving kernel version information from within a chroot environment.
Credits:
- PeppermintOS Team (peppermintosteam@proton.me) - Development and maintenance of the project.
License:
This code is distributed under the GNU General Public License version 3 or later (GPL-3.0-or-later).
For more details, please refer to the LICENSE file included in the project or visit:
https://www.gnu.org/licenses/gpl-3.0.html
"""
import os
import subprocess
import shutil
import logging
import sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, BASE_DIR)
try:
from builder.core.bootstrap.paths import paths
from builder.core.xbps_commands import xbps_commands
from builder.configs import logger_config
logger = logger_config.setup_logger('initramfs_builder')
except ImportError as e:
print(f"Error importing necessary modules: {e}. Ensure your environment is set up correctly.")
sys.exit(1)
def execute_xbps_query_chroot(rootfs_path, xbps_query_args, iso_build_config):
"""Executes xbps-query inside the chroot and returns the output."""
xbps_arch = iso_build_config.get('target_arch', 'x86_64')
command_xbps_query = ["chroot", rootfs_path, f"XBPS_ARCH={xbps_arch}", xbps_commands["XBPS_QUERY_CMD"]] + list(xbps_query_args)
completed_process = subprocess.run(command_xbps_query, capture_output=True, text=True)
return completed_process.stdout.strip()
def execute_xbps_uhelper_chroot(rootfs_path, xbps_uhelper_args, iso_build_config):
"""Executes xbps-uhelper inside the chroot and returns the output."""
command_xbps_uhelper = ["chroot", rootfs_path, xbps_commands["XBPS_UHELPER_CMD"]] + list(xbps_uhelper_args)
completed_process = subprocess.run(command_xbps_uhelper, capture_output=True, text=True)
return completed_process.stdout.strip()
def copy_kernel_image(rootfs_path, boot_path, target_architecture, iso_build_config):
"""
Copies the kernel image file (vmlinuz or vmlinux) from the ROOTFS to the ISO's BOOT directory,
renaming it to 'vmlinuz' (x86_64, i686) or 'vmlinux' (aarch64) and using the correct kernel version.
"""
logger.info("=> Copying kernel image to the ISO BOOT directory...")
kernel_versions = get_kernel_version_in_chroot(rootfs_path, target_architecture, iso_build_config)
if not kernel_versions or 'version_final_part' not in kernel_versions:
logger.error("=> ERROR: Could not obtain kernel version to copy the image.")
raise ValueError("Could not obtain kernel version.")
kernel_file_suffix = kernel_versions['version_final_part']
target_arch = target_architecture.lower() 
if target_arch.startswith('i686') or target_arch == 'x86_64':
kernel_filename_in_boot = "vmlinuz"
elif target_arch == 'aarch64':
kernel_filename_in_boot = "vmlinux"
else:
logger.error(f"=> Unknown target architecture: '{target_arch}'. Cannot copy kernel.")
raise ValueError(f"Unsupported target architecture: '{target_arch}'")
boot_dir = os.path.join(rootfs_path, 'boot')
kernel_filename_in_rootfs = None
try:
boot_files = os.listdir(boot_dir)
for filename in boot_files:
if target_arch.startswith('i686') or target_arch == 'x86_64':
if filename.startswith(f'vmlinuz-{kernel_file_suffix}'):
kernel_filename_in_rootfs = filename
break
elif target_arch == 'aarch64':
if filename.startswith(f'vmlinux-{kernel_file_suffix}'):
kernel_filename_in_rootfs = filename
break
except FileNotFoundError:
logger.error(f"=> ERROR: /boot directory not found at: {boot_dir}")
raise FileNotFoundError(f"/boot directory not found: {boot_dir}")
if not kernel_filename_in_rootfs:
logger.error(f"=> ERROR: Kernel file with suffix '{kernel_file_suffix}' not found in: {boot_dir}")
raise FileNotFoundError(f"Kernel file not found with suffix: '{kernel_file_suffix}'")
kernel_path_in_rootfs = os.path.join(rootfs_path, "boot", kernel_filename_in_rootfs)
kernel_path_in_boot = os.path.join(boot_path, kernel_filename_in_boot)
logger.info(f"=> Copying kernel from: {kernel_path_in_rootfs} to: {kernel_path_in_boot}...")
shutil.copy2(kernel_path_in_rootfs, kernel_path_in_boot)
logger.info(f"=> Kernel copied successfully to: {kernel_path_in_boot}")
return kernel_path_in_boot
def get_kernel_version_in_chroot(rootfs_path, target_architecture, iso_build_config):
target_arch = target_architecture.lower()
boot_dir = os.path.join(rootfs_path, 'boot')
try:
boot_files = os.listdir(boot_dir)
except FileNotFoundError:
logger.error(f"=> ERROR: /boot directory not found at: {boot_dir}")
return None
kernel_version = None
for filename in boot_files:
if target_arch.startswith('i686') or target_arch == 'x86_64':
if filename.startswith('vmlinuz-'):
kernel_version = filename.replace('vmlinuz-', '')
break
elif target_arch == 'aarch64':
if filename.startswith('vmlinux-'):
kernel_version = filename.replace('vmlinux-', '')
break
if not kernel_version:
logger.error(f"=> ERROR: Kernel image not found in: {boot_dir}")
return None
logger.info(f"=> Kernel version found in /boot: {kernel_version}")
kernel_versions_dict = {
'version_final_part': kernel_version,
'version_major_minor': ".".join(kernel_version.split('.')[:2])
}
return kernel_versions_dict
def get_kernel_module_dir_in_chroot(rootfs_path, kernel_version):
"""
Gets the path of the kernel modules directory in the chroot using the provided version.
"""
kernel_modules_dir = f"/lib/modules/{kernel_version}"
return kernel_modules_dir
def create_initramfs(rootfs_path, boot_path, target_architecture, xbps_commands, iso_build_config):
logger.info("=> Starting initramfs creation...")
logger.info("=> Creating initramfs using dracut...")
kernel_versions = get_kernel_version_in_chroot(rootfs_path, target_architecture, iso_build_config)
if not kernel_versions or 'version_final_part' not in kernel_versions:
logger.error("=> ERROR: Could not obtain kernel version to create the initramfs.")
raise ValueError("Could not obtain kernel version.")
kernel_version_final_part = kernel_versions["version_final_part"]
kernel_modules_dir = get_kernel_module_dir_in_chroot(rootfs_path, kernel_version_final_part)
KERNELVERSION = execute_xbps_uhelper_chroot(rootfs_path, ["getpkgversion", kernel_version_final_part], iso_build_config)
dracut_command = [
"chroot",
rootfs_path,
xbps_commands["DRACUT_CMD"],
"-N",
"--force",
f"--kmoddir={kernel_modules_dir}",
"--add-drivers", "ahci",
"--force-add", "vmklive",
"--omit", "systemd",
"/boot/initrd",
kernel_version_final_part,
]
try:
subprocess.run(dracut_command, check=True)
except subprocess.CalledProcessError as e:
logger.error(f"=> Error executing dracut: Exit code: {e.returncode}")
logger.error(f"=> Failed command: {e.cmd}")
logger.error(f"=> Stderr: {e.stderr.decode('utf-8', errors='ignore') if e.stderr else '<empty>'}")
raise
else:
logger.info("=> Initramfs created successfully using dracut!")
initrd_path_in_rootfs = os.path.join(rootfs_path, "boot", "initrd")
initrd_path_in_boot = os.path.join(boot_path, "initrd")
logger.info(f"=> Moving initramfs from: {initrd_path_in_rootfs} to: {initrd_path_in_boot}...")
shutil.move(initrd_path_in_rootfs, initrd_path_in_boot)
logger.info(f"=> Initramfs moved successfully to: {initrd_path_in_boot}")
return initrd_path_in_boot, kernel_versions