diff --git a/docs/src/commands/index.md b/docs/src/commands/index.md index 8c4ff13..2b1ac7c 100644 --- a/docs/src/commands/index.md +++ b/docs/src/commands/index.md @@ -14,3 +14,23 @@ a Rust-generated WebAssembly project. [new]: ./new.html [build]: ./build.html [pack-pub]: ./pack-and-publish.html + +### Log levels + +By default `wasm-pack` displays a lot of useful information. + +You can cause it to display even *more* information by using `--verbose`, or you can silence *all* stdout by using `--quiet`. + +You can also use `--log-level` to have fine-grained control over wasm-pack's log output: + +* `--log-level info` is the default, it causes all messages to be logged. +* `--log-level warn` causes warnings and errors to be displayed, but not info. +* `--log-level error` causes only errors to be displayed. + +These flags are global flags, so they can be used with every command, and they must come *before* the command: + +```sh +wasm-pack --log-level error build +wasm-pack --quiet build +wasm-pack --verbose build +``` diff --git a/src/build/mod.rs b/src/build/mod.rs index 6a97035..f87133d 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -80,8 +80,14 @@ pub fn cargo_build_wasm( ) -> Result<(), Error> { let msg = format!("{}Compiling to Wasm...", emoji::CYCLONE); PBAR.info(&msg); + let mut cmd = Command::new("cargo"); cmd.current_dir(path).arg("build").arg("--lib"); + + if PBAR.quiet() { + cmd.arg("--quiet"); + } + match profile { BuildProfile::Profiling => { // Once there are DWARF debug info consumers, force enable debug @@ -99,6 +105,7 @@ pub fn cargo_build_wasm( // debug info by default. } } + cmd.arg("--target").arg("wasm32-unknown-unknown"); cmd.args(extra_options); child::run(cmd, "cargo build").context("Compiling your crate to WebAssembly failed")?; @@ -111,11 +118,19 @@ pub fn cargo_build_wasm( /// wasm-bindgen-cli to use when running tests. pub fn cargo_build_wasm_tests(path: &Path, debug: bool) -> Result<(), Error> { let mut cmd = Command::new("cargo"); + cmd.current_dir(path).arg("build").arg("--tests"); + + if PBAR.quiet() { + cmd.arg("--quiet"); + } + if !debug { cmd.arg("--release"); } + cmd.arg("--target").arg("wasm32-unknown-unknown"); + child::run(cmd, "cargo build").context("Compilation of your program failed")?; Ok(()) } diff --git a/src/install/mod.rs b/src/install/mod.rs index 7009c28..7999916 100644 --- a/src/install/mod.rs +++ b/src/install/mod.rs @@ -210,6 +210,10 @@ pub fn cargo_install( .arg("--root") .arg(&tmp); + if PBAR.quiet() { + cmd.arg("--quiet"); + } + let context = format!("Installing {} with cargo", tool); child::run(cmd, "cargo install").context(context)?; diff --git a/src/lib.rs b/src/lib.rs index 9e5f99f..3efc15f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,10 +45,10 @@ pub mod target; pub mod test; pub mod wasm_opt; -use progressbar::ProgressOutput; +use progressbar::{LogLevel, ProgressOutput}; /// The global progress bar and user-facing message output. -pub static PBAR: ProgressOutput = ProgressOutput; +pub static PBAR: ProgressOutput = ProgressOutput::new(); /// 📦 ✨ pack and publish your wasm! #[derive(Debug, StructOpt)] @@ -60,4 +60,12 @@ pub struct Cli { /// Log verbosity is based off the number of v used #[structopt(long = "verbose", short = "v", parse(from_occurrences))] pub verbosity: u8, + + #[structopt(long = "quiet", short = "q")] + /// No output printed to stdout + pub quiet: bool, + + #[structopt(long = "log-level", default_value = "info")] + /// The maximum level of messages that should be logged by wasm-pack. [possible values: info, warn, error] + pub log_level: LogLevel, } diff --git a/src/main.rs b/src/main.rs index 7fbf467..8f313d8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -80,6 +80,13 @@ fn run() -> Result<(), failure::Error> { } let args = Cli::from_args(); + + PBAR.set_log_level(args.log_level); + + if args.quiet { + PBAR.set_quiet(true); + } + run_wasm_pack(args.cmd)?; if let Ok(wasm_pack_version) = wasm_pack_version.try_recv() { diff --git a/src/progressbar.rs b/src/progressbar.rs index 50eea02..94ef6da 100644 --- a/src/progressbar.rs +++ b/src/progressbar.rs @@ -2,47 +2,111 @@ use console::style; use emoji; +use std::sync::atomic::{AtomicBool, AtomicU8, Ordering}; + +#[repr(u8)] +#[derive(Debug, Clone, Copy)] +/// The maximum log level for wasm-pack +// The ordering is important: the least verbose must be at +// the top and the most verbose at the bottom +pub enum LogLevel { + /// Logs only error + Error, + /// Logs only warn and error + Warn, + /// Logs everything + Info, +} + +impl std::str::FromStr for LogLevel { + type Err = failure::Error; + fn from_str(s: &str) -> Result { + match s { + "error" => Ok(LogLevel::Error), + "warn" => Ok(LogLevel::Warn), + "info" => Ok(LogLevel::Info), + _ => bail!("Unknown log-level: {}", s), + } + } +} /// Synchronized progress bar and status message printing. -pub struct ProgressOutput; +pub struct ProgressOutput { + quiet: AtomicBool, + log_level: AtomicU8, +} impl ProgressOutput { + /// Returns a new ProgressOutput + pub const fn new() -> Self { + Self { + quiet: AtomicBool::new(false), + log_level: AtomicU8::new(LogLevel::Info as u8), + } + } + /// Print the given message. fn message(&self, message: &str) { eprintln!("{}", message); } + /// Returns whether it should silence stdout or not + pub fn quiet(&self) -> bool { + self.quiet.load(Ordering::SeqCst) + } + + /// Causes it to silence stdout + pub fn set_quiet(&self, quiet: bool) { + self.quiet.store(quiet, Ordering::SeqCst); + } + + /// Returns whether the specified log level is enabled or not + pub fn is_log_enabled(&self, level: LogLevel) -> bool { + (level as u8) <= self.log_level.load(Ordering::SeqCst) + } + + /// Sets the log level for wasm-pack + pub fn set_log_level(&self, log_level: LogLevel) { + self.log_level.store(log_level as u8, Ordering::SeqCst); + } + /// Add an informational message. pub fn info(&self, message: &str) { - let info = format!("{}: {}", style("[INFO]").bold().dim(), message,); - self.message(&info); + if !self.quiet() && self.is_log_enabled(LogLevel::Info) { + let info = format!("{}: {}", style("[INFO]").bold().dim(), message,); + self.message(&info); + } } /// Add a warning message. pub fn warn(&self, message: &str) { - let warn = format!( - "{} {}: {}", - emoji::WARN, - style("[WARN]").bold().dim(), - message - ); - self.message(&warn); + if !self.quiet() && self.is_log_enabled(LogLevel::Warn) { + let warn = format!( + "{} {}: {}", + emoji::WARN, + style("[WARN]").bold().dim(), + message + ); + self.message(&warn); + } } /// Add an error message. pub fn error(&self, message: &str) { - let err = format!( - "{} {}: {}", - emoji::ERROR, - style("[ERR]").bold().dim(), - message - ); - self.message(&err); + if self.is_log_enabled(LogLevel::Error) { + let err = format!( + "{} {}: {}", + emoji::ERROR, + style("[ERR]").bold().dim(), + message + ); + self.message(&err); + } } } impl Default for ProgressOutput { fn default() -> Self { - ProgressOutput + ProgressOutput::new() } } diff --git a/src/test/mod.rs b/src/test/mod.rs index 8edecf0..513e8fa 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -2,6 +2,7 @@ pub mod webdriver; +use crate::PBAR; use child; use failure::{self, ResultExt}; use std::ffi::OsStr; @@ -22,13 +23,22 @@ where V: AsRef, { let mut cmd = Command::new("cargo"); + cmd.envs(envs); cmd.current_dir(path).arg("test"); + + if PBAR.quiet() { + cmd.arg("--quiet"); + } + if release { cmd.arg("--release"); } + cmd.arg("--target").arg("wasm32-unknown-unknown"); + cmd.args(extra_options); + child::run(cmd, "cargo test").context("Running Wasm tests with wasm-bindgen-test failed")?; // NB: `child::run` took care of ensuring that test output gets printed. diff --git a/tests/all/log_level.rs b/tests/all/log_level.rs new file mode 100644 index 0000000..ea129a0 --- /dev/null +++ b/tests/all/log_level.rs @@ -0,0 +1,91 @@ +use assert_cmd::prelude::*; +use predicates::boolean::PredicateBooleanExt; +use predicates::prelude::predicate::str::contains; +use predicates::reflection::PredicateReflection; +use predicates::Predicate; +use utils; + +fn matches_info() -> impl Predicate + PredicateReflection { + contains("[INFO]: Checking for the Wasm target...") + .and(contains("[INFO]: Compiling to Wasm...")) + .and(contains("[INFO]: License key is set in Cargo.toml but no LICENSE file(s) were found; Please add the LICENSE file(s) to your project directory")) + .and(contains("[INFO]: Installing wasm-bindgen...")) + .and(contains("[INFO]: Optimizing wasm binaries with `wasm-opt`...")) + .and(contains("[INFO]: :-) Done in ")) + .and(contains("[INFO]: :-) Your wasm pkg is ready to publish at ")) +} + +fn matches_warn() -> impl Predicate + PredicateReflection { + contains(":-) [WARN]: origin crate has no README") +} + +fn matches_cargo() -> impl Predicate + PredicateReflection { + contains("Finished release [optimized] target(s) in ") +} + +#[test] +fn quiet() { + utils::fixture::Fixture::new() + .cargo_toml("js-hello-world") + .hello_world_src_lib() + .wasm_pack() + .arg("--quiet") + .arg("build") + .assert() + .success() + .stdout("") + .stderr(""); +} + +#[test] +fn log_level_info() { + utils::fixture::Fixture::new() + .cargo_toml("js-hello-world") + .hello_world_src_lib() + .wasm_pack() + .arg("--log-level") + .arg("info") + .arg("build") + .assert() + .success() + .stdout("") + .stderr(matches_cargo().and(matches_warn()).and(matches_info())); +} + +#[test] +fn log_level_warn() { + utils::fixture::Fixture::new() + .cargo_toml("js-hello-world") + .hello_world_src_lib() + .wasm_pack() + .arg("--log-level") + .arg("warn") + .arg("build") + .assert() + .success() + .stdout("") + .stderr( + matches_cargo() + .and(matches_warn()) + .and(matches_info().not()), + ); +} + +#[test] +fn log_level_error() { + utils::fixture::Fixture::new() + .cargo_toml("js-hello-world") + .hello_world_src_lib() + .wasm_pack() + .arg("--log-level") + .arg("error") + .arg("build") + .assert() + .success() + .stdout("") + .stderr( + matches_cargo() + .and(matches_warn().not()) + .and(matches_info().not()), + ); +} diff --git a/tests/all/main.rs b/tests/all/main.rs index ac2b270..0a306ab 100644 --- a/tests/all/main.rs +++ b/tests/all/main.rs @@ -18,6 +18,7 @@ mod download; mod generate; mod license; mod lockfile; +mod log_level; mod manifest; mod readme; mod stamps;