diff --git a/src/bin/cook.rs b/src/bin/cook.rs index 7bc3502fe..8f33c9667 100644 --- a/src/bin/cook.rs +++ b/src/bin/cook.rs @@ -1,4 +1,5 @@ use cookbook::config::init_config; +use cookbook::cook::build::build_remote; use cookbook::cook::fetch::*; use cookbook::cook::fs::*; use cookbook::cook::script::SHARED_PRESCRIPT; @@ -555,6 +556,7 @@ done flags_fn("COOKBOOK_MESON_FLAGS", mesonflags), ), BuildKind::Custom { script } => script.clone(), + BuildKind::Remote => return build_remote(target_dir, name, offline_mode), BuildKind::None => "".to_owned(), }; diff --git a/src/cook.rs b/src/cook.rs index e50800384..d538dbc35 100644 --- a/src/cook.rs +++ b/src/cook.rs @@ -2,5 +2,5 @@ pub mod fetch; pub mod fs; pub mod script; //TODO: Move rest of cook functions here in the next refactor -//pub mod build; +pub mod build; //pub mod package; diff --git a/src/cook/build.rs b/src/cook/build.rs new file mode 100644 index 000000000..cda47fd65 --- /dev/null +++ b/src/cook/build.rs @@ -0,0 +1,84 @@ +use std::{ + collections::BTreeSet, + fs, + path::{Path, PathBuf}, +}; + +use pkg::{Package, PackageName}; +use redoxer::target; + +use crate::{REMOTE_PKG_SOURCE, cook::fs::*, recipe::AutoDeps}; + +fn get_remote_url(name: &PackageName, ext: &str) -> String { + return format!("{}/{}/{}.{}", REMOTE_PKG_SOURCE, target(), name, ext); +} +fn get_pubkey_url() -> String { + return format!("{}/id_ed25519.pub.toml", REMOTE_PKG_SOURCE); +} + +pub fn build_remote( + target_dir: &Path, + name: &PackageName, + offline_mode: bool, +) -> Result<(PathBuf, BTreeSet), String> { + // download straight from remote source then declare pkg dependencies as autodeps dependency + let stage_dir = target_dir.join("stage"); + + let source_pkgar = target_dir.join("source.pkgar"); + let source_toml = target_dir.join("source.toml"); + let source_pubkey = target_dir.join("id_ed25519.pub.toml"); + + if !offline_mode { + download_wget(&get_remote_url(name, "pkgar"), &source_pkgar)?; + download_wget(&get_remote_url(name, "toml"), &source_toml)?; + download_wget(&get_pubkey_url(), &source_pubkey)?; + } else { + offline_check_exists(&source_pkgar)?; + offline_check_exists(&source_toml)?; + offline_check_exists(&source_pubkey)?; + } + + if stage_dir.is_dir() && modified(&source_pkgar)? > modified(&stage_dir)? { + remove_all(&stage_dir)? + } + if !stage_dir.is_dir() { + let stage_dir_tmp = target_dir.join("stage.tmp"); + + pkgar::extract(&source_pubkey, &source_pkgar, &stage_dir_tmp).map_err(|err| { + format!( + "failed to install '{}' in '{}': {:?}", + source_pkgar.display(), + stage_dir_tmp.display(), + err + ) + })?; + + // Move stage.tmp to stage atomically + rename(&stage_dir_tmp, &stage_dir)?; + } + + let auto_deps_path = target_dir.join("auto_deps.toml"); + if auto_deps_path.is_file() && modified(&auto_deps_path)? < modified(&stage_dir)? { + remove_all(&auto_deps_path)? + } + + let auto_deps = if auto_deps_path.exists() { + let toml_content = + fs::read_to_string(&auto_deps_path).map_err(|_| "failed to read cached auto_deps")?; + let wrapper: AutoDeps = + toml::from_str(&toml_content).map_err(|_| "failed to deserialize cached auto_deps")?; + wrapper.packages + } else { + let toml_content = + fs::read_to_string(&source_toml).map_err(|_| "failed to read source.toml")?; + let pkg_toml: Package = + toml::from_str(&toml_content).map_err(|_| "failed to deserialize source.toml")?; + let wrapper = AutoDeps { + packages: pkg_toml.depends.into_iter().collect(), + }; + serialize_and_write(&auto_deps_path, &wrapper)?; + wrapper.packages + }; + + Ok((stage_dir, auto_deps)) +} diff --git a/src/cook/fetch.rs b/src/cook/fetch.rs index 21f45daa0..c3ce9bdc7 100644 --- a/src/cook/fetch.rs +++ b/src/cook/fetch.rs @@ -42,12 +42,7 @@ pub fn fetch_offline(recipe_dir: &Path, source: &Option) -> Result script: _, shallow_clone: _, }) => { - if !source_dir.is_dir() { - return Err(format!( - "'{dir}' is not exist and unable to continue in offline mode", - dir = source_dir.display(), - )); - } + offline_check_exists(&source_dir)?; } Some(SourceRecipe::Tar { tar: _, @@ -75,10 +70,7 @@ pub fn fetch_offline(recipe_dir: &Path, source: &Option) -> Result )); } } else { - return Err(format!( - "'{dir}' is not exist and unable to continue in offline mode", - dir = source_dir.display(), - )); + offline_check_exists(&source_dir)?; } } } @@ -229,18 +221,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/fs.rs b/src/cook/fs.rs index 0fa51ecfd..f156bb10a 100644 --- a/src/cook/fs.rs +++ b/src/cook/fs.rs @@ -2,12 +2,14 @@ use serde::Serialize; use std::{ fs, io::{self, Write}, - path::Path, - process::{self, Stdio}, + path::{Path, PathBuf}, + process::{self, Command, Stdio}, time::SystemTime, }; use walkdir::{DirEntry, WalkDir}; +use crate::config::translate_mirror; + //TODO: pub(crate) for all of these functions pub fn remove_all(path: &Path) -> Result<(), String> { @@ -192,3 +194,25 @@ pub fn serialize_and_write(file_path: &Path, content: &T) -> Resul .map_err(|err| format!("Failed to write to file '{}': {}", file_path.display(), err))?; Ok(()) } + +pub fn offline_check_exists(path: &PathBuf) -> Result<(), String> { + if !path.exists() { + return Err(format!( + "'{path}' is not exist and unable to continue in offline mode", + path = path.display(), + ))?; + } + Ok(()) +} + +pub fn download_wget(url: &str, dest: &PathBuf) -> Result<(), String> { + if !dest.is_file() { + let dest_tmp = PathBuf::from(format!("{}.tmp", dest.display())); + let mut command = Command::new("wget"); + command.arg(translate_mirror(url)); + command.arg("--continue").arg("-O").arg(&dest_tmp); + run_command(command)?; + rename(&dest_tmp, &dest)?; + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index cef82ad06..0c8f7153b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,12 +8,9 @@ mod progress_bar; /// Default for maximum number of levels to descend down dependencies tree. pub const WALK_DEPTH: usize = 16; -#[cfg(target_os = "redox")] -pub fn is_redox() -> bool { - true -} +/// Default remote package source, for recipes with build type = "remote" +pub const REMOTE_PKG_SOURCE: &str = "https://static.redox-os.org/pkg"; -#[cfg(not(target_os = "redox"))] pub fn is_redox() -> bool { - false + cfg!(target_os = "redox") } diff --git a/src/recipe.rs b/src/recipe.rs index ff71fd5b0..99e808007 100644 --- a/src/recipe.rs +++ b/src/recipe.rs @@ -89,6 +89,9 @@ pub enum BuildKind { /// Will not build (for meta packages) #[serde(rename = "none")] None, + /// Will download compiled package from remote + #[serde(rename = "remote")] + Remote, /// Will build and install using cargo #[serde(rename = "cargo")] Cargo {