Scan more directories in auto_deps

Closes: redox-os/redox#1600, redox-os/redox#1598
Probably closes: redox-os/installer#24

Some packages place files in nested directories, such as
`lib/packagename` or `libexec/ARCH/gcc`. If these directories are not
scanned, the dependencies key in stage.toml will be empty which leads to
dependencies not being installed in the image.
This commit is contained in:
Josh Megnauth 2025-07-01 03:38:57 -04:00
parent fa5cfdb00c
commit 57d4249847
No known key found for this signature in database
GPG Key ID: 70813183462EFAD3
3 changed files with 62 additions and 1 deletions

1
Cargo.lock generated
View File

@ -1853,6 +1853,7 @@ dependencies = [
"pkgar-keys 0.1.17",
"redoxer",
"serde",
"tempfile",
"termion",
"toml 0.8.19",
"walkdir",

View File

@ -32,3 +32,6 @@ serde = { version = "=1.0.197", features = ["derive"] }
termion = "4"
toml = "0.8"
walkdir = "2.3.1"
[dev-dependencies]
tempfile = "3"

View File

@ -2,6 +2,7 @@ use cookbook::blake3::blake3_progress;
use cookbook::package::StageToml;
use cookbook::recipe::{BuildKind, CookRecipe, Recipe, SourceRecipe};
use cookbook::recipe_find::recipe_find;
use std::collections::VecDeque;
use std::{
collections::BTreeSet,
env, fs,
@ -505,7 +506,32 @@ fi"#,
fn auto_deps(stage_dir: &Path, dep_pkgars: &BTreeSet<(String, PathBuf)>) -> BTreeSet<String> {
let mut paths = BTreeSet::new();
for dir in &[stage_dir.join("usr/bin"), stage_dir.join("usr/lib")] {
let mut visited = BTreeSet::new();
// Base directories may need to be updated for packages that place binaries in odd locations.
let mut walk = VecDeque::from([
stage_dir.join("libexec"),
stage_dir.join("usr/bin"),
stage_dir.join("usr/games"),
stage_dir.join("usr/lib"),
]);
// Recursively (DFS) walk each directory to ensure nested libs and bins are checked.
while let Some(dir) = walk.pop_front() {
let Ok(dir) = dir.canonicalize() else {
continue;
};
if visited.contains(&dir) {
#[cfg(debug_assertions)]
eprintln!("DEBUG: auto_deps => Skipping `{dir:?}` (already visited)");
continue;
}
assert!(
visited.insert(dir.clone()),
"Directory `{:?}` should not be in visited\nVisited: {:#?}",
dir,
visited
);
let Ok(read_dir) = fs::read_dir(&dir) else {
continue;
};
@ -516,6 +542,8 @@ fn auto_deps(stage_dir: &Path, dep_pkgars: &BTreeSet<(String, PathBuf)>) -> BTre
};
if file_type.is_file() {
paths.insert(entry.path());
} else if file_type.is_dir() {
walk.push_front(entry.path());
}
}
}
@ -1185,3 +1213,32 @@ fn main() {
}
}
}
#[cfg(test)]
mod tests {
use std::os::unix;
use super::auto_deps;
#[test]
fn file_system_loop_no_infinite_loop() {
// Hierarchy with an infinite loop
let temp = tempfile::tempdir().unwrap();
let root = temp.path();
let dir = root.join("loop");
unix::fs::symlink(root, &dir).expect("Linking {dir:?} to {root:?}");
// Sanity check that we have a loop
assert_eq!(
root.canonicalize().unwrap(),
dir.canonicalize().unwrap(),
"Expected a loop where {dir:?} points to {root:?}"
);
let entries = auto_deps(root, &Default::default());
assert!(
entries.is_empty(),
"auto_deps shouldn't have yielded any libraries"
);
}
}