mirror of
https://git.launchpad.net/livecd-rootfs
synced 2026-03-05 23:28:47 +00:00
Make generate_grub_config return strings instead of writing files
Separate config generation from file I/O by having generate_grub_config() and its helpers return strings. The base class make_bootable() now handles writing grub.cfg. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a5cffa8414
commit
a6466ab0a3
@ -87,102 +87,95 @@ class AMD64BootConfigurator(UEFIBootConfigurator):
|
|||||||
["*.mod", "*.lst", "*.o"],
|
["*.mod", "*.lst", "*.o"],
|
||||||
)
|
)
|
||||||
|
|
||||||
def generate_grub_config(self) -> None:
|
def generate_grub_config(self) -> str:
|
||||||
"""Generate grub.cfg and loopback.cfg for the boot tree."""
|
"""Generate grub.cfg content for AMD64."""
|
||||||
boot_grub_dir = self.iso_root.joinpath("boot", "grub")
|
result = self.grub_header()
|
||||||
boot_grub_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
grub_cfg = boot_grub_dir.joinpath("grub.cfg")
|
|
||||||
|
|
||||||
if self.project == "ubuntu-mini-iso":
|
if self.project == "ubuntu-mini-iso":
|
||||||
self.write_grub_header(grub_cfg)
|
result += """\
|
||||||
with grub_cfg.open("a") as f:
|
menuentry "Choose an Ubuntu version to install" {
|
||||||
f.write(
|
|
||||||
"""menuentry "Choose an Ubuntu version to install" {
|
|
||||||
set gfxpayload=keep
|
set gfxpayload=keep
|
||||||
linux /casper/vmlinuz iso-chooser-menu ip=dhcp ---
|
linux /casper/vmlinuz iso-chooser-menu ip=dhcp ---
|
||||||
initrd /casper/initrd
|
initrd /casper/initrd
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
)
|
return result
|
||||||
return
|
|
||||||
|
|
||||||
# Generate grub.cfg
|
|
||||||
kernel_params = default_kernel_params(self.project)
|
kernel_params = default_kernel_params(self.project)
|
||||||
|
|
||||||
# Write common GRUB header
|
|
||||||
self.write_grub_header(grub_cfg)
|
|
||||||
|
|
||||||
# Main menu entry
|
# Main menu entry
|
||||||
with grub_cfg.open("a") as f:
|
result += f"""\
|
||||||
f.write(
|
menuentry "Try or Install {self.humanproject}" {{
|
||||||
f"""menuentry "Try or Install {self.humanproject}" {{
|
|
||||||
set gfxpayload=keep
|
set gfxpayload=keep
|
||||||
linux /casper/vmlinuz {kernel_params}
|
linux /casper/vmlinuz {kernel_params}
|
||||||
initrd /casper/initrd
|
initrd /casper/initrd
|
||||||
}}
|
}}
|
||||||
"""
|
"""
|
||||||
)
|
|
||||||
|
|
||||||
# All but server get safe-graphics mode
|
# All but server get safe-graphics mode
|
||||||
if self.project != "ubuntu-server":
|
if self.project != "ubuntu-server":
|
||||||
with grub_cfg.open("a") as f:
|
result += f"""\
|
||||||
f.write(
|
menuentry "{self.humanproject} (safe graphics)" {{
|
||||||
f"""menuentry "{self.humanproject} (safe graphics)" {{
|
|
||||||
set gfxpayload=keep
|
set gfxpayload=keep
|
||||||
linux /casper/vmlinuz nomodeset {kernel_params}
|
linux /casper/vmlinuz nomodeset {kernel_params}
|
||||||
initrd /casper/initrd
|
initrd /casper/initrd
|
||||||
}}
|
}}
|
||||||
"""
|
"""
|
||||||
)
|
|
||||||
|
|
||||||
# ubiquity based projects get OEM mode
|
# ubiquity based projects get OEM mode
|
||||||
if "maybe-ubiquity" in kernel_params:
|
if "maybe-ubiquity" in kernel_params:
|
||||||
oem_kernel_params = kernel_params.replace(
|
oem_kernel_params = kernel_params.replace(
|
||||||
"maybe-ubiquity", "only-ubiquity oem-config/enable=true"
|
"maybe-ubiquity", "only-ubiquity oem-config/enable=true"
|
||||||
)
|
)
|
||||||
with grub_cfg.open("a") as f:
|
result += f"""\
|
||||||
f.write(
|
menuentry "OEM install (for manufacturers)" {{
|
||||||
f"""menuentry "OEM install (for manufacturers)" {{
|
|
||||||
set gfxpayload=keep
|
set gfxpayload=keep
|
||||||
linux /casper/vmlinuz {oem_kernel_params}
|
linux /casper/vmlinuz {oem_kernel_params}
|
||||||
initrd /casper/initrd
|
initrd /casper/initrd
|
||||||
}}
|
}}
|
||||||
"""
|
"""
|
||||||
)
|
|
||||||
|
|
||||||
# Calamares-based projects get OEM mode
|
# Calamares-based projects get OEM mode
|
||||||
if self.project in CALAMARES_PROJECTS:
|
if self.project in CALAMARES_PROJECTS:
|
||||||
with grub_cfg.open("a") as f:
|
result += f"""\
|
||||||
f.write(
|
menuentry "OEM install (for manufacturers)" {{
|
||||||
f"""menuentry "OEM install (for manufacturers)" {{
|
|
||||||
set gfxpayload=keep
|
set gfxpayload=keep
|
||||||
linux /casper/vmlinuz {kernel_params} oem-config/enable=true
|
linux /casper/vmlinuz {kernel_params} oem-config/enable=true
|
||||||
initrd /casper/initrd
|
initrd /casper/initrd
|
||||||
}}
|
}}
|
||||||
"""
|
"""
|
||||||
)
|
|
||||||
|
|
||||||
# Currently only server is built with HWE, hence no safe-graphics/OEM
|
# Currently only server is built with HWE, hence no safe-graphics/OEM
|
||||||
if self.hwe:
|
if self.hwe:
|
||||||
with grub_cfg.open("a") as f:
|
result += f"""\
|
||||||
f.write(
|
menuentry "{self.humanproject} with the HWE kernel" {{
|
||||||
f"""menuentry "{self.humanproject} with the HWE kernel" {{
|
|
||||||
set gfxpayload=keep
|
set gfxpayload=keep
|
||||||
linux /casper/hwe-vmlinuz {kernel_params}
|
linux /casper/hwe-vmlinuz {kernel_params}
|
||||||
initrd /casper/hwe-initrd
|
initrd /casper/hwe-initrd
|
||||||
}}
|
}}
|
||||||
"""
|
"""
|
||||||
)
|
|
||||||
|
|
||||||
# Create the loopback config, based on the main config
|
# UEFI Entries (wrapped in grub_platform check for dual BIOS/UEFI support)
|
||||||
with grub_cfg.open("r") as f:
|
uefi_menu_entries = self.uefi_menu_entries()
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
# sed: delete from line 1 to menu_color_highlight, delete from
|
result += f"""\
|
||||||
# grub_platform to end and replace '---' with
|
grub_platform
|
||||||
# 'iso-scan/filename=${iso_path} ---' in lines with 'linux'
|
if [ "$grub_platform" = "efi" ]; then
|
||||||
lines = content.split("\n")
|
{uefi_menu_entries}
|
||||||
|
fi
|
||||||
|
"""
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def generate_loopback_config(grub_content: str) -> str:
|
||||||
|
"""Derive loopback.cfg from grub.cfg content.
|
||||||
|
|
||||||
|
Strips the header (up to menu_color_highlight) and the UEFI
|
||||||
|
trailer (from grub_platform to end), and adds iso-scan/filename
|
||||||
|
to linux lines.
|
||||||
|
"""
|
||||||
|
lines = grub_content.split("\n")
|
||||||
start_idx = 0
|
start_idx = 0
|
||||||
for i, line in enumerate(lines):
|
for i, line in enumerate(lines):
|
||||||
if "menu_color_highlight" in line:
|
if "menu_color_highlight" in line:
|
||||||
@ -205,16 +198,20 @@ class AMD64BootConfigurator(UEFIBootConfigurator):
|
|||||||
for line in loopback_lines
|
for line in loopback_lines
|
||||||
]
|
]
|
||||||
|
|
||||||
loopback_cfg = boot_grub_dir.joinpath("loopback.cfg")
|
return "\n".join(loopback_lines)
|
||||||
with loopback_cfg.open("w") as f:
|
|
||||||
f.write("\n".join(loopback_lines))
|
|
||||||
|
|
||||||
# UEFI Entries (wrapped in grub_platform check for dual BIOS/UEFI support)
|
def make_bootable(
|
||||||
with grub_cfg.open("a") as f:
|
self,
|
||||||
f.write("grub_platform\n")
|
workdir: pathlib.Path,
|
||||||
f.write('if [ "$grub_platform" = "efi" ]; then\n')
|
project: str,
|
||||||
|
capproject: str,
|
||||||
self.write_uefi_menu_entries(grub_cfg)
|
subarch: str,
|
||||||
|
hwe: bool,
|
||||||
with grub_cfg.open("a") as f:
|
) -> None:
|
||||||
f.write("fi\n")
|
"""Make the ISO bootable, including generating loopback.cfg."""
|
||||||
|
super().make_bootable(workdir, project, capproject, subarch, hwe)
|
||||||
|
grub_cfg = self.iso_root.joinpath("boot", "grub", "grub.cfg")
|
||||||
|
grub_content = grub_cfg.read_text()
|
||||||
|
self.iso_root.joinpath("boot", "grub", "loopback.cfg").write_text(
|
||||||
|
self.generate_loopback_config(grub_content)
|
||||||
|
)
|
||||||
|
|||||||
@ -33,19 +33,15 @@ class ARM64BootConfigurator(UEFIBootConfigurator):
|
|||||||
with self.logger.logged("extracting ARM64 boot files"):
|
with self.logger.logged("extracting ARM64 boot files"):
|
||||||
self.extract_uefi_files()
|
self.extract_uefi_files()
|
||||||
|
|
||||||
def generate_grub_config(self) -> None:
|
def generate_grub_config(self) -> str:
|
||||||
"""Generate grub.cfg for ARM64."""
|
"""Generate grub.cfg for ARM64."""
|
||||||
kernel_params = default_kernel_params(self.project)
|
kernel_params = default_kernel_params(self.project)
|
||||||
|
|
||||||
grub_cfg = self.iso_root.joinpath("boot", "grub", "grub.cfg")
|
result = self.grub_header()
|
||||||
|
|
||||||
# Write common GRUB header
|
|
||||||
self.write_grub_header(grub_cfg)
|
|
||||||
|
|
||||||
# ARM64-specific: Snapdragon workarounds
|
# ARM64-specific: Snapdragon workarounds
|
||||||
with grub_cfg.open("a") as f:
|
result += f"""\
|
||||||
f.write(
|
set cmdline=
|
||||||
"""set cmdline=
|
|
||||||
smbios --type 4 --get-string 5 --set proc_version
|
smbios --type 4 --get-string 5 --set proc_version
|
||||||
regexp "Snapdragon.*" "$proc_version"
|
regexp "Snapdragon.*" "$proc_version"
|
||||||
if [ $? = 0 ]; then
|
if [ $? = 0 ]; then
|
||||||
@ -63,11 +59,9 @@ menuentry "Try or Install {self.humanproject}" {{
|
|||||||
\tinitrd\t/casper/initrd
|
\tinitrd\t/casper/initrd
|
||||||
}}
|
}}
|
||||||
"""
|
"""
|
||||||
)
|
|
||||||
|
|
||||||
# HWE kernel option if available
|
# HWE kernel option if available
|
||||||
self.write_hwe_menu_entry(
|
result += self.hwe_menu_entry(
|
||||||
grub_cfg,
|
|
||||||
"vmlinuz",
|
"vmlinuz",
|
||||||
f"{kernel_params} console=tty0",
|
f"{kernel_params} console=tty0",
|
||||||
extra_params="$cmdline ",
|
extra_params="$cmdline ",
|
||||||
@ -77,4 +71,6 @@ menuentry "Try or Install {self.humanproject}" {{
|
|||||||
# but it's not actually set anywhere in the grub.cfg, so we omit it here
|
# but it's not actually set anywhere in the grub.cfg, so we omit it here
|
||||||
|
|
||||||
# UEFI Entries (ARM64 is UEFI-only, no grub_platform check needed)
|
# UEFI Entries (ARM64 is UEFI-only, no grub_platform check needed)
|
||||||
self.write_uefi_menu_entries(grub_cfg)
|
result += self.uefi_menu_entries()
|
||||||
|
|
||||||
|
return result
|
||||||
|
|||||||
@ -39,59 +39,52 @@ class GrubBootConfigurator(BaseBootConfigurator):
|
|||||||
Subclasses must implement generate_grub_config().
|
Subclasses must implement generate_grub_config().
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def write_grub_header(
|
def grub_header(self, include_loadfont: bool = True) -> str:
|
||||||
self, grub_cfg: pathlib.Path, include_loadfont: bool = True
|
"""Return common GRUB config header (timeout, colors).
|
||||||
) -> None:
|
|
||||||
"""Write common GRUB config header (timeout, colors).
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
grub_cfg: Path to grub.cfg file
|
|
||||||
include_loadfont: Whether to include 'loadfont unicode'
|
include_loadfont: Whether to include 'loadfont unicode'
|
||||||
(not needed for RISC-V)
|
(not needed for RISC-V)
|
||||||
"""
|
"""
|
||||||
with grub_cfg.open("a") as f:
|
result = "set timeout=30\n\n"
|
||||||
f.write("set timeout=30\n\n")
|
if include_loadfont:
|
||||||
if include_loadfont:
|
result += "loadfont unicode\n\n"
|
||||||
f.write("loadfont unicode\n\n")
|
result += """\
|
||||||
f.write(
|
set menu_color_normal=white/black
|
||||||
"""set menu_color_normal=white/black
|
|
||||||
set menu_color_highlight=black/light-gray
|
set menu_color_highlight=black/light-gray
|
||||||
|
|
||||||
"""
|
"""
|
||||||
)
|
return result
|
||||||
|
|
||||||
def write_hwe_menu_entry(
|
def hwe_menu_entry(
|
||||||
self,
|
self,
|
||||||
grub_cfg: pathlib.Path,
|
|
||||||
kernel_name: str,
|
kernel_name: str,
|
||||||
kernel_params: str,
|
kernel_params: str,
|
||||||
extra_params: str = "",
|
extra_params: str = "",
|
||||||
) -> None:
|
) -> str:
|
||||||
"""Write HWE kernel menu entry if HWE is enabled.
|
"""Return HWE kernel menu entry if HWE is enabled.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
grub_cfg: Path to grub.cfg file
|
|
||||||
kernel_name: Kernel binary name (vmlinuz or vmlinux)
|
kernel_name: Kernel binary name (vmlinuz or vmlinux)
|
||||||
kernel_params: Kernel parameters to append
|
kernel_params: Kernel parameters to append
|
||||||
extra_params: Additional parameters (e.g., console=tty0, $cmdline)
|
extra_params: Additional parameters (e.g., console=tty0, $cmdline)
|
||||||
"""
|
"""
|
||||||
if self.hwe:
|
if not self.hwe:
|
||||||
with grub_cfg.open("a") as f:
|
return ""
|
||||||
f.write(
|
return f"""\
|
||||||
f"""menuentry "{self.humanproject} with the HWE kernel" {{
|
menuentry "{self.humanproject} with the HWE kernel" {{
|
||||||
\tset gfxpayload=keep
|
set gfxpayload=keep
|
||||||
\tlinux\t/casper/hwe-{kernel_name} {extra_params}{kernel_params}
|
linux /casper/hwe-{kernel_name} {extra_params}{kernel_params}
|
||||||
\tinitrd\t/casper/hwe-initrd
|
initrd /casper/hwe-initrd
|
||||||
}}
|
}}
|
||||||
"""
|
"""
|
||||||
)
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def generate_grub_config(self) -> None:
|
def generate_grub_config(self) -> str:
|
||||||
"""Generate grub.cfg configuration file.
|
"""Generate grub.cfg content.
|
||||||
|
|
||||||
Each GRUB-based architecture must implement this to create its
|
Each GRUB-based architecture must implement this to return the
|
||||||
specific GRUB configuration.
|
GRUB configuration.
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
@ -106,4 +99,7 @@ set menu_color_highlight=black/light-gray
|
|||||||
"""Make the ISO bootable by extracting files and generating GRUB config."""
|
"""Make the ISO bootable by extracting files and generating GRUB config."""
|
||||||
super().make_bootable(workdir, project, capproject, subarch, hwe)
|
super().make_bootable(workdir, project, capproject, subarch, hwe)
|
||||||
with self.logger.logged("generating grub config"):
|
with self.logger.logged("generating grub config"):
|
||||||
self.generate_grub_config()
|
content = self.generate_grub_config()
|
||||||
|
grub_dir = self.iso_root.joinpath("boot", "grub")
|
||||||
|
grub_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
grub_dir.joinpath("grub.cfg").write_text(content)
|
||||||
|
|||||||
@ -53,27 +53,22 @@ class PPC64ELBootConfigurator(GrubBootConfigurator):
|
|||||||
grub_pkg_dir, self.iso_root, "powerpc-ieee1275", ["*.mod", "*.lst"]
|
grub_pkg_dir, self.iso_root, "powerpc-ieee1275", ["*.mod", "*.lst"]
|
||||||
)
|
)
|
||||||
|
|
||||||
def generate_grub_config(self) -> None:
|
def generate_grub_config(self) -> str:
|
||||||
"""Generate grub.cfg for PPC64EL."""
|
"""Generate grub.cfg for PPC64EL."""
|
||||||
kernel_params = default_kernel_params(self.project)
|
kernel_params = default_kernel_params(self.project)
|
||||||
|
|
||||||
grub_cfg = self.iso_root.joinpath("boot", "grub", "grub.cfg")
|
result = self.grub_header()
|
||||||
|
|
||||||
# Write common GRUB header
|
|
||||||
self.write_grub_header(grub_cfg)
|
|
||||||
|
|
||||||
# Main menu entry
|
# Main menu entry
|
||||||
with grub_cfg.open("a") as f:
|
result += f"""\
|
||||||
f.write(
|
menuentry "Try or Install {self.humanproject}" {{
|
||||||
f"""menuentry "Try or Install {self.humanproject}" {{
|
set gfxpayload=keep
|
||||||
\tset gfxpayload=keep
|
linux /casper/vmlinux quiet {kernel_params}
|
||||||
\tlinux\t/casper/vmlinux quiet {kernel_params}
|
initrd /casper/initrd
|
||||||
\tinitrd\t/casper/initrd
|
|
||||||
}}
|
}}
|
||||||
"""
|
"""
|
||||||
)
|
|
||||||
|
|
||||||
# HWE kernel option if available
|
# HWE kernel option if available
|
||||||
self.write_hwe_menu_entry(
|
result += self.hwe_menu_entry("vmlinux", kernel_params, extra_params="quiet ")
|
||||||
grub_cfg, "vmlinux", kernel_params, extra_params="quiet "
|
|
||||||
)
|
return result
|
||||||
|
|||||||
@ -126,35 +126,28 @@ class RISCV64BootConfigurator(GrubBootConfigurator):
|
|||||||
# Add DTBs to ESP
|
# Add DTBs to ESP
|
||||||
self.logger.run(["mcopy", "-s", "-i", efi_img, dtb_dir, "::/."], check=True)
|
self.logger.run(["mcopy", "-s", "-i", efi_img, dtb_dir, "::/."], check=True)
|
||||||
|
|
||||||
def generate_grub_config(self) -> None:
|
def generate_grub_config(self) -> str:
|
||||||
"""Generate grub.cfg for RISC-V64."""
|
"""Generate grub.cfg for RISC-V64."""
|
||||||
grub_dir = self.iso_root.joinpath("boot", "grub")
|
result = self.grub_header(include_loadfont=False)
|
||||||
grub_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
grub_cfg = grub_dir.joinpath("grub.cfg")
|
|
||||||
|
|
||||||
# Write GRUB header (without loadfont for RISC-V)
|
|
||||||
self.write_grub_header(grub_cfg, include_loadfont=False)
|
|
||||||
|
|
||||||
# Main menu entry
|
# Main menu entry
|
||||||
with grub_cfg.open("a") as f:
|
result += f"""\
|
||||||
f.write(
|
menuentry "Try or Install {self.humanproject}" {{
|
||||||
f"""menuentry "Try or Install {self.humanproject}" {{
|
set gfxpayload=keep
|
||||||
\tset gfxpayload=keep
|
linux /casper/vmlinux efi=debug sysctl.kernel.watchdog_thresh=60 ---
|
||||||
\tlinux\t/casper/vmlinux efi=debug sysctl.kernel.watchdog_thresh=60 ---
|
initrd /casper/initrd
|
||||||
\tinitrd\t/casper/initrd
|
|
||||||
}}
|
}}
|
||||||
"""
|
"""
|
||||||
)
|
|
||||||
|
|
||||||
# HWE kernel option if available
|
# HWE kernel option if available
|
||||||
self.write_hwe_menu_entry(
|
result += self.hwe_menu_entry(
|
||||||
grub_cfg,
|
|
||||||
"vmlinux",
|
"vmlinux",
|
||||||
"---",
|
"---",
|
||||||
extra_params="efi=debug sysctl.kernel.watchdog_thresh=60 ",
|
extra_params="efi=debug sysctl.kernel.watchdog_thresh=60 ",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
def post_process_iso(self, iso_path: pathlib.Path) -> None:
|
def post_process_iso(self, iso_path: pathlib.Path) -> None:
|
||||||
"""Add GPT partitions with U-Boot for SiFive Unmatched board.
|
"""Add GPT partitions with U-Boot for SiFive Unmatched board.
|
||||||
|
|
||||||
|
|||||||
@ -122,18 +122,16 @@ class UEFIBootConfigurator(GrubBootConfigurator):
|
|||||||
self.logger, self.iso_root, self.scratch.joinpath("cd-boot-efi.img")
|
self.logger, self.iso_root, self.scratch.joinpath("cd-boot-efi.img")
|
||||||
)
|
)
|
||||||
|
|
||||||
def write_uefi_menu_entries(self, grub_cfg: pathlib.Path) -> None:
|
def uefi_menu_entries(self) -> str:
|
||||||
"""Write UEFI firmware menu entries."""
|
"""Return UEFI firmware menu entries."""
|
||||||
with grub_cfg.open("a") as f:
|
return """\
|
||||||
f.write(
|
menuentry 'Boot from next volume' {
|
||||||
"""menuentry 'Boot from next volume' {
|
|
||||||
\texit 1
|
\texit 1
|
||||||
}
|
}
|
||||||
menuentry 'UEFI Firmware Settings' {
|
menuentry 'UEFI Firmware Settings' {
|
||||||
\tfwsetup
|
\tfwsetup
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
)
|
|
||||||
|
|
||||||
def get_uefi_mkisofs_opts(self) -> list[str | pathlib.Path]:
|
def get_uefi_mkisofs_opts(self) -> list[str | pathlib.Path]:
|
||||||
"""Return common UEFI mkisofs options."""
|
"""Return common UEFI mkisofs options."""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user