More stuff, didn't test

This commit is contained in:
Marvin Friedrich
2026-05-19 18:12:11 +02:00
parent b71906f402
commit 312750c61b
32 changed files with 978 additions and 170 deletions
+129 -22
View File
@@ -49,8 +49,9 @@ impl Builder {
rebuild: bool,
dry_run: bool,
) -> anyhow::Result<()> {
let requested = filter_skipped(recipes, requested);
let plan = TaskPlanner::new(&self.root, &self.config.arch, recipes)
.build_plan(requested, rebuild)?;
.build_plan(&requested, rebuild)?;
self.print_plan(&plan);
if dry_run {
return Ok(());
@@ -64,8 +65,9 @@ impl Builder {
requested: &[String],
dry_run: bool,
) -> anyhow::Result<()> {
let requested = filter_skipped(recipes, requested);
let plan =
TaskPlanner::new(&self.root, &self.config.arch, recipes).fetch_plan(requested)?;
TaskPlanner::new(&self.root, &self.config.arch, recipes).fetch_plan(&requested)?;
self.print_plan(&plan);
if dry_run {
return Ok(());
@@ -424,11 +426,7 @@ impl Builder {
) -> anyhow::Result<()> {
log::step("configure", &recipe.key());
if let Some(func) = recipe.phases().configure() {
let ctx = PhaseContext::new(
source_dir_for(recipe),
prefix_for(recipe.kind()),
default_jobs(),
);
let ctx = phase_context_for(recipe);
self.invoke_with_runtime(active, &[PhaseArg::Ctx(ctx)], func)?;
}
self.write_recipe_stamp(layout, recipe, "configure")
@@ -441,11 +439,7 @@ impl Builder {
active: &ActiveContainer,
) -> anyhow::Result<()> {
log::step("build", &recipe.key());
let ctx = PhaseContext::new(
source_dir_for(recipe),
prefix_for(recipe.kind()),
default_jobs(),
);
let ctx = phase_context_for(recipe);
self.invoke_with_runtime(active, &[PhaseArg::Ctx(ctx)], recipe.phases().build())?;
self.write_recipe_stamp(layout, recipe, "build")
}
@@ -464,11 +458,7 @@ impl Builder {
&base_env(&active.base_path),
"/",
)?;
let ctx = PhaseContext::new(
source_dir_for(recipe),
prefix_for(recipe.kind()),
default_jobs(),
);
let ctx = phase_context_for(recipe);
let pkg = PackageContext::new(dest);
self.invoke_with_runtime(
active,
@@ -491,11 +481,7 @@ impl Builder {
&base_env(&active.base_path),
"/",
)?;
let ctx = PhaseContext::new(
source_dir_for(recipe),
prefix_for(recipe.kind()),
default_jobs(),
);
let ctx = phase_context_for(recipe);
let pkg = PackageContext::new(dest.clone());
self.invoke_with_runtime(
active,
@@ -595,6 +581,61 @@ impl Builder {
phase::invoke_phase(func, args)
}
fn populate_sysroot(
&self,
layout: &Layout<'_>,
recipes: &RecipeSet,
recipe: &Recipe,
active: &ActiveContainer,
deps: &[String],
) -> anyhow::Result<()> {
if deps.is_empty() {
return Ok(());
}
let env = base_env(&active.base_path);
for dep in deps {
let output = recipes.output(dep)?;
let owning = recipes.recipe(output.recipe())?;
let apk_host = layout.apk_path(owning, output);
if !apk_host.exists() {
bail!(
"missing apk for target dependency `{dep}` at {}; \
rebuild it first (e.g. `distro build -r {dep}`)",
apk_host.display()
);
}
let file_name = apk_host
.file_name()
.and_then(|n| n.to_str())
.ok_or_else(|| {
anyhow::anyhow!("apk path {} has no UTF-8 file name", apk_host.display())
})?;
active.container.borrow().exec(
&[
"apk".to_owned(),
"extract".to_owned(),
"--allow-untrusted".to_owned(),
"--destination".to_owned(),
"/sysroot".to_owned(),
format!("/pkgs/{file_name}"),
],
&env,
"/",
)?;
}
log::info(
"sysroot",
&format!(
"{}: extracted /sysroot from {} target apk(s)",
recipe.key(),
deps.len()
),
);
Ok(())
}
fn start_recipe_container(
&self,
recipes: &RecipeSet,
@@ -609,8 +650,10 @@ impl Builder {
fs::create_dir_all(&build_dir)?;
let host_deps = transitive_host_deps(recipes, recipe)?;
let target_deps = transitive_target_deps(recipes, recipe)?;
let pkgs_dir = self.root.join("build/pkgs").join(&self.config.arch);
fs::create_dir_all(&pkgs_dir)?;
let mut mounts = vec![
Mount {
host: source_dir,
@@ -628,6 +671,15 @@ impl Builder {
read_only: false,
},
];
if let Some(files_dir) = recipe.files_dir() {
mounts.push(Mount {
host: files_dir,
container: "/files".to_owned(),
read_only: true,
});
}
let mut tools_bins: Vec<String> = Vec::new();
for dep_key in &host_deps {
let dep_recipe = recipes.recipe(dep_key)?;
@@ -674,6 +726,8 @@ impl Builder {
base_path,
};
self.populate_sysroot(&layout, recipes, recipe, &active, &target_deps)?;
Ok(active)
}
@@ -817,6 +871,21 @@ struct ActiveContainer {
base_path: String,
}
fn filter_skipped(recipes: &RecipeSet, requested: &[String]) -> Vec<String> {
requested
.iter()
.filter(|key| {
if recipes.is_skipped(key) {
log::skip("skip", &format!("{key} (build_if returned false)"));
false
} else {
true
}
})
.cloned()
.collect()
}
fn task_needs_container(task: &TaskId) -> bool {
matches!(
task,
@@ -835,6 +904,16 @@ fn prefix_for(kind: RecipeKind) -> &'static str {
}
}
fn phase_context_for(recipe: &Recipe) -> PhaseContext {
let files = recipe.files_dir().map(|_| "/files".to_owned());
PhaseContext::new(
source_dir_for(recipe),
prefix_for(recipe.kind()),
default_jobs(),
files,
)
}
fn source_dir_for(recipe: &Recipe) -> SourceDir {
let entries = recipe.sources().entries();
let named: Vec<(&str, &crate::recipe::Source)> = entries
@@ -896,6 +975,34 @@ fn transitive_host_deps(recipes: &RecipeSet, recipe: &Recipe) -> anyhow::Result<
Ok(order)
}
/// Compute the transitive closure of a recipe's target package dependencies.
/// The recipe's own `build_deps` and `deps` seed the queue; for each visited
/// dependency we follow its `deps` (runtime+build-needed) recursively, but
/// not its `build_deps` (build-time-only relative to *that* package, hence
/// not propagated outward).
fn transitive_target_deps(recipes: &RecipeSet, recipe: &Recipe) -> anyhow::Result<Vec<String>> {
let mut order: Vec<String> = Vec::new();
let mut seen: BTreeSet<String> = BTreeSet::new();
let mut queue: VecDeque<String> = recipe
.build_deps()
.iter()
.chain(recipe.deps().iter())
.cloned()
.collect();
while let Some(dep) = queue.pop_front() {
if !seen.insert(dep.clone()) {
continue;
}
let output = recipes.output(&dep)?;
let owning = recipes.recipe(output.recipe())?;
for sub in owning.deps() {
queue.push_back(sub.clone());
}
order.push(dep);
}
Ok(order)
}
fn random_suffix() -> u64 {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)