Skip to content

Commit

Permalink
Added a logging abstraction and cleaned up the main argument parsing …
Browse files Browse the repository at this point in the history
…logic
  • Loading branch information
Michael-F-Bryan committed Jun 14, 2023
1 parent e7e7419 commit eee6765
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 147 deletions.
211 changes: 89 additions & 122 deletions lib/cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,65 @@ use crate::commands::{
#[cfg(feature = "static-artifact-create")]
use crate::commands::{CreateObj, GenCHeader};
use crate::error::PrettyError;
use clap::{error::ErrorKind, CommandFactory, Parser};
use clap::{CommandFactory, Parser};

/// The main function for the Wasmer CLI tool.
pub fn wasmer_main() {
// We allow windows to print properly colors
#[cfg(windows)]
colored::control::set_virtual_terminal(true).unwrap();

PrettyError::report(wasmer_main_inner())
}

fn wasmer_main_inner() -> Result<(), anyhow::Error> {
if is_binfmt_interpreter() {
todo!();
}

let args = Args::parse();

if args.version {
return print_version(args.output.is_verbose());
}

args.execute()
}

/// Command-line arguments for the Wasmer CLI.
#[derive(Parser, Debug)]
#[clap(about, author)]
#[cfg_attr(feature = "headless", clap(name = "wasmer-headless"))]
#[cfg_attr(not(feature = "headless"), clap(name = "wasmer-headless"))]
pub struct Args {
/// Print version info and exit.
#[clap(short = 'V', long)]
version: bool,
#[clap(flatten)]
output: crate::logging::Output,
#[clap(subcommand)]
cmd: Option<Cmd>,
}

impl Args {
fn execute(self) -> Result<(), anyhow::Error> {
if self.version {
return print_version(self.output.is_verbose());
}

if let Some(cmd) = self.cmd {
cmd.execute()
} else {
let help = Args::command().render_long_help();
eprintln!("{help}");
Ok(())
}
}
}

#[derive(Parser, Debug)]
#[cfg_attr(
not(feature = "headless"),
clap(
name = "wasmer",
about = concat!("wasmer ", env!("CARGO_PKG_VERSION")),
version,
author
)
)]
#[cfg_attr(
feature = "headless",
clap(
name = "wasmer-headless",
about = concat!("wasmer ", env!("CARGO_PKG_VERSION")),
version,
author
)
)]
/// The options for the wasmer Command Line Interface
enum WasmerCLIOptions {
enum Cmd {
/// Login into a wapm.io-like registry
Login(Login),

Expand Down Expand Up @@ -155,7 +191,7 @@ enum WasmerCLIOptions {
/// Add a WAPM package's bindings to your application.
Add(Add),

/// (unstable) Run a WebAssembly file or WEBC container.
/// Run a WebAssembly file or WEBC container.
#[clap(alias = "run-unstable")]
Run(Run),

Expand All @@ -170,7 +206,7 @@ enum WasmerCLIOptions {
Namespace(wasmer_deploy_cli::cmd::namespace::CmdNamespace),
}

impl WasmerCLIOptions {
impl Cmd {
fn execute(self) -> Result<(), anyhow::Error> {
use wasmer_deploy_cli::cmd::CliCommand;

Expand Down Expand Up @@ -208,114 +244,45 @@ impl WasmerCLIOptions {
}
}

/// The main function for the Wasmer CLI tool.
pub fn wasmer_main() {
// We allow windows to print properly colors
#[cfg(windows)]
colored::control::set_virtual_terminal(true).unwrap();

PrettyError::report(wasmer_main_inner())
}

fn wasmer_main_inner() -> Result<(), anyhow::Error> {
crate::logging::set_up_logging();

// We try to run wasmer with the normal arguments.
// Eg. `wasmer <SUBCOMMAND>`
// In case that fails, we fallback trying the Run subcommand directly.
// Eg. `wasmer myfile.wasm --dir=.`
//
// In case we've been run as wasmer-binfmt-interpreter myfile.wasm args,
// we assume that we're registered via binfmt_misc
let args = std::env::args().collect::<Vec<_>>();
let binpath = args.get(0).map(|s| s.as_ref()).unwrap_or("");

let firstarg = args.get(1).map(|s| s.as_str());
let secondarg = args.get(2).map(|s| s.as_str());

match (firstarg, secondarg) {
(None, _) | (Some("help"), _) | (Some("--help"), _) => {
return print_help(true);
}
(Some("-h"), _) => {
return print_help(false);
}
(Some("-vV"), _)
| (Some("version"), Some("--verbose"))
| (Some("--version"), Some("--verbose")) => {
return print_version(true);
}

(Some("-v"), _) | (Some("-V"), _) | (Some("version"), _) | (Some("--version"), _) => {
return print_version(false);
}
_ => {}
fn is_binfmt_interpreter() -> bool {
if !cfg!(linux) {
return false;
}

let command = args.get(1);
let options = if cfg!(target_os = "linux") && binpath.ends_with("wasmer-binfmt-interpreter") {
WasmerCLIOptions::Run(Run::from_binfmt_args())
} else {
match command.unwrap_or(&String::new()).as_ref() {
"add" | "cache" | "compile" | "config" | "create-obj" | "create-exe" | "help"
| "gen-c-header" | "inspect" | "init" | "run" | "run-unstable" | "self-update"
| "validate" | "wast" | "binfmt" | "login" | "publish" | "app" | "namespace" | "" => {
WasmerCLIOptions::parse()
}
_ => {
WasmerCLIOptions::try_parse_from(args.iter()).unwrap_or_else(|e| {
match e.kind() {
// This fixes a issue that:
// 1. Shows the version twice when doing `wasmer -V`
// 2. Shows the run help (instead of normal help) when doing `wasmer --help`
ErrorKind::DisplayVersion | ErrorKind::DisplayHelp => e.exit(),
_ => WasmerCLIOptions::Run(Run::parse()),
}
})
}
}
};

options.execute()
}

fn print_help(verbose: bool) -> Result<(), anyhow::Error> {
let mut cmd = WasmerCLIOptions::command();
if verbose {
let _ = cmd.print_long_help();
} else {
let _ = cmd.print_help();
}
Ok(())
// TODO: Figure out if we actually are running as the binfmt interpreter
false
}

#[allow(unused_mut, clippy::vec_init_then_push)]
fn print_version(verbose: bool) -> Result<(), anyhow::Error> {
if !verbose {
println!("wasmer {}", env!("CARGO_PKG_VERSION"));
} else {
println!(
"wasmer {} ({} {})",
env!("CARGO_PKG_VERSION"),
env!("WASMER_BUILD_GIT_HASH_SHORT"),
env!("WASMER_BUILD_DATE")
);
println!("binary: {}", env!("CARGO_PKG_NAME"));
println!("commit-hash: {}", env!("WASMER_BUILD_GIT_HASH"));
println!("commit-date: {}", env!("WASMER_BUILD_DATE"));
println!("host: {}", target_lexicon::HOST);
println!("compiler: {}", {
let mut s = Vec::<&'static str>::new();

#[cfg(feature = "singlepass")]
s.push("singlepass");
#[cfg(feature = "cranelift")]
s.push("cranelift");
#[cfg(feature = "llvm")]
s.push("llvm");

s.join(",")
});
return Ok(());
}

println!(
"wasmer {} ({} {})",
env!("CARGO_PKG_VERSION"),
env!("WASMER_BUILD_GIT_HASH_SHORT"),
env!("WASMER_BUILD_DATE")
);
println!("binary: {}", env!("CARGO_PKG_NAME"));
println!("commit-hash: {}", env!("WASMER_BUILD_GIT_HASH"));
println!("commit-date: {}", env!("WASMER_BUILD_DATE"));
println!("host: {}", target_lexicon::HOST);

println!("compiler: {}", {
let mut s = Vec::<&'static str>::new();

#[cfg(feature = "singlepass")]
s.push("singlepass");
#[cfg(feature = "cranelift")]
s.push("cranelift");
#[cfg(feature = "llvm")]
s.push("llvm");

s.join(",")
});

Ok(())
}
114 changes: 89 additions & 25 deletions lib/cli/src/logging.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,96 @@
//! Logging functions for the debug feature.
use tracing::level_filters::LevelFilter;
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};

/// Initialize logging based on the `$RUST_LOG` environment variable. Logs will
/// be disabled when `$RUST_LOG` isn't set.
pub fn set_up_logging() {
let fmt_layer = fmt::layer()
.with_target(true)
.with_span_events(fmt::format::FmtSpan::CLOSE)
.with_ansi(should_emit_colors())
.with_thread_ids(true)
.with_writer(std::io::stderr)
.compact();

let filter_layer = EnvFilter::builder().from_env_lossy();

tracing_subscriber::registry()
.with(filter_layer)
.with(fmt_layer)
.init();
const WHITELISTED_LOG_TARGETS: &[&str] = &["wasmer", "wasmer_wasix", "virtual_fs"];

/// Control the output generated by the CLI.
#[derive(Debug, Clone, PartialEq, clap::Parser)]
pub struct Output {
/// Generate verbose output (repeat for more verbosity)
#[clap(short, long, action = clap::ArgAction::Count, global = true, conflicts_with = "quiet")]
pub verbose: usize,
/// Do not print progress messages.
#[clap(short, long, global = true, conflicts_with = "verbose")]
pub quiet: bool,
/// When to display colored output.
#[clap(long, default_value_t = clap::ColorChoice::Auto, global = true)]
pub color: clap::ColorChoice,
}

/// Check whether we should emit ANSI escape codes for log formatting.
///
/// The `tracing-subscriber` crate doesn't have native support for
/// "--color=always|never|auto", so we implement a poor man's version.
///
/// For more, see https://github.com/tokio-rs/tracing/issues/2388
fn should_emit_colors() -> bool {
isatty::stderr_isatty() && std::env::var_os("NO_COLOR").is_none()
impl Output {
/// Has the `--verbose` flag been set?
pub fn is_verbose(&self) -> bool {
self.verbose > 0
}

/// Initialize logging based on the `$RUST_LOG` environment variable and
/// command-line flags.
pub fn initialize_logging(&self) {
let fmt_layer = fmt::layer()
.with_target(true)
.with_span_events(fmt::format::FmtSpan::CLOSE)
.with_ansi(self.should_emit_colors())
.with_thread_ids(true)
.with_writer(std::io::stderr)
.compact();

let filter_layer = self.log_filter();

tracing_subscriber::registry()
.with(filter_layer)
.with(fmt_layer)
.init();
}

fn log_filter(&self) -> EnvFilter {
let default_filters = [
LevelFilter::OFF,
LevelFilter::WARN,
LevelFilter::INFO,
LevelFilter::DEBUG,
];

// First, we set up the default log level.
let default_level = default_filters
.get(self.verbose)
.copied()
.unwrap_or(LevelFilter::TRACE);
let mut filter = EnvFilter::builder()
.with_default_directive(default_level.into())
.from_env_lossy();

// Next we add level-specific directives, where verbosity=0 means don't
// override anything. Note that these are shifted one level up so we'll
// get something like RUST_LOG="warn,wasmer_wasix=info"
let specific_filters = [LevelFilter::WARN, LevelFilter::INFO, LevelFilter::DEBUG];
if self.verbose > 0 {
let level = specific_filters
.get(self.verbose)
.copied()
.unwrap_or(LevelFilter::TRACE);

for target in WHITELISTED_LOG_TARGETS {
let directive = format!("{target}={level}").parse().unwrap();
filter = filter.add_directive(directive);
}
}

filter
}

/// Check whether we should emit ANSI escape codes for log formatting.
///
/// The `tracing-subscriber` crate doesn't have native support for
/// "--color=always|never|auto", so we implement a poor man's version.
///
/// For more, see https://github.com/tokio-rs/tracing/issues/2388
fn should_emit_colors(&self) -> bool {
match self.color {
clap::ColorChoice::Auto => isatty::stderr_isatty(),
clap::ColorChoice::Always => true,
clap::ColorChoice::Never => false,
}
}
}

0 comments on commit eee6765

Please sign in to comment.