diff --git a/src/bin/cook.rs b/src/bin/cook.rs index 38d126c95..d5299d702 100644 --- a/src/bin/cook.rs +++ b/src/bin/cook.rs @@ -1,36 +1,17 @@ -use std::collections::BTreeSet; use std::path::Path; use std::{env, process}; use cookbook::WALK_DEPTH; use cookbook::cook::fetch::{fetch, fetch_offline}; use cookbook::cook::fs::create_target_dir; -use cookbook::cook::package::{package, package_toml}; -use cookbook::recipe::{BuildKind, CookRecipe, Recipe}; +use cookbook::cook::package::package; +use cookbook::recipe::{CookRecipe, Recipe}; use pkg::PackageName; use cookbook::config::init_config; use cookbook::cook::cook_build::build; use termion::{color, style}; -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, @@ -39,12 +20,9 @@ fn cook( fetch_only: bool, is_offline: bool, ) -> Result<(), String> { - if recipe.build.kind == BuildKind::None { - return cook_meta(recipe_dir, name, recipe, fetch_only); - } let source_dir = match is_offline { - true => fetch_offline(recipe_dir, &recipe.source), - false => fetch(recipe_dir, &recipe.source), + true => fetch_offline(recipe_dir, recipe), + false => fetch(recipe_dir, recipe), } .map_err(|err| format!("failed to fetch: {}", err))?; @@ -65,7 +43,7 @@ fn cook( ) .map_err(|err| format!("failed to build: {}", err))?; - let _package_file = package(&stage_dir, &target_dir, name, recipe, &auto_deps) + package(&stage_dir, &target_dir, name, recipe, &auto_deps) .map_err(|err| format!("failed to package: {}", err))?; Ok(()) diff --git a/src/bin/repo.rs b/src/bin/repo.rs index ec0a604fb..cab4ddcde 100644 --- a/src/bin/repo.rs +++ b/src/bin/repo.rs @@ -1,9 +1,18 @@ -use std::collections::BTreeSet; -use std::path::{Path, PathBuf}; -use std::process::{self, Command}; +use std::path::PathBuf; +use std::process; +use std::str::FromStr; use std::{env, fs}; -use anyhow::{Context, anyhow}; +use anyhow::{Context, anyhow, bail}; +use cookbook::WALK_DEPTH; +use cookbook::config::{CookConfig, get_config, init_config}; +use cookbook::cook::cook_build::build; +use cookbook::cook::fetch::{fetch, fetch_offline}; +use cookbook::cook::fs::create_target_dir; +use cookbook::cook::package::package; +use cookbook::recipe::CookRecipe; +use pkg::PackageName; +use pkg::package::PackageError; // A repo manager, to replace repo.sh @@ -36,16 +45,51 @@ struct CliConfig { repo_dir: PathBuf, sysroot_dir: PathBuf, with_package_deps: bool, - offline: bool, - nonstop: bool, all: bool, - quiet: bool, + cook: CookConfig, +} + +#[derive(PartialEq)] +enum CliCommand { + Fetch, + Cook, + Unfetch, + Clean, + Push, +} + +impl FromStr for CliCommand { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + match s { + "fetch" => Ok(CliCommand::Fetch), + "cook" => Ok(CliCommand::Cook), + "unfetch" => Ok(CliCommand::Unfetch), + "clean" => Ok(CliCommand::Clean), + "push" => Ok(CliCommand::Push), + _ => Err(anyhow!("Unknown command '{}'", s)), + } + } +} + +impl ToString for CliCommand { + fn to_string(&self) -> String { + match self { + CliCommand::Fetch => "fetch".to_string(), + CliCommand::Cook => "cook".to_string(), + CliCommand::Unfetch => "unfetch".to_string(), + CliCommand::Clean => "clean".to_string(), + CliCommand::Push => "push".to_string(), + } + } } impl CliConfig { fn new() -> Result { let current_dir = env::current_dir()?; Ok(CliConfig { + //FIXME: This config is unused as redox-pkg harcoded this to $PWD/recipes cookbook_dir: current_dir.join("recipes"), repo_dir: current_dir.join("repo"), sysroot_dir: if cfg!(target_os = "redox") { @@ -54,15 +98,14 @@ impl CliConfig { current_dir.join("sysroot") }, with_package_deps: false, - offline: false, - nonstop: false, + cook: get_config().cook.clone(), all: false, - quiet: false, }) } } fn main() { + init_config(); main_inner().unwrap(); } @@ -74,10 +117,29 @@ fn main_inner() -> anyhow::Result<()> { process::exit(1); } + let (config, command, recipe_names) = parse_args(args)?; + + for recipe in &recipe_names { + match command { + CliCommand::Fetch => handle_cook(recipe, &config, true, recipe.is_deps)?, + CliCommand::Cook => handle_cook(recipe, &config, false, recipe.is_deps)?, + CliCommand::Unfetch => handle_clean(recipe, &config, true, true)?, + CliCommand::Clean => handle_clean(recipe, &config, false, true)?, + CliCommand::Push => handle_push(recipe, &config)?, + } + } + + println!( + "\nCommand '{}' completed for all specified recipes.", + command.to_string(), + ); + Ok(()) +} + +fn parse_args(args: Vec) -> anyhow::Result<(CliConfig, CliCommand, Vec)> { let mut config = CliConfig::new()?; let mut command: Option = None; - let mut recipe_paths: BTreeSet = BTreeSet::new(); - + let mut recipe_names: Vec = Vec::new(); for arg in args { if arg.starts_with("--") { if let Some((key, value)) = arg.split_once('=') { @@ -93,10 +155,7 @@ fn main_inner() -> anyhow::Result<()> { } else { match arg.as_str() { "--with-package-deps" => config.with_package_deps = true, - "--offline" => config.offline = true, - "--nonstop" => config.nonstop = true, "--all" => config.all = true, - "--quiet" => config.quiet = true, _ => { eprintln!("Error: Unknown flag: {}", arg); process::exit(1); @@ -105,7 +164,6 @@ fn main_inner() -> anyhow::Result<()> { } } else if arg.starts_with('-') { match arg.as_str() { - "-q" => config.quiet = true, _ => { eprintln!("Error: Unknown flag: {}", arg); process::exit(1); @@ -116,122 +174,117 @@ fn main_inner() -> anyhow::Result<()> { command = Some(arg); } else { // Subsequent non-flag arguments are recipe names - if let Some(path) = pkg::recipes::find(&arg) { - recipe_paths.insert(path.to_owned()); - } else { - panic!("Error: recipe not found '{arg}'"); - } + recipe_names.push(arg.try_into().context("Invalid package name")?); } } - let command = command.ok_or("Error: No command specified.").unwrap(); - - if !config.all && recipe_paths.is_empty() { - panic!("Error: No recipe names provided and --all flag was not used."); - } - if config.all && !recipe_paths.is_empty() { - panic!("Error: Cannot specify recipe names when using the --all flag."); - } - - if config.all { - recipe_paths = pkg::recipes::list(""); - } - - for recipe_path in &recipe_paths { - match command.as_str() { - "fetch" => handle_fetch(recipe_path, &config)?, - "cook" => handle_cook(recipe_path, &config)?, - "unfetch" => handle_unfetch(recipe_path, &config)?, - "clean" => handle_clean(recipe_path, &config)?, - "push" => handle_push(recipe_path, &config)?, - _ => { - eprintln!("Error: Unknown command '{}'\n", command); - println!("{}", REPO_HELP_STR); - process::exit(1); - } + let command = command.ok_or(anyhow!("Error: No command specified."))?; + let command: CliCommand = str::parse(&command)?; + let recipes = if config.all { + if !recipe_names.is_empty() { + bail!("Cannot specify recipe names when using the --all flag."); } + if command == CliCommand::Cook + || command == CliCommand::Fetch + || command == CliCommand::Push + { + // because read_recipe is false below + // some recipes on wip folders are invalid anyway + bail!( + "Refusing to run an unrealistic command to {} all recipes", + command.to_string() + ); + } + + pkg::recipes::list("") + .iter() + .map(|f| CookRecipe::from_path(f, false)) + .collect::, PackageError>>()? + } else { + if recipe_names.is_empty() { + bail!("Error: No recipe names provided and --all flag was not used."); + } + if config.with_package_deps { + recipe_names = CookRecipe::get_package_deps_recursive(&recipe_names, WALK_DEPTH) + .context("failed get package deps")?; + } + + CookRecipe::get_build_deps_recursive(&recipe_names, !config.with_package_deps)? + }; + + Ok((config, command, recipes)) +} + +fn handle_cook( + recipe: &CookRecipe, + config: &CliConfig, + fetch_only: bool, + is_deps: bool, +) -> anyhow::Result<()> { + let recipe_dir = &recipe.dir; + let source_dir = match config.cook.offline { + true => fetch_offline(recipe_dir, &recipe.recipe), + false => fetch(recipe_dir, &recipe.recipe), + } + .map_err(|e| anyhow!(e))?; + + if fetch_only { + return Ok(()); } - println!( - "\nCommand '{}' completed for all specified recipes.", - command - ); + let target_dir = create_target_dir(recipe_dir).map_err(|e| anyhow!(e))?; + + let (stage_dir, auto_deps) = build( + recipe_dir, + &source_dir, + &target_dir, + &recipe.name, + &recipe.recipe, + config.cook.offline, + !is_deps, + ) + .map_err(|err| anyhow!("failed to build: {}", err))?; + + package( + &stage_dir, + &target_dir, + &recipe.name, + &recipe.recipe, + &auto_deps, + ) + .map_err(|err| anyhow!("failed to package: {}", err))?; + Ok(()) } -fn handle_fetch(recipe_path: &Path, config: &CliConfig) -> anyhow::Result<()> { - let mut cmd = Command::new("cook"); - cmd.arg("--fetch-only"); - if config.with_package_deps { - cmd.arg("--with-package-deps"); +fn handle_clean( + recipe: &CookRecipe, + _config: &CliConfig, + source: bool, + target: bool, +) -> anyhow::Result<()> { + let dir = recipe.dir.join("target"); + if dir.exists() && target { + fs::remove_dir_all(&dir).context(format!("failed to delete {}", dir.display()))?; } - if config.offline { - cmd.arg("--offline"); - } - if config.quiet { - cmd.arg("--quiet"); - } - cmd.arg(recipe_path); - let status = cmd.status().context("Failed to execute cook command")?; - if !status.success() && !config.nonstop { - return Err(anyhow!( - "Cook command failed for recipe '{}' with exit code: {}", - recipe_path.display(), - status.code().unwrap_or(1) - )); + let dir = recipe.dir.join("source"); + if dir.exists() && source { + fs::remove_dir_all(&dir).context(format!("failed to delete {}", dir.display()))?; } Ok(()) } -fn handle_cook(recipe_path: &Path, config: &CliConfig) -> anyhow::Result<()> { - let mut cmd = Command::new("cook"); - cmd.arg(recipe_path); - if config.with_package_deps { - cmd.arg("--with-package-deps"); - } - if config.offline { - cmd.arg("--offline"); - } - if config.quiet { - cmd.arg("--quiet"); - } - let status = cmd.status().context("Failed to execute cook command")?; - if !status.success() && !config.nonstop { - return Err(anyhow!( - "Cook command failed for recipe '{}' with exit code: {}", - recipe_path.display(), - status.code().unwrap_or(1) - )); - } - Ok(()) -} - -fn handle_unfetch(recipe_path: &Path, _config: &CliConfig) -> anyhow::Result<()> { - let dir = recipe_path.join("source"); - if dir.exists() { - fs::remove_dir_all(dir).context(format!("failed to delete {}", recipe_path.display()))?; - } - Ok(()) -} - -fn handle_clean(recipe_path: &Path, _config: &CliConfig) -> anyhow::Result<()> { - let dir = recipe_path.join("target"); - if dir.exists() { - fs::remove_dir_all(dir).context(format!("failed to delete {}", recipe_path.display()))?; - } - Ok(()) -} - -fn handle_push(recipe_path: &Path, config: &CliConfig) -> anyhow::Result<()> { +fn handle_push(recipe: &CookRecipe, config: &CliConfig) -> anyhow::Result<()> { let public_path = "build/id_ed25519.pub.toml"; + let archive_path = config.repo_dir.join(recipe.name.as_str()); pkgar::extract( public_path, - config.sysroot_dir.as_path(), + archive_path.as_path(), config.sysroot_dir.to_str().unwrap(), ) .context(format!( "failed to install '{}' in '{}'", - recipe_path.display(), + archive_path.display(), config.sysroot_dir.display(), )) } diff --git a/src/bin/repo_builder.rs b/src/bin/repo_builder.rs index a94123df1..4748560e8 100644 --- a/src/bin/repo_builder.rs +++ b/src/bin/repo_builder.rs @@ -26,6 +26,9 @@ fn main() -> Result<(), Box> { .next() .expect("Usage: repo_builder ..."); let repo_path = Path::new(&repo_dir); + if !repo_path.is_dir() { + fs::create_dir_all(repo_path)?; + } // Runtime dependencies include both `[package.dependencies]` and dynamically // linked packages discovered by auto_deps. diff --git a/src/config.rs b/src/config.rs index 54d6f8370..d79848fb0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,16 +1,58 @@ -use std::{collections::HashMap, fs, sync::OnceLock}; +use std::{collections::HashMap, env, fs, str::FromStr, sync::OnceLock}; use serde::{Deserialize, Serialize}; +#[derive(Debug, Default, Clone, Deserialize, PartialEq, Serialize)] +pub struct CookConfigOpt { + /// whether to run offline + pub offline: Option, + /// whether to set jobs number instead of from nproc + pub jobs: Option, + /// whether to use TUI to allow parallel build + /// default value is yes if "CI" env unset and STDIN is open. + pub tui: Option, + /// whether to ignore build errors + pub nonstop: Option, + /// whether to not capture build output, + /// default is true if "tui" is false. + /// build failure still be printed anyway + pub verbose: Option, +} + +#[derive(Debug, Default, Clone, Deserialize, PartialEq, Serialize)] +pub struct CookConfig { + pub offline: bool, + pub jobs: usize, + pub tui: bool, + pub nonstop: bool, + pub verbose: bool, +} + +impl From for CookConfig { + fn from(value: CookConfigOpt) -> Self { + CookConfig { + offline: value.offline.unwrap(), + jobs: value.jobs.unwrap(), + tui: value.tui.unwrap(), + nonstop: value.nonstop.unwrap(), + verbose: value.verbose.unwrap(), + } + } +} + #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] pub struct CookbookConfig { + #[serde(rename = "cook")] + cook_opt: CookConfigOpt, + #[serde(skip)] + pub cook: CookConfig, pub mirrors: HashMap, } static CONFIG: OnceLock = OnceLock::new(); pub fn init_config() { - let config: CookbookConfig = if fs::exists("cookbook.toml").unwrap_or(false) { + let mut config: CookbookConfig = if fs::exists("cookbook.toml").unwrap_or(false) { let toml_content = fs::read_to_string("cookbook.toml") .map_err(|e| format!("Unable to read config: {:?}", e)) .unwrap(); @@ -21,9 +63,47 @@ pub fn init_config() { CookbookConfig::default() }; + if config.cook_opt.tui.is_none() { + config.cook_opt.tui = Some(!env::var("CI").is_ok_and(|s| !s.is_empty())); + } + if config.cook_opt.jobs.is_none() { + config.cook_opt.jobs = Some(extract_env( + "COOKBOOK_MAKE_JOBS", + std::thread::available_parallelism() + .map(|f| usize::from(f)) + .unwrap_or(1), + )); + } + if config.cook_opt.offline.is_none() { + config.cook_opt.offline = Some(extract_env("COOKBOOK_OFFLINE", false)); + } + if config.cook_opt.verbose.is_none() { + config.cook_opt.verbose = Some(extract_env( + "COOKBOOK_VERBOSE", + !config.cook_opt.tui.unwrap(), + )); + } + if config.cook_opt.nonstop.is_none() { + config.cook_opt.nonstop = Some(extract_env("COOKBOOK_NONSTOP", false)); + } + + config.cook = CookConfig::from(config.cook_opt.clone()); + CONFIG.set(config).expect("config is initialized twice"); } +fn extract_env(key: &str, default: T) -> T { + if let Ok(e) = env::var(&key) { + str::parse(&e).unwrap_or(default) + } else { + default + } +} + +pub fn get_config() -> &'static CookbookConfig { + return CONFIG.get().expect("Configuration is not initialized"); +} + pub fn translate_mirror(original_url: &str) -> String { let config = CONFIG.get().expect("Configuration is not initialized"); diff --git a/src/cook/cook_build.rs b/src/cook/cook_build.rs index bdf7ab88c..a4773f26d 100644 --- a/src/cook/cook_build.rs +++ b/src/cook/cook_build.rs @@ -151,6 +151,10 @@ pub fn build( ) -> Result<(PathBuf, BTreeSet), String> { let sysroot_dir = target_dir.join("sysroot"); let stage_dir = target_dir.join("stage"); + if recipe.build.kind == BuildKind::None { + // metapackages don't need to do anything here + return Ok((stage_dir, BTreeSet::new())); + } let mut dep_pkgars = BTreeSet::new(); for dependency in recipe.build.dependencies.iter() { diff --git a/src/cook/fetch.rs b/src/cook/fetch.rs index c3ce9bdc7..b9a2bfc6a 100644 --- a/src/cook/fetch.rs +++ b/src/cook/fetch.rs @@ -2,6 +2,7 @@ use crate::config::translate_mirror; use crate::cook::fs::*; use crate::cook::script::*; use crate::is_redox; +use crate::recipe::BuildKind; use crate::recipe::Recipe; use crate::{blake3, recipe::SourceRecipe}; use std::fs; @@ -24,14 +25,18 @@ pub(crate) fn get_blake3(path: &PathBuf, show_progress: bool) -> Result) -> Result { +pub fn fetch_offline(recipe_dir: &Path, recipe: &Recipe) -> Result { let source_dir = recipe_dir.join("source"); - match source { + if recipe.build.kind == BuildKind::None || recipe.build.kind == BuildKind::Remote { + // the build function doesn't need source dir exists + return Ok(source_dir); + } + match &recipe.source { Some(SourceRecipe::Path { path: _ }) | None => { - return fetch(recipe_dir, source); + return fetch(recipe_dir, recipe); } Some(SourceRecipe::SameAs { same_as: _ }) => { - return fetch(recipe_dir, source); + return fetch(recipe_dir, recipe); } Some(SourceRecipe::Git { git: _, @@ -79,17 +84,22 @@ pub fn fetch_offline(recipe_dir: &Path, source: &Option) -> Result Ok(source_dir) } -pub fn fetch(recipe_dir: &Path, source: &Option) -> Result { +pub fn fetch(recipe_dir: &Path, recipe: &Recipe) -> Result { let source_dir = recipe_dir.join("source"); - match source { + if recipe.build.kind == BuildKind::None || recipe.build.kind == BuildKind::Remote { + // the build function doesn't need source dir exists + return Ok(source_dir); + } + match &recipe.source { Some(SourceRecipe::SameAs { same_as }) => { - let (canon_dir, recipe) = fetch_resolve_canon(recipe_dir, same_as)?; + let (canon_dir, recipe) = fetch_resolve_canon(recipe_dir, &same_as)?; // recursively fetch - fetch(&canon_dir, &recipe.source)?; - fetch_make_symlink(&source_dir, same_as)?; + fetch(&canon_dir, &recipe)?; + fetch_make_symlink(&source_dir, &same_as)?; } Some(SourceRecipe::Path { path }) => { - if !source_dir.is_dir() || modified_dir(Path::new(path))? > modified_dir(&source_dir)? { + if !source_dir.is_dir() || modified_dir(Path::new(&path))? > modified_dir(&source_dir)? + { eprintln!("[DEBUG]: {} is newer than {}", path, source_dir.display()); copy_dir_all(path, &source_dir).map_err(|e| { format!( @@ -122,7 +132,7 @@ pub fn fetch(recipe_dir: &Path, source: &Option) -> Result) -> Result { if !source_dir.is_dir() { - //TODO: Don't print if build template is none or remote eprintln!( "WARNING: Recipe without source section expected source dir at '{}'", source_dir.display(), diff --git a/src/cook/package.rs b/src/cook/package.rs index aaee9fa65..a02ad9b11 100644 --- a/src/cook/package.rs +++ b/src/cook/package.rs @@ -1,8 +1,4 @@ -use std::{ - collections::BTreeSet, - env, - path::{Path, PathBuf}, -}; +use std::{collections::BTreeSet, env, path::Path}; use pkg::{Package, PackageName}; @@ -17,7 +13,13 @@ pub fn package( name: &PackageName, recipe: &Recipe, auto_deps: &BTreeSet, -) -> Result { +) -> Result<(), String> { + if recipe.build.kind == BuildKind::None { + // metapackages don't have stage dir + package_toml(target_dir, name, recipe, auto_deps)?; + return Ok(()); + } + 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() { @@ -58,7 +60,7 @@ pub fn package( package_toml(target_dir, name, recipe, auto_deps)?; } - Ok(package_file) + Ok(()) } pub fn package_toml( @@ -80,7 +82,8 @@ pub fn package_toml( depends, }; - serialize_and_write(&target_dir.join("stage.toml"), &package)?; + let toml_path = &target_dir.join("stage.toml"); + serialize_and_write(&toml_path, &package)?; return Ok(()); } diff --git a/src/recipe.rs b/src/recipe.rs index 99e808007..ce068a640 100644 --- a/src/recipe.rs +++ b/src/recipe.rs @@ -1,4 +1,9 @@ -use std::{collections::BTreeSet, convert::TryInto, fs, path::PathBuf}; +use std::{ + collections::BTreeSet, + convert::TryInto, + fs, + path::{Path, PathBuf}, +}; use pkg::{PackageName, package::PackageError, recipes}; use regex::Regex; @@ -146,7 +151,7 @@ pub struct PackageRecipe { } /// Everything required to build a Redox package -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Default, Deserialize, PartialEq, Serialize)] pub struct Recipe { /// Specifies how to download the source for this recipe pub source: Option, @@ -158,6 +163,18 @@ pub struct Recipe { pub package: PackageRecipe, } +impl Recipe { + pub fn new(file: &PathBuf) -> Result { + if !file.is_file() { + return Err(PackageError::FileMissing(file.clone())); + } + let toml = fs::read_to_string(&file) + .map_err(|err| PackageError::Parse(DeError::custom(err), Some(file.clone())))?; + let recipe: Recipe = toml::from_str(&toml) + .map_err(|err| PackageError::Parse(DeError::custom(err), Some(file.clone())))?; + Ok(recipe) + } +} #[derive(Debug, PartialEq)] pub struct CookRecipe { pub name: PackageName, @@ -168,24 +185,7 @@ pub struct CookRecipe { } impl CookRecipe { - pub fn new( - name: impl TryInto, - ) -> Result { - let name: PackageName = name.try_into()?; - let dir = recipes::find(name.as_str()) - .ok_or_else(|| PackageError::PackageNotFound(name.clone()))?; - let file = dir.join("recipe.toml"); - if !file.is_file() { - return Err(PackageError::FileMissing(file)); - } - - let toml = fs::read_to_string(&file) - .map_err(|err| PackageError::Parse(DeError::custom(err), Some(file.clone())))?; - - let recipe: Recipe = toml::from_str(&toml) - .map_err(|err| PackageError::Parse(DeError::custom(err), Some(file)))?; - - let dir = dir.to_path_buf(); + pub fn new(name: PackageName, dir: PathBuf, recipe: Recipe) -> Result { Ok(Self { name, dir, @@ -194,6 +194,29 @@ impl CookRecipe { }) } + pub fn from_name( + name: impl TryInto, + ) -> Result { + let name: PackageName = name.try_into()?; + let dir = recipes::find(name.as_str()) + .ok_or_else(|| PackageError::PackageNotFound(name.clone()))?; + let file = dir.join("recipe.toml"); + let recipe = Recipe::new(&file)?; + Self::new(name, dir.to_path_buf(), recipe) + } + + pub fn from_path(dir: &Path, read_recipe: bool) -> Result { + let file = dir.join("recipe.toml"); + let name: PackageName = file.file_name().unwrap().try_into()?; + let recipe = if read_recipe { + Recipe::new(&file)? + } else { + // clean/unfetch don't need to read recipe + Recipe::default() + }; + Self::new(name, dir.to_path_buf(), recipe) + } + pub fn new_recursive( names: &[PackageName], recursion: usize, @@ -204,7 +227,7 @@ impl CookRecipe { let mut recipes = Vec::new(); for name in names { - let recipe = Self::new(name.as_str())?; + let recipe = Self::from_name(name.as_str())?; let dependencies = Self::new_recursive(&recipe.recipe.build.dependencies, recursion - 1).map_err( @@ -253,7 +276,7 @@ impl CookRecipe { let mut recipes: Vec = Vec::new(); for name in names { - let recipe = Self::new(name.as_str())?; + let recipe = Self::from_name(name.as_str())?; let dependencies = Self::get_package_deps_recursive( &recipe.recipe.package.dependencies,