From 25141384b40d2502d9b02ac484163a0ba30a2cd5 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Sun, 27 Jul 2025 21:43:54 +0700 Subject: [PATCH] Support for metapackages --- src/bin/cook.rs | 97 ++++++++++++++++++++++++++--------------- src/bin/repo_builder.rs | 6 ++- src/recipe.rs | 45 ++++++++++++++++++- 3 files changed, 109 insertions(+), 39 deletions(-) diff --git a/src/bin/cook.rs b/src/bin/cook.rs index 6a2364de3..1f18fdfc5 100644 --- a/src/bin/cook.rs +++ b/src/bin/cook.rs @@ -1029,6 +1029,7 @@ done } BuildKind::Configure => "cookbook_configure".to_owned(), BuildKind::Custom { script } => script.clone(), + BuildKind::None => "".to_owned(), }; let command = { @@ -1075,16 +1076,12 @@ done } fn package( - _recipe_dir: &Path, stage_dir: &Path, target_dir: &Path, name: &PackageName, recipe: &Recipe, auto_deps: &BTreeSet, ) -> Result { - //TODO: metadata like dependencies, name, and version - let package = &recipe.package; - let secret_path = "build/id_ed25519.toml"; let public_path = "build/id_ed25519.pub.toml"; if !Path::new(secret_path).is_file() || !Path::new(public_path).is_file() { @@ -1122,33 +1119,65 @@ fn package( ) .map_err(|err| format!("failed to create pkgar archive: {:?}", err))?; - let mut depends = package.dependencies.clone(); - for dep in auto_deps.iter() { - if !depends.contains(dep) { - depends.push(dep.clone()); - } - } - let stage_toml = toml::to_string(&Package { - name: name.clone(), - version: "TODO".into(), - target: env::var("TARGET") - .map_err(|err| format!("failed to read TARGET: {:?}", err))?, - depends, - }) - .map_err(|err| format!("failed to serialize stage.toml: {:?}", err))?; - fs::write(target_dir.join("stage.toml"), stage_toml) - .map_err(|err| format!("failed to write stage.toml: {:?}", err))?; + package_toml(target_dir, name, recipe, auto_deps)?; } Ok(package_file) } +fn package_toml( + target_dir: &Path, + name: &PackageName, + recipe: &Recipe, + auto_deps: &BTreeSet, +) -> Result<(), String> { + let mut depends = recipe.package.dependencies.clone(); + for dep in auto_deps.iter() { + if !depends.contains(dep) { + depends.push(dep.clone()); + } + } + let stage_toml = toml::to_string(&Package { + name: name.clone(), + version: "TODO".into(), + target: env::var("TARGET").map_err(|err| format!("failed to read TARGET: {:?}", err))?, + depends, + }) + .map_err(|err| format!("failed to serialize stage.toml: {:?}", err))?; + fs::write(target_dir.join("stage.toml"), stage_toml) + .map_err(|err| format!("failed to write stage.toml: {:?}", err))?; + + return Ok(()); +} + +fn cook_meta( + recipe_dir: &Path, + name: &PackageName, + recipe: &Recipe, + fetch_only: bool, +) -> Result<(), String> { + if fetch_only { + return Ok(()); + } + + let target_dir = create_target_dir(recipe_dir)?; + let empty_deps = BTreeSet::new(); + let _package_file = package_toml(&target_dir, name, recipe, &empty_deps) + .map_err(|err| format!("failed to package: {}", err))?; + + Ok(()) +} + fn cook( recipe_dir: &Path, name: &PackageName, recipe: &Recipe, fetch_only: bool, ) -> Result<(), String> { + if recipe.build.kind == BuildKind::None { + return cook_meta(recipe_dir, name, recipe, fetch_only); + } + let is_offline = env::var("COOKBOOK_OFFLINE").unwrap_or("".to_string()) == "1"; let source_dir = match is_offline { true => fetch_offline(recipe_dir, &recipe.source), @@ -1160,6 +1189,18 @@ fn cook( return Ok(()); } + let target_dir = create_target_dir(recipe_dir)?; + + let (stage_dir, auto_deps) = build(recipe_dir, &source_dir, &target_dir, name, recipe) + .map_err(|err| format!("failed to build: {}", err))?; + + let _package_file = package(&stage_dir, &target_dir, name, recipe, &auto_deps) + .map_err(|err| format!("failed to package: {}", err))?; + + Ok(()) +} + +fn create_target_dir(recipe_dir: &Path) -> Result { let target_parent_dir = recipe_dir.join("target"); if !target_parent_dir.is_dir() { create_dir(&target_parent_dir)?; @@ -1168,21 +1209,7 @@ fn cook( if !target_dir.is_dir() { create_dir(&target_dir)?; } - - let (stage_dir, auto_deps) = build(recipe_dir, &source_dir, &target_dir, name, recipe) - .map_err(|err| format!("failed to build: {}", err))?; - - let _package_file = package( - recipe_dir, - &stage_dir, - &target_dir, - name, - recipe, - &auto_deps, - ) - .map_err(|err| format!("failed to package: {}", err))?; - - Ok(()) + Ok(target_dir) } fn main() { diff --git a/src/bin/repo_builder.rs b/src/bin/repo_builder.rs index db66483b5..6a7a8a35d 100644 --- a/src/bin/repo_builder.rs +++ b/src/bin/repo_builder.rs @@ -46,9 +46,11 @@ fn main() -> Result<(), Box> { let toml_src = stage_dir.with_extension("toml"); let toml_dst = repo_path.join(format!("{}.toml", recipe)); - if is_newer(&pkgar_src, &pkgar_dst) { + if is_newer(&toml_src, &toml_dst) { eprintln!("\x1b[01;38;5;155mrepo - publishing {}\x1b[0m", recipe); - fs::copy(&pkgar_src, &pkgar_dst)?; + if fs::exists(&pkgar_src)? { + fs::copy(&pkgar_src, &pkgar_dst)?; + } fs::copy(&toml_src, &toml_dst)?; } diff --git a/src/recipe.rs b/src/recipe.rs index 85240dce9..4341551d7 100644 --- a/src/recipe.rs +++ b/src/recipe.rs @@ -63,6 +63,9 @@ pub enum SourceRecipe { #[derive(Debug, Deserialize, PartialEq, Serialize)] #[serde(tag = "template")] pub enum BuildKind { + /// Will not build (for meta packages) + #[serde(rename = "none")] + None, /// Will build and install using cargo #[serde(rename = "cargo")] Cargo { @@ -80,9 +83,15 @@ pub enum BuildKind { Custom { script: String }, } -#[derive(Debug, Deserialize, PartialEq, Serialize)] +impl Default for BuildKind { + fn default() -> Self { + BuildKind::None + } +} + +#[derive(Debug, Default, Deserialize, PartialEq, Serialize)] pub struct BuildRecipe { - #[serde(flatten)] + #[serde(flatten, default)] pub kind: BuildKind, #[serde(default)] pub dependencies: Vec, @@ -100,6 +109,7 @@ pub struct Recipe { /// Specifies how to download the source for this recipe pub source: Option, /// Specifies how to build this recipe + #[serde(default)] pub build: BuildRecipe, /// Specifies how to package this recipe #[serde(default)] @@ -207,6 +217,8 @@ impl CookRecipe { #[cfg(test)] mod tests { + use pkg::PackageName; + #[test] fn git_cargo_recipe() { use crate::recipe::{BuildKind, BuildRecipe, PackageRecipe, Recipe, SourceRecipe}; @@ -291,4 +303,33 @@ mod tests { } ); } + + #[test] + fn meta_recipe() { + use crate::recipe::{BuildKind, BuildRecipe, PackageRecipe, Recipe}; + + let recipe: Recipe = toml::from_str( + r#" + [package] + dependencies = [ + "gcc13", + ] + "#, + ) + .unwrap(); + + assert_eq!( + recipe, + Recipe { + source: None, + build: BuildRecipe { + kind: BuildKind::None, + dependencies: Vec::new(), + }, + package: PackageRecipe { + dependencies: vec![PackageName::new("gcc13").unwrap()], + }, + } + ); + } }