mirror of
https://gitlab.redox-os.org/redox-os/redox.git
synced 2026-06-24 05:44:17 +08:00
221 lines
6.5 KiB
Rust
221 lines
6.5 KiB
Rust
use std::{
|
|
cell::RefCell,
|
|
io::{PipeWriter, Write},
|
|
path::{Path, PathBuf},
|
|
rc::Rc,
|
|
time::Duration,
|
|
};
|
|
|
|
use crate::cook::fs;
|
|
use pkg::{
|
|
PackageName, RemotePackage, RepoManager, Repository,
|
|
callback::{Callback, PlainCallback, SilentCallback},
|
|
net_backend::{CurlBackend, DownloadBackend},
|
|
};
|
|
|
|
// TODO: This is a workaround, but as long as whole
|
|
// fetch operation is in single thread, this is ok
|
|
thread_local! {
|
|
static BINARY_REPO: RefCell<Option<(RepoManager, Repository)>> = RefCell::new(None);
|
|
}
|
|
|
|
fn load_cached_repo(path: &Path) -> Option<Repository> {
|
|
let metadata = std::fs::metadata(path).ok()?;
|
|
|
|
if !crate::config::get_config().cook.offline {
|
|
let stale_time = std::time::SystemTime::now().checked_sub(Duration::from_secs(8 * 3600))?;
|
|
if metadata.modified().ok()? < stale_time {
|
|
// stale cache
|
|
let _ = std::fs::remove_file(path);
|
|
return None;
|
|
}
|
|
}
|
|
|
|
let toml_str = std::fs::read_to_string(path).ok()?;
|
|
Repository::from_toml(&toml_str).ok()
|
|
}
|
|
|
|
fn init_binary_repo() -> (RepoManager, Repository) {
|
|
let callback = Rc::new(RefCell::new(SilentCallback::new()));
|
|
let download_backend = CurlBackend::new().expect("Curl not found");
|
|
let mut repo = RepoManager::new(callback, Box::new(download_backend));
|
|
let target = redoxer::target();
|
|
repo.add_remote(crate::REMOTE_PKG_SOURCE, target)
|
|
.expect("Unable to add remote");
|
|
|
|
let repo_path = PathBuf::from("build/remotes");
|
|
repo.set_download_path(repo_path.clone());
|
|
repo.sync_keys().expect("Unable to sync keys");
|
|
|
|
let repo_toml = load_cached_repo(&repo_path.join(format!("{target}_repo.toml")))
|
|
.unwrap_or_else(|| {
|
|
let repo = download_repo(&repo, repo_path)
|
|
.map_err(|e| {
|
|
eprintln!(
|
|
"Unable to load server repo.toml, all recipes will build from source: {e}"
|
|
);
|
|
e
|
|
})
|
|
.unwrap_or_default();
|
|
repo
|
|
});
|
|
// reset here to not clobber pty
|
|
repo.callback = Rc::new(RefCell::new(PlainCallback::new()));
|
|
(repo, repo_toml)
|
|
}
|
|
|
|
fn download_repo(repo: &RepoManager, repo_path: PathBuf) -> crate::Result<Repository> {
|
|
let (toml_str, _) = repo.get_package_toml(&PackageName::new("repo").unwrap())?;
|
|
let repo = Repository::from_toml(&toml_str)?;
|
|
let target = redoxer::target();
|
|
fs::serialize_and_write(&repo_path.join(format!("{target}_repo.toml")), &repo)?;
|
|
Ok(repo)
|
|
}
|
|
|
|
pub fn get_binary_repo() -> (RepoManager, Repository) {
|
|
BINARY_REPO.with(|cell| {
|
|
let mut opt = cell.borrow_mut();
|
|
if opt.is_none() {
|
|
*opt = Some(init_binary_repo());
|
|
}
|
|
let (repo, repo_toml) = opt.as_ref().unwrap();
|
|
((*repo).clone(), repo_toml.clone())
|
|
})
|
|
}
|
|
|
|
pub struct PlainPtyCallback {
|
|
size: u64,
|
|
unknown_size: bool,
|
|
pos: u64,
|
|
fetch_processed: usize,
|
|
fetch_total: usize,
|
|
interactive: bool,
|
|
download_file: Option<String>,
|
|
pty: PipeWriter,
|
|
}
|
|
|
|
impl PlainPtyCallback {
|
|
pub fn new(pty: PipeWriter) -> Self {
|
|
Self {
|
|
size: 0,
|
|
unknown_size: false,
|
|
pos: 0,
|
|
fetch_processed: 0,
|
|
fetch_total: 0,
|
|
interactive: false,
|
|
download_file: None,
|
|
pty,
|
|
}
|
|
}
|
|
|
|
/// Set if user require to agree on terminal
|
|
pub fn set_interactive(&mut self, enabled: bool) {
|
|
self.interactive = enabled;
|
|
}
|
|
|
|
fn flush(&self) {
|
|
let _ = std::io::stderr().flush();
|
|
}
|
|
|
|
pub fn format_size(bytes: u64) -> String {
|
|
if bytes == 0 {
|
|
return "0 B".to_string();
|
|
}
|
|
const UNITS: [&str; 5] = ["B", "KiB", "MiB", "GiB", "TiB"];
|
|
let i = (bytes as f64).log(1024.0).floor() as usize;
|
|
let size = bytes as f64 / 1024.0_f64.powi(i as i32);
|
|
format!("{:.2} {}", size, UNITS[i])
|
|
}
|
|
|
|
fn downloading_str(&self) -> &'static str {
|
|
"Downloading"
|
|
}
|
|
}
|
|
|
|
const RESET_LINE: &str = "\r\x1b[2K";
|
|
|
|
impl Callback for PlainPtyCallback {
|
|
fn fetch_start(&mut self, initial_count: usize) {
|
|
self.fetch_total = 0;
|
|
self.fetch_processed = 0;
|
|
self.fetch_package_increment(0, initial_count);
|
|
}
|
|
|
|
fn fetch_package_name(&mut self, pkg_name: &PackageName) {
|
|
// resuming after fetch_package_increment
|
|
let _ = write!(&self.pty, " {}", pkg_name.as_str());
|
|
self.flush();
|
|
}
|
|
|
|
fn fetch_package_increment(&mut self, added_processed: usize, added_count: usize) {
|
|
self.fetch_processed += added_processed;
|
|
self.fetch_total += added_count;
|
|
|
|
let _ = write!(
|
|
&self.pty,
|
|
"{RESET_LINE}Fetching: [{}/{}]",
|
|
self.fetch_processed, self.fetch_total
|
|
);
|
|
self.flush();
|
|
}
|
|
|
|
fn fetch_end(&mut self) {
|
|
if self.fetch_processed == self.fetch_total {
|
|
let _ = writeln!(&self.pty, "{RESET_LINE}Fetch complete.");
|
|
} else {
|
|
let _ = writeln!(&self.pty, "{RESET_LINE}Fetch incomplete.");
|
|
}
|
|
}
|
|
|
|
fn download_start(&mut self, length: u64, file: &str) {
|
|
self.size = length;
|
|
self.unknown_size = length == 0;
|
|
self.pos = 0;
|
|
if !self.unknown_size {
|
|
let _ = write!(&self.pty, "{RESET_LINE}{} {file}", self.downloading_str());
|
|
self.download_file = Some(file.to_string());
|
|
self.flush();
|
|
}
|
|
}
|
|
|
|
fn download_increment(&mut self, downloaded: u64) {
|
|
self.pos += downloaded;
|
|
if self.unknown_size {
|
|
self.size += downloaded;
|
|
}
|
|
if self.unknown_size {
|
|
return;
|
|
}
|
|
|
|
// keep using MB for consistency
|
|
let pos_mb = self.pos as f64 / 1_048_576.0;
|
|
let size_mb = self.size as f64 / 1_048_576.0;
|
|
let file_name = self
|
|
.download_file
|
|
.as_ref()
|
|
.map(|s| s.as_str())
|
|
.unwrap_or("");
|
|
let _ = write!(
|
|
&self.pty,
|
|
"{RESET_LINE}{} {} [{:.2} MB / {:.2} MB]",
|
|
self.downloading_str(),
|
|
file_name,
|
|
pos_mb,
|
|
size_mb
|
|
);
|
|
self.flush();
|
|
}
|
|
|
|
fn download_end(&mut self) {
|
|
if !self.unknown_size {
|
|
let _ = writeln!(&self.pty, "");
|
|
self.download_file = None;
|
|
}
|
|
}
|
|
|
|
fn install_extract(&mut self, remote_pkg: &RemotePackage) {
|
|
let _ = writeln!(&self.pty, "Extracting {}...", remote_pkg.package.name);
|
|
self.flush();
|
|
}
|
|
}
|