Compiling with direct call to cook

This commit is contained in:
Wildan M 2025-10-24 16:18:11 +07:00
parent 97bef43ca0
commit 8e2ac316e4
8 changed files with 335 additions and 182 deletions

View File

@ -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(())

View File

@ -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<Self, Self::Err> {
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<Self, std::io::Error> {
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<String>) -> anyhow::Result<(CliConfig, CliCommand, Vec<CookRecipe>)> {
let mut config = CliConfig::new()?;
let mut command: Option<String> = None;
let mut recipe_paths: BTreeSet<PathBuf> = BTreeSet::new();
let mut recipe_names: Vec<PackageName> = 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::<Result<Vec<CookRecipe>, 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(),
))
}

View File

@ -26,6 +26,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.next()
.expect("Usage: repo_builder <REPO_DIR> <recipe1> <recipe2> ...");
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.

View File

@ -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<bool>,
/// whether to set jobs number instead of from nproc
pub jobs: Option<usize>,
/// whether to use TUI to allow parallel build
/// default value is yes if "CI" env unset and STDIN is open.
pub tui: Option<bool>,
/// whether to ignore build errors
pub nonstop: Option<bool>,
/// whether to not capture build output,
/// default is true if "tui" is false.
/// build failure still be printed anyway
pub verbose: Option<bool>,
}
#[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<CookConfigOpt> 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<String, String>,
}
static CONFIG: OnceLock<CookbookConfig> = 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<T: FromStr>(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");

View File

@ -151,6 +151,10 @@ pub fn build(
) -> Result<(PathBuf, BTreeSet<PackageName>), 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() {

View File

@ -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<String,
})
}
pub fn fetch_offline(recipe_dir: &Path, source: &Option<SourceRecipe>) -> Result<PathBuf, String> {
pub fn fetch_offline(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, String> {
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<SourceRecipe>) -> Result
Ok(source_dir)
}
pub fn fetch(recipe_dir: &Path, source: &Option<SourceRecipe>) -> Result<PathBuf, String> {
pub fn fetch(recipe_dir: &Path, recipe: &Recipe) -> Result<PathBuf, String> {
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<SourceRecipe>) -> Result<PathBuf
command
.arg("clone")
.arg("--recursive")
.arg(translate_mirror(git));
.arg(translate_mirror(&git));
if let Some(branch) = branch {
command.arg("--branch").arg(branch);
}
@ -268,7 +278,6 @@ pub fn fetch(recipe_dir: &Path, source: &Option<SourceRecipe>) -> Result<PathBuf
// Local Sources
None => {
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(),

View File

@ -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<PackageName>,
) -> Result<PathBuf, String> {
) -> 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(());
}

View File

@ -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<SourceRecipe>,
@ -158,6 +163,18 @@ pub struct Recipe {
pub package: PackageRecipe,
}
impl Recipe {
pub fn new(file: &PathBuf) -> Result<Recipe, PackageError> {
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<PackageName, Error = PackageError>,
) -> Result<Self, PackageError> {
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<Self, PackageError> {
Ok(Self {
name,
dir,
@ -194,6 +194,29 @@ impl CookRecipe {
})
}
pub fn from_name(
name: impl TryInto<PackageName, Error = PackageError>,
) -> Result<Self, PackageError> {
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<Self, PackageError> {
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<PackageName> = 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,