diff --git a/Cargo.lock b/Cargo.lock index 03d32af84e7..a741c5ac3f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5641,6 +5641,7 @@ dependencies = [ "flate2", "hex", "indexmap", + "indicatif", "isatty", "libc", "log", diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index 2bbc529f89c..9f02d33c591 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -95,6 +95,7 @@ clap-verbosity-flag = "2" async-trait = "0.1.68" tokio = { version = "1.28.1", features = ["macros", "rt-multi-thread"] } once_cell = "1.17.1" +indicatif = "0.17.5" # NOTE: Must use different features for clap because the "color" feature does not # work on wasi due to the anstream dependency not compiling. @@ -126,84 +127,29 @@ unix_mode = "0.1.3" [features] # Don't add the compiler features in default, please add them on the Makefile # since we might want to autoconfigure them depending on the availability on the host. -default = [ - "sys", - "wat", - "wast", - "compiler", - "wasmer-artifact-create", - "static-artifact-create", -] +default = ["sys", "wat", "wast", "compiler", "wasmer-artifact-create", "static-artifact-create"] backend = [] -coredump = [ - "wasm-coredump-builder", -] -sys = [ - "compiler", - "wasmer-vm", -] -jsc = [ - "backend", - "wasmer/jsc", - "wasmer/std" -] +coredump = ["wasm-coredump-builder"] +sys = ["compiler", "wasmer-vm"] +jsc = ["backend", "wasmer/jsc", "wasmer/std"] wast = ["wasmer-wast"] -host-net = [ "virtual-net/host-net" ] +host-net = ["virtual-net/host-net"] wat = ["wasmer/wat"] -compiler = [ - "backend", - "wasmer/compiler", - "wasmer-compiler/translator", - "wasmer-compiler/compiler" -] -wasmer-artifact-create = ["compiler", - "wasmer/wasmer-artifact-load", - "wasmer/wasmer-artifact-create", - "wasmer-compiler/wasmer-artifact-load", - "wasmer-compiler/wasmer-artifact-create", - "wasmer-object", - ] -static-artifact-create = ["compiler", - "wasmer/static-artifact-load", - "wasmer/static-artifact-create", - "wasmer-compiler/static-artifact-load", - "wasmer-compiler/static-artifact-create", - "wasmer-object", - ] -wasmer-artifact-load = ["compiler", - "wasmer/wasmer-artifact-load", - "wasmer-compiler/wasmer-artifact-load", - ] -static-artifact-load = ["compiler", - "wasmer/static-artifact-load", - "wasmer-compiler/static-artifact-load", - ] -experimental-io-devices = [ - "wasmer-wasix-experimental-io-devices", -] -singlepass = [ - "wasmer-compiler-singlepass", - "compiler", -] -cranelift = [ - "wasmer-compiler-cranelift", - "compiler", -] -llvm = [ - "wasmer-compiler-llvm", - "compiler", -] +compiler = ["backend", "wasmer/compiler", "wasmer-compiler/translator", "wasmer-compiler/compiler"] +wasmer-artifact-create = ["compiler", "wasmer/wasmer-artifact-load", "wasmer/wasmer-artifact-create", "wasmer-compiler/wasmer-artifact-load", "wasmer-compiler/wasmer-artifact-create", "wasmer-object"] +static-artifact-create = ["compiler", "wasmer/static-artifact-load", "wasmer/static-artifact-create", "wasmer-compiler/static-artifact-load", "wasmer-compiler/static-artifact-create", "wasmer-object"] +wasmer-artifact-load = ["compiler", "wasmer/wasmer-artifact-load", "wasmer-compiler/wasmer-artifact-load"] +static-artifact-load = ["compiler", "wasmer/static-artifact-load", "wasmer-compiler/static-artifact-load"] +experimental-io-devices = ["wasmer-wasix-experimental-io-devices"] +singlepass = ["wasmer-compiler-singlepass", "compiler"] +cranelift = ["wasmer-compiler-cranelift", "compiler"] +llvm = ["wasmer-compiler-llvm", "compiler"] disable-all-logging = ["wasmer-wasix/disable-all-logging", "log/release_max_level_off"] headless = [] headless-minimal = ["headless", "disable-all-logging"] # Optional -enable-serde = [ - "wasmer/enable-serde", - "wasmer-vm/enable-serde", - "wasmer-compiler/enable-serde", - "wasmer-wasix/enable-serde", -] +enable-serde = ["wasmer/enable-serde", "wasmer-vm/enable-serde", "wasmer-compiler/enable-serde", "wasmer-wasix/enable-serde"] [target.'cfg(target_os = "windows")'.dependencies] colored = "2.0.0" diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 85505b18da1..f947b6d3da2 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -17,6 +17,7 @@ use std::{ use anyhow::{Context, Error}; use clap::Parser; use clap_verbosity_flag::WarnLevel; +use indicatif::{MultiProgress, ProgressBar}; use once_cell::sync::Lazy; use sha2::{Digest, Sha256}; use tempfile::NamedTempFile; @@ -33,7 +34,7 @@ use wasmer_registry::Package; use wasmer_wasix::{ bin_factory::BinaryPackage, runners::{MappedDirectory, Runner}, - runtime::resolver::PackageSpecifier, + runtime::{package_loader::PackageLoader, resolver::PackageSpecifier}, WasiError, }; use wasmer_wasix::{ @@ -48,6 +49,8 @@ use webc::{metadata::Manifest, Container}; use crate::{commands::run::wasi::Wasi, error::PrettyError, store::StoreOptions}; +const TICK: Duration = Duration::from_millis(250); + static WASMER_HOME: Lazy = Lazy::new(|| { wasmer_registry::WasmerConfig::get_wasmer_dir() .ok() @@ -93,6 +96,7 @@ impl Run { fn execute_inner(&self) -> Result<(), Error> { crate::logging::set_up_logging(self.verbosity.log_level_filter()); + let runtime = tokio::runtime::Builder::new_multi_thread() .enable_all() .build()?; @@ -108,12 +112,23 @@ impl Run { self.wasi .prepare_runtime(store.engine().clone(), &self.wasmer_dir, handle)?; + let progress = MultiProgress::new(); + + // This is a slow operation, so let's temporarily wrap the runtime with + // something that displays progress + let monitoring_runtime = MonitoringRuntime::new(runtime, progress.clone()); + let pb = progress.add(ProgressBar::new_spinner().with_message("Getting Ready...")); + pb.enable_steady_tick(TICK); + let target = self .input - .resolve_target(&runtime) + .resolve_target(&monitoring_runtime, &monitoring_runtime.progress) .with_context(|| format!("Unable to resolve \"{}\"", self.input))?; - let runtime: Arc = Arc::new(runtime); + pb.finish_and_clear(); + + let runtime: Arc = Arc::new(monitoring_runtime.runtime); + let result = { match target { ExecutableTarget::WebAssembly { module, path } => { @@ -130,20 +145,6 @@ impl Run { result } - fn execute_target( - &self, - executable_target: ExecutableTarget, - runtime: Arc, - store: Store, - ) -> Result<(), Error> { - match executable_target { - ExecutableTarget::WebAssembly { module, path } => { - self.execute_wasm(&path, &module, store, runtime) - } - ExecutableTarget::Package(pkg) => self.execute_webc(&pkg, runtime), - } - } - #[tracing::instrument(skip_all)] fn execute_wasm( &self, @@ -469,10 +470,14 @@ impl PackageSource { /// /// This will try to automatically download and cache any resources from the /// internet. - fn resolve_target(&self, rt: &dyn Runtime) -> Result { + fn resolve_target( + &self, + rt: &dyn Runtime, + progress: &MultiProgress, + ) -> Result { match self { PackageSource::File(path) => ExecutableTarget::from_file(path, rt), - PackageSource::Dir(d) => ExecutableTarget::from_dir(d, rt), + PackageSource::Dir(d) => ExecutableTarget::from_dir(d, rt, progress), PackageSource::Package(pkg) => { let pkg = rt .task_manager() @@ -549,10 +554,23 @@ enum ExecutableTarget { impl ExecutableTarget { /// Try to load a Wasmer package from a directory containing a `wasmer.toml` /// file. - fn from_dir(dir: &Path, runtime: &dyn Runtime) -> Result { + fn from_dir( + dir: &Path, + runtime: &dyn Runtime, + progress: &MultiProgress, + ) -> Result { + let pb = progress.add( + ProgressBar::new_spinner() + .with_message(format!("Loading \"{}\" into memory", dir.display())), + ); + pb.enable_steady_tick(TICK); + let webc = construct_webc_in_memory(dir)?; let container = Container::from_bytes(webc)?; + pb.finish_and_clear(); + progress.remove(&pb); + let pkg = runtime .task_manager() .block_on(BinaryPackage::from_webc(&container, runtime))?; @@ -757,3 +775,110 @@ fn get_exit_code( None } + +#[derive(Debug)] +struct MonitoringRuntime { + runtime: R, + progress: MultiProgress, +} + +impl MonitoringRuntime { + fn new(runtime: R, progress: MultiProgress) -> Self { + MonitoringRuntime { runtime, progress } + } +} + +impl wasmer_wasix::Runtime for MonitoringRuntime { + fn networking(&self) -> &virtual_net::DynVirtualNetworking { + self.runtime.networking() + } + + fn task_manager(&self) -> &Arc { + self.runtime.task_manager() + } + + fn package_loader( + &self, + ) -> Arc { + let inner = self.runtime.package_loader(); + Arc::new(MonitoringPackageLoader { + inner, + progress: self.progress.clone(), + }) + } + + fn module_cache( + &self, + ) -> Arc { + self.runtime.module_cache() + } + + fn source(&self) -> Arc { + let inner = self.runtime.source(); + Arc::new(MonitoringSource { + inner, + progress: self.progress.clone(), + }) + } +} + +#[derive(Debug)] +struct MonitoringSource { + inner: Arc, + progress: MultiProgress, +} + +#[async_trait::async_trait] +impl wasmer_wasix::runtime::resolver::Source for MonitoringSource { + async fn query( + &self, + package: &PackageSpecifier, + ) -> Result, Error> { + let pb = self + .progress + .add(ProgressBar::new_spinner().with_message(format!("Looking up {package}"))); + pb.enable_steady_tick(TICK); + + let result = self.inner.query(package).await; + + pb.finish_and_clear(); + self.progress.remove(&pb); + + result + } +} + +#[derive(Debug)] +struct MonitoringPackageLoader { + inner: Arc, + progress: MultiProgress, +} + +#[async_trait::async_trait] +impl wasmer_wasix::runtime::package_loader::PackageLoader for MonitoringPackageLoader { + async fn load( + &self, + summary: &wasmer_wasix::runtime::resolver::PackageSummary, + ) -> Result { + let pkg_id = summary.package_id(); + let pb = self + .progress + .add(ProgressBar::new_spinner().with_message(format!("Downloading {pkg_id}"))); + pb.enable_steady_tick(TICK); + + let result = self.inner.load(summary).await; + + pb.finish_and_clear(); + self.progress.remove(&pb); + + result + } + + async fn load_package_tree( + &self, + root: &Container, + resolution: &wasmer_wasix::runtime::resolver::Resolution, + ) -> Result { + self.inner.load_package_tree(root, resolution).await + } +}