Add a way to download compiled recipes from remote

This commit is contained in:
Wildan Mubarok 2025-10-19 12:20:39 +00:00 committed by Jeremy Soller
parent e686d5c792
commit dba3334d82
7 changed files with 123 additions and 31 deletions

View File

@ -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(),
};

View File

@ -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;

84
src/cook/build.rs Normal file
View File

@ -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<PackageName>), 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))
}

View File

@ -42,12 +42,7 @@ pub fn fetch_offline(recipe_dir: &Path, source: &Option<SourceRecipe>) -> 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<SourceRecipe>) -> 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<SourceRecipe>) -> Result<PathBuf
while {
if !source_tar.is_file() {
tar_updated = true;
//TODO: replace wget
if !source_tar.is_file() {
let source_tar_tmp = recipe_dir.join("source.tar.tmp");
let mut command = Command::new("wget");
command.arg(translate_mirror(tar));
command.arg("--continue").arg("-O").arg(&source_tar_tmp);
run_command(command)?;
// Move source.tar.tmp to source.tar atomically
rename(&source_tar_tmp, &source_tar)?;
}
download_wget(&tar, &source_tar)?;
}
let source_tar_blake3 = get_blake3(&source_tar, tar_updated)?;
if let Some(blake3) = blake3 {
@ -287,6 +268,7 @@ 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

@ -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<T: Serialize>(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(())
}

View File

@ -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")
}

View File

@ -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 {