refactor(kernel): report kernel download progress through the sink
get_default_kernel emits Progress events for the APK transfer instead of drawing a bar to stderr from inside the library.
This commit is contained in:
@@ -63,7 +63,7 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
veprintln!("QEMU mode: downloading default kernel...");
|
veprintln!("QEMU mode: downloading default kernel...");
|
||||||
kernel::get_default_kernel(cache.dir(), &arch)?
|
kernel::get_default_kernel(cache.dir(), &arch, &ecr::NoProgress)?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+23
-18
@@ -17,9 +17,9 @@
|
|||||||
//! kernel image gzipped, and QEMU's riscv `-kernel` loader understands
|
//! kernel image gzipped, and QEMU's riscv `-kernel` loader understands
|
||||||
//! only ELF, uImage and raw images.
|
//! only ELF, uImage and raw images.
|
||||||
|
|
||||||
|
use crate::progress::{Progress, Unit};
|
||||||
use crate::veprintln;
|
use crate::veprintln;
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use indicatif::{ProgressBar, ProgressStyle};
|
|
||||||
use std::io::{Read, Seek};
|
use std::io::{Read, Seek};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -237,7 +237,12 @@ fn find_kernel_package(branch: &str, arch: &str) -> Result<(&'static str, String
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Download and extract the kernel from an Alpine package repository
|
/// Download and extract the kernel from an Alpine package repository
|
||||||
fn download_alpine_kernel(branch: &str, arch: &str, dest: &Path) -> Result<()> {
|
fn download_alpine_kernel(
|
||||||
|
branch: &str,
|
||||||
|
arch: &str,
|
||||||
|
dest: &Path,
|
||||||
|
progress: &dyn Progress,
|
||||||
|
) -> Result<()> {
|
||||||
let (package, version) = find_kernel_package(branch, arch)?;
|
let (package, version) = find_kernel_package(branch, arch)?;
|
||||||
veprintln!("Found {} version: {}", package, version);
|
veprintln!("Found {} version: {}", package, version);
|
||||||
|
|
||||||
@@ -252,7 +257,7 @@ fn download_alpine_kernel(branch: &str, arch: &str, dest: &Path) -> Result<()> {
|
|||||||
|
|
||||||
// Use async download via the existing download module pattern
|
// Use async download via the existing download module pattern
|
||||||
let rt = tokio::runtime::Runtime::new().context("Failed to create Tokio runtime")?;
|
let rt = tokio::runtime::Runtime::new().context("Failed to create Tokio runtime")?;
|
||||||
rt.block_on(download_kernel_async(&url, dest))?;
|
rt.block_on(download_kernel_async(&url, dest, progress))?;
|
||||||
|
|
||||||
// Extract the kernel image from the APK, then store it decompressed.
|
// Extract the kernel image from the APK, then store it decompressed.
|
||||||
// Remove the ~45MB APK whatever the outcome — don't leave it behind
|
// Remove the ~45MB APK whatever the outcome — don't leave it behind
|
||||||
@@ -264,7 +269,7 @@ fn download_alpine_kernel(branch: &str, arch: &str, dest: &Path) -> Result<()> {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn download_kernel_async(url: &str, dest: &Path) -> Result<()> {
|
async fn download_kernel_async(url: &str, dest: &Path, progress: &dyn Progress) -> Result<()> {
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
let client = reqwest::Client::builder()
|
let client = reqwest::Client::builder()
|
||||||
.timeout(Duration::from_secs(300))
|
.timeout(Duration::from_secs(300))
|
||||||
@@ -286,13 +291,10 @@ async fn download_kernel_async(url: &str, dest: &Path) -> Result<()> {
|
|||||||
|
|
||||||
let total_size = response.content_length().unwrap_or(0);
|
let total_size = response.content_length().unwrap_or(0);
|
||||||
|
|
||||||
// Setup progress bar
|
progress.begin(
|
||||||
let pb = ProgressBar::new(total_size);
|
"Downloading kernel",
|
||||||
pb.set_style(
|
Unit::Bytes,
|
||||||
ProgressStyle::default_bar()
|
(total_size > 0).then_some(total_size),
|
||||||
.template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})")
|
|
||||||
.unwrap()
|
|
||||||
.progress_chars("#>-"),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let temp_apk = dest.with_extension("apk.partial");
|
let temp_apk = dest.with_extension("apk.partial");
|
||||||
@@ -300,7 +302,6 @@ async fn download_kernel_async(url: &str, dest: &Path) -> Result<()> {
|
|||||||
.await
|
.await
|
||||||
.context("Failed to create temp APK file")?;
|
.context("Failed to create temp APK file")?;
|
||||||
|
|
||||||
let mut downloaded: u64 = 0;
|
|
||||||
let mut stream = response.bytes_stream();
|
let mut stream = response.bytes_stream();
|
||||||
|
|
||||||
while let Some(chunk) = stream.next().await {
|
while let Some(chunk) = stream.next().await {
|
||||||
@@ -308,15 +309,14 @@ async fn download_kernel_async(url: &str, dest: &Path) -> Result<()> {
|
|||||||
tokio::io::AsyncWriteExt::write_all(&mut file, &chunk)
|
tokio::io::AsyncWriteExt::write_all(&mut file, &chunk)
|
||||||
.await
|
.await
|
||||||
.context("Failed to write chunk")?;
|
.context("Failed to write chunk")?;
|
||||||
downloaded += chunk.len() as u64;
|
progress.advance(chunk.len() as u64);
|
||||||
pb.set_position(downloaded);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tokio::io::AsyncWriteExt::flush(&mut file)
|
tokio::io::AsyncWriteExt::flush(&mut file)
|
||||||
.await
|
.await
|
||||||
.context("Failed to flush file")?;
|
.context("Failed to flush file")?;
|
||||||
|
|
||||||
pb.finish_with_message("Download complete");
|
progress.finish();
|
||||||
|
|
||||||
// Rename to final name
|
// Rename to final name
|
||||||
let final_apk = dest.with_extension("apk");
|
let final_apk = dest.with_extension("apk");
|
||||||
@@ -392,8 +392,13 @@ fn decompress_kernel_if_gzipped(path: &Path) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the path to the cached default kernel for the given architecture.
|
/// Get the path to the cached default kernel for the given architecture.
|
||||||
/// Downloads and caches it if not present.
|
/// Downloads and caches it if not present, reporting download progress
|
||||||
pub fn get_default_kernel(cache_dir: &Path, arch: &str) -> Result<PathBuf> {
|
/// through `progress`.
|
||||||
|
pub fn get_default_kernel(
|
||||||
|
cache_dir: &Path,
|
||||||
|
arch: &str,
|
||||||
|
progress: &dyn Progress,
|
||||||
|
) -> Result<PathBuf> {
|
||||||
let alpine_arch = alpine_kernel_arch(arch);
|
let alpine_arch = alpine_kernel_arch(arch);
|
||||||
|
|
||||||
// Cache filename includes architecture
|
// Cache filename includes architecture
|
||||||
@@ -414,7 +419,7 @@ pub fn get_default_kernel(cache_dir: &Path, arch: &str) -> Result<PathBuf> {
|
|||||||
veprintln!("Using Alpine branch: {}", branch);
|
veprintln!("Using Alpine branch: {}", branch);
|
||||||
|
|
||||||
// Download and extract the kernel
|
// Download and extract the kernel
|
||||||
download_alpine_kernel(&branch, alpine_arch, &kernel_path)?;
|
download_alpine_kernel(&branch, alpine_arch, &kernel_path, progress)?;
|
||||||
|
|
||||||
Ok(kernel_path)
|
Ok(kernel_path)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user