feat(boot): introduce sort keys, which are sorted in reverse order, to make boot order more configurable (#55)

This commit is contained in:
2025-12-21 14:11:26 -08:00
committed by GitHub
parent d50f22a386
commit 6a57c72869
6 changed files with 40 additions and 6 deletions

View File

@@ -1,5 +1,4 @@
use crate::utils; use crate::utils;
use crate::utils::vercmp;
use alloc::collections::BTreeMap; use alloc::collections::BTreeMap;
use alloc::string::{String, ToString}; use alloc::string::{String, ToString};
use alloc::vec::Vec; use alloc::vec::Vec;
@@ -186,17 +185,15 @@ pub fn scan(
return Ok(false); return Ok(false);
} }
// Sort the kernel pairs by kernel version, if it has one, newer kernels first.
pairs.sort_by(|a, b| vercmp::compare_versions(&a.kernel, &b.kernel).reverse());
// Generate a unique name for the linux chainload action. // Generate a unique name for the linux chainload action.
let chainload_action_name = format!("{}{}", LINUX_CHAINLOAD_ACTION_PREFIX, root_unique_hash,); let chainload_action_name = format!("{}{}", LINUX_CHAINLOAD_ACTION_PREFIX, root_unique_hash);
// Kernel pairs are detected, generate a list configuration for it. // Kernel pairs are detected, generate a list configuration for it.
let generator = ListConfiguration { let generator = ListConfiguration {
entry: EntryDeclaration { entry: EntryDeclaration {
title: "Boot Linux $name".to_string(), title: "Boot Linux $name".to_string(),
actions: vec![chainload_action_name.clone()], actions: vec![chainload_action_name.clone()],
sort_key: Some("$kernel".to_string()),
..Default::default() ..Default::default()
}, },
values: pairs values: pairs

View File

@@ -51,13 +51,14 @@ pub fn scan(
let chainload_action_name = format!("{}{}", WINDOWS_CHAINLOAD_ACTION_PREFIX, root_unique_hash,); let chainload_action_name = format!("{}{}", WINDOWS_CHAINLOAD_ACTION_PREFIX, root_unique_hash,);
// Generate an entry name for Windows. // Generate an entry name for Windows.
let entry_name = format!("auto-windows-{}", root_unique_hash,); let entry_name = format!("auto-windows-{}", root_unique_hash);
// Create an entry for Windows and insert it into the configuration. // Create an entry for Windows and insert it into the configuration.
let entry = EntryDeclaration { let entry = EntryDeclaration {
title: "Boot Windows".to_string(), title: "Boot Windows".to_string(),
actions: vec![chainload_action_name.clone()], actions: vec![chainload_action_name.clone()],
values: Default::default(), values: Default::default(),
sort_key: None, // Use the default sort key.
}; };
config.entries.insert(entry_name, entry); config.entries.insert(entry_name, entry);

View File

@@ -12,6 +12,7 @@ pub struct BootableEntry {
declaration: EntryDeclaration, declaration: EntryDeclaration,
default: bool, default: bool,
pin_name: bool, pin_name: bool,
sort_key: Option<String>,
} }
impl BootableEntry { impl BootableEntry {
@@ -29,6 +30,7 @@ impl BootableEntry {
declaration, declaration,
default: false, default: false,
pin_name: false, pin_name: false,
sort_key: None,
} }
} }
@@ -107,6 +109,22 @@ impl BootableEntry {
self.name == needle || self.title == needle self.name == needle || self.title == needle
} }
/// Set the sort key of the entry. This is used to sort entries via version comparison.
pub fn set_sort_key(&mut self, sort_key: String) {
self.sort_key = Some(sort_key);
}
/// Retrieve a reference to the sort key of the entry. If one is not specified, we will use the
/// name of the entry.
pub fn sort_key(&self) -> &str {
// Use the sort key specified in the bootable entry, or use the declaration sort key,
// or use the name of the entry.
self.sort_key
.as_deref()
.or(self.declaration.sort_key.as_deref())
.unwrap_or(&self.name)
}
/// Find an entry by `needle` inside the entry iterator `haystack`. /// Find an entry by `needle` inside the entry iterator `haystack`.
/// This will search for an entry by name, title, or index. /// This will search for an entry by name, title, or index.
pub fn find<'a>( pub fn find<'a>(

View File

@@ -206,6 +206,16 @@ pub fn generate(context: Rc<SproutContext>, bls: &BlsConfiguration) -> Result<Ve
// Sort all the entries according to the BLS sort system. // Sort all the entries according to the BLS sort system.
entries.sort_by(sort_entries); entries.sort_by(sort_entries);
// Grab the number of entries that we have, so we can calculate a reverse index.
let entry_count = entries.len();
// Set the sort keys of all the bootable entries to a semi-unique prefix + the BLS sort order.
// The final comparison happens using version comparison, so this will sort
// things properly.
for (idx, (_bls, boot)) in entries.iter_mut().enumerate() {
boot.set_sort_key(format!("bls-{}-{}", path, entry_count - idx - 1));
}
// Collect all the bootable entries and return them. // Collect all the bootable entries and return them.
Ok(entries.into_iter().map(|(_, boot)| boot).collect()) Ok(entries.into_iter().map(|(_, boot)| boot).collect())
} }

View File

@@ -7,6 +7,7 @@ use crate::context::{RootContext, SproutContext};
use crate::entries::BootableEntry; use crate::entries::BootableEntry;
use crate::options::SproutOptions; use crate::options::SproutOptions;
use crate::phases::phase; use crate::phases::phase;
use crate::utils::vercmp::compare_versions;
use alloc::collections::BTreeMap; use alloc::collections::BTreeMap;
use alloc::format; use alloc::format;
use alloc::string::ToString; use alloc::string::ToString;
@@ -257,6 +258,10 @@ fn run() -> Result<()> {
} }
} }
// Sort the entries by their sort key, finalizing the order to show entries. This happens
// in reverse order so that entries that would come last show up first in the menu.
entries.sort_by(|a, b| compare_versions(a.sort_key(), b.sort_key()).reverse());
// Tell the bootloader interface what entries are available. // Tell the bootloader interface what entries are available.
BootloaderInterface::set_entries(entries.iter().map(|entry| entry.name())) BootloaderInterface::set_entries(entries.iter().map(|entry| entry.name()))
.context("unable to set entries in bootloader interface")?; .context("unable to set entries in bootloader interface")?;

View File

@@ -18,4 +18,7 @@ pub struct EntryDeclaration {
/// The values to insert into the context when the entry is selected. /// The values to insert into the context when the entry is selected.
#[serde(default)] #[serde(default)]
pub values: BTreeMap<String, String>, pub values: BTreeMap<String, String>,
/// The key to sort entries, via version comparison.
#[serde(default, rename = "sort-key")]
pub sort_key: Option<String>,
} }