fix: topological sort cookbook dependency resolution
- Added Kahn's algorithm topological sort to new_recursive() in src/recipe.rs - BFS previously returned flat dependency list with dependents before deps, causing stage.pkgar 'Not Found' when cooking in list order - Now deps always cook before dependents (kdecoration before breeze, kf6-extra-cmake-modules before kde-cli-tools, etc.) - Falls back to original order on dependency cycles - Verified: kdecoration, kwin, plasma-wayland-protocols, KF6 packages all cook successfully in correct dependency order - kirigami still fails (needs Qt6 QML headers — known QML gate)
This commit is contained in:
+57
-2
@@ -1,5 +1,5 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeSet, VecDeque},
|
collections::{BTreeSet, BTreeMap, VecDeque},
|
||||||
convert::TryInto,
|
convert::TryInto,
|
||||||
fs,
|
fs,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
@@ -363,7 +363,11 @@ impl CookRecipe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(recipes)
|
// Topological sort: ensure dependencies come before dependents.
|
||||||
|
// Without this, the flat BFS list has dependents before their deps,
|
||||||
|
// causing stage.pkgar "Not Found" failures when cooking in list order.
|
||||||
|
let sorted = topological_sort(recipes)?;
|
||||||
|
Ok(sorted)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_build_deps_recursive(
|
pub fn get_build_deps_recursive(
|
||||||
@@ -521,6 +525,57 @@ impl CookRecipe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Topologically sort a list of CookRecipes so that dependencies always
|
||||||
|
/// appear before the recipes that depend on them. Uses Kahn's algorithm.
|
||||||
|
/// Falls back to original order on cycles.
|
||||||
|
fn topological_sort(recipes: Vec<CookRecipe>) -> Result<Vec<CookRecipe>, PackageError> {
|
||||||
|
if recipes.len() <= 1 {
|
||||||
|
return Ok(recipes);
|
||||||
|
}
|
||||||
|
|
||||||
|
let name_to_idx: BTreeMap<PackageName, usize> = recipes
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, r)| (r.name.clone(), i))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut dep_counts: Vec<usize> = vec![0; recipes.len()];
|
||||||
|
let mut reverse_deps: Vec<Vec<usize>> = vec![Vec::new(); recipes.len()];
|
||||||
|
|
||||||
|
for (i, recipe) in recipes.iter().enumerate() {
|
||||||
|
for dep in &recipe.recipe.build.dependencies {
|
||||||
|
if let Some(&dep_idx) = name_to_idx.get(dep) {
|
||||||
|
dep_counts[i] = dep_counts[i].saturating_add(1);
|
||||||
|
reverse_deps[dep_idx].push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut queue: VecDeque<usize> = VecDeque::new();
|
||||||
|
for (i, &count) in dep_counts.iter().enumerate() {
|
||||||
|
if count == 0 {
|
||||||
|
queue.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sorted = Vec::with_capacity(recipes.len());
|
||||||
|
while let Some(idx) = queue.pop_front() {
|
||||||
|
sorted.push(recipes[idx].clone());
|
||||||
|
for &dep_idx in &reverse_deps[idx] {
|
||||||
|
dep_counts[dep_idx] = dep_counts[dep_idx].saturating_sub(1);
|
||||||
|
if dep_counts[dep_idx] == 0 {
|
||||||
|
queue.push_back(dep_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sorted.len() != recipes.len() {
|
||||||
|
return Ok(recipes);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(sorted)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Wrap these vectors in a struct
|
// TODO: Wrap these vectors in a struct
|
||||||
|
|
||||||
pub fn recipes_mark_as_deps(names: &[PackageName], packages: &mut Vec<CookRecipe>) {
|
pub fn recipes_mark_as_deps(names: &[PackageName], packages: &mut Vec<CookRecipe>) {
|
||||||
|
|||||||
Reference in New Issue
Block a user