Files
distro/src/layout.rs
T
2026-05-20 00:30:38 +02:00

124 lines
3.8 KiB
Rust

use std::{
fs,
path::{Path, PathBuf},
};
use anyhow::Context;
use sha2::{Digest, Sha256};
use crate::recipe::{OutputPackage, Recipe};
pub struct Layout<'a> {
pub root: &'a Path,
pub arch: &'a str,
}
impl<'a> Layout<'a> {
pub fn new(root: &'a Path, arch: &'a str) -> Self {
Self { root, arch }
}
pub fn source_cache_dir(&self) -> PathBuf {
self.root.join("build/cache/sources")
}
pub fn source_cache_path(&self, key: &str) -> PathBuf {
self.source_cache_dir().join(key)
}
pub fn source_workdir(&self, recipe: &Recipe) -> PathBuf {
self.root.join("build/sources").join(recipe.slug())
}
pub fn build_workdir(&self, recipe: &Recipe) -> PathBuf {
self.root.join("build/builds").join(recipe.slug())
}
pub fn host_install_dir(&self, recipe: &Recipe) -> PathBuf {
self.root.join("build/host-pkgs").join(recipe.slug())
}
pub fn host_install_root(&self, recipe: &Recipe) -> PathBuf {
self.root.join("build/host-pkgs").join(recipe.slug())
}
pub fn apk_path(&self, recipe: &Recipe, output: &OutputPackage) -> PathBuf {
self.root.join("build/pkgs").join(self.arch).join(format!(
"{}-{}-r{}.apk",
output.name(),
recipe.version(),
recipe.revision()
))
}
pub fn source_stamp(&self, recipe: &Recipe, kind: &str) -> PathBuf {
self.root
.join("build/sources")
.join(format!("{}.{kind}", recipe.slug()))
}
pub fn recipe_task_stamp(&self, recipe: &Recipe, kind: &str) -> PathBuf {
self.root
.join("build/tasks")
.join(format!("{}.{kind}", recipe.slug()))
}
pub fn output_task_stamp(&self, output: &OutputPackage, kind: &str) -> PathBuf {
self.root
.join("build/tasks")
.join(format!("{}.{kind}", output.key().replace(':', "-")))
}
pub fn recipe_fingerprint(&self, recipe: &Recipe) -> anyhow::Result<String> {
let mut hasher = Sha256::new();
hasher.update(self.arch.as_bytes());
hasher.update(recipe.key().as_bytes());
hasher.update(recipe.version().as_bytes());
hasher.update(recipe.revision().to_le_bytes());
hasher.update(
fs::read(recipe.path())
.with_context(|| format!("reading recipe {}", recipe.path().display()))?,
);
for (name, source) in recipe.sources().entries() {
hasher.update(name.unwrap_or("").as_bytes());
hasher.update(source.url().as_bytes());
hasher.update(source.cache_key().as_bytes());
}
for patch in self.recipe_patches(recipe)? {
hasher.update(patch.display().to_string().as_bytes());
hasher
.update(fs::read(&patch).with_context(|| format!("reading {}", patch.display()))?);
}
Ok(hex::encode(hasher.finalize()))
}
pub fn output_fingerprint(
&self,
recipe: &Recipe,
output: &OutputPackage,
) -> anyhow::Result<String> {
let mut hasher = Sha256::new();
hasher.update(self.recipe_fingerprint(recipe)?.as_bytes());
hasher.update(output.key().as_bytes());
Ok(hex::encode(hasher.finalize()))
}
pub fn recipe_has_patches(&self, recipe: &Recipe) -> anyhow::Result<bool> {
Ok(!self.recipe_patches(recipe)?.is_empty())
}
pub fn recipe_patches(&self, recipe: &Recipe) -> anyhow::Result<Vec<PathBuf>> {
let Some(data_dir) = recipe.data_dir() else {
return Ok(Vec::new());
};
let patches_dir = data_dir.join("patches");
let mut out = Vec::new();
for (_, source) in recipe.sources().entries() {
for name in source.patches() {
out.push(patches_dir.join(name));
}
}
Ok(out)
}
}