Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions crates/cli-flags/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ fn init_file_per_thread_logger(prefix: &'static str) {
}

wasmtime_option_group! {
#[env = "OPTIMIZE"]
pub struct OptimizeOptions {
/// Optimization level of generated code (0-2, s; default: 2)
#[serde(default)]
Expand Down Expand Up @@ -234,6 +235,7 @@ wasmtime_option_group! {
}

wasmtime_option_group! {
#[env = "CODEGEN"]
pub struct CodegenOptions {
/// Either `cranelift` or `winch`.
///
Expand Down Expand Up @@ -296,6 +298,7 @@ wasmtime_option_group! {
}

wasmtime_option_group! {
#[env = "DEBUG"]
pub struct DebugOptions {
/// Enable generation of DWARF debug information in compiled code.
pub debug_info: Option<bool>,
Expand Down Expand Up @@ -337,6 +340,7 @@ wasmtime_option_group! {
}

wasmtime_option_group! {
#[env = "WASM"]
pub struct WasmOptions {
/// Enable canonicalization of all NaN values.
pub nan_canonicalization: Option<bool>,
Expand Down Expand Up @@ -487,6 +491,7 @@ wasmtime_option_group! {
}

wasmtime_option_group! {
#[env = "WASI"]
pub struct WasiOptions {
/// Enable support for WASI CLI APIs, including filesystems, sockets, clocks, and random.
pub cli: Option<bool>,
Expand Down Expand Up @@ -587,6 +592,7 @@ wasmtime_option_group! {
}

wasmtime_option_group! {
#[env = "RECORD"]
pub struct RecordOptions {
/// Filename for the recorded execution trace (or empty string to skip writing a file).
pub path: Option<String>,
Expand Down Expand Up @@ -801,12 +807,12 @@ impl CommonOptions {
);
}
}
self.opts.configure_with(&self.opts_raw);
self.codegen.configure_with(&self.codegen_raw);
self.debug.configure_with(&self.debug_raw);
self.wasm.configure_with(&self.wasm_raw);
self.wasi.configure_with(&self.wasi_raw);
self.record.configure_with(&self.record_raw);
self.opts.configure_with(&self.opts_raw)?;
self.codegen.configure_with(&self.codegen_raw)?;
self.debug.configure_with(&self.debug_raw)?;
self.wasm.configure_with(&self.wasm_raw)?;
self.wasi.configure_with(&self.wasi_raw)?;
self.record.configure_with(&self.record_raw)?;
Ok(())
}

Expand Down
119 changes: 84 additions & 35 deletions crates/cli-flags/src/opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use std::num::NonZeroU32;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;
use wasmtime::error::Context;
use wasmtime::{Result, bail, format_err};

/// Characters which can be safely ignored while parsing numeric options to wasmtime
Expand All @@ -25,6 +26,7 @@ const IGNORED_NUMBER_CHARS: [char; 1] = ['_'];
#[macro_export]
macro_rules! wasmtime_option_group {
(
#[env = $env:tt]
$(#[$attr:meta])*
pub struct $opts:ident {
$(
Expand Down Expand Up @@ -74,6 +76,7 @@ macro_rules! wasmtime_option_group {
}

impl $crate::opt::WasmtimeOption for $option {
const ENV_PREFIX: &'static str = concat!("WASMTIME_", $env);
const OPTIONS: &'static [$crate::opt::OptionDesc<$option>] = &[
$(
$crate::opt::OptionDesc {
Expand Down Expand Up @@ -126,18 +129,24 @@ macro_rules! wasmtime_option_group {
}

impl $opts {
fn configure_with(&mut self, opts: &[$crate::opt::CommaSeparated<$option>]) {
for opt in opts.iter().flat_map(|o| o.0.iter()) {
match opt {
$(
$option::$opt(val) => {
$crate::opt::OptionContainer::push(&mut self.$opt, val.clone());
}
)+
$(
$option::$prefixed(key, val) => self.$prefixed.push((key.clone(), val.clone())),
)?
}
fn configure_with(&mut self, opts: &[$crate::opt::CommaSeparated<$option>]) -> Result<()> {
let env_opts = <$option as $crate::opt::WasmtimeOption>::parse_env()?;
for opt in env_opts.iter().chain(opts.iter().flat_map(|o| o.0.iter())) {
self.configure(opt);
}
Ok(())
}

fn configure(&mut self, opt: &$option) {
match opt {
$(
$option::$opt(val) => {
$crate::opt::OptionContainer::push(&mut self.$opt, val.clone());
}
)+
$(
$option::$prefixed(key, val) => self.$prefixed.push((key.clone(), val.clone())),
)?
}
}

Expand Down Expand Up @@ -251,6 +260,62 @@ where
std::process::exit(0);
}

T::parse_csv(&val).map(CommaSeparated).map_err(|e| {
Error::raw(
ErrorKind::InvalidValue,
format!("failed to parse -{arg_short} / --{arg_long} option: {e:?}\n"),
)
})
}
}

/// Helper trait used by `CommaSeparated` which contains a list of all options
/// supported by the option group.
pub trait WasmtimeOption: Sized + Send + Sync + Clone + 'static {
const OPTIONS: &'static [OptionDesc<Self>];
const ENV_PREFIX: &'static str;

/// Parse all environment variables that relate to this option, returning
/// all parsed variables as a list.
///
/// Returns an error if any environment variable has invalid syntax and/or
/// failed to parse.
fn parse_env() -> Result<Vec<Self>> {
let mut ret = Vec::new();
if let Some(val) = std::env::var_os(Self::ENV_PREFIX) {
let val = match val.to_str() {
Some(s) => s,
None => bail!("env var `{}` is not valid UTF-8", Self::ENV_PREFIX),
};
ret.extend(
Self::parse_csv(&val)
.with_context(|| format!("failed to parse env var `{}`", Self::ENV_PREFIX))?,
);
}

for option in Self::OPTIONS {
let key = match &option.name {
OptName::Name(s) => format!("{}_{}", Self::ENV_PREFIX, s.to_ascii_uppercase()),
OptName::Prefix(_) => continue,
};
let val = match std::env::var_os(&key) {
Some(val) => val,
None => continue,
};
let val = match val.to_str() {
Some(s) => s,
None => bail!("env var `{key}` is not valid UTF-8"),
};
ret.push(
(option.parse)(&key, Some(val))
.with_context(|| format!("failed to parse env var `{key}`"))?,
);
}
Ok(ret)
}

/// Parses `val` as a comma-separated list of values for `Self::OPTIONS`.
fn parse_csv(val: &str) -> Result<Vec<Self>> {
let mut result = Vec::new();
for val in val.split(',') {
// Split `k=v` into `k` and `v` where `v` is optional
Expand All @@ -259,7 +324,7 @@ where
let key_val = iter.next();

// Find `key` within `T::OPTIONS`
let option = options
let option = Self::OPTIONS
.iter()
.filter_map(|d| match d.name {
OptName::Name(s) => {
Expand All @@ -275,34 +340,18 @@ where

let (desc, key) = match option {
Some(pair) => pair,
None => {
let err = Error::raw(
ErrorKind::InvalidValue,
format!("unknown -{arg_short} / --{arg_long} option: {key}\n"),
);
return Err(err.with_cmd(cmd));
}
None => bail!("unknown option: {key}\n"),
};

result.push((desc.parse)(&key, key_val).map_err(|e| {
Error::raw(
ErrorKind::InvalidValue,
format!("failed to parse -{arg_short} option `{val}`: {e:?}\n"),
)
.with_cmd(cmd)
})?)
result.push(
(desc.parse)(&key, key_val)
.with_context(|| format!("failed to parse option `{val}`"))?,
);
}

Ok(CommaSeparated(result))
Ok(result)
}
}

/// Helper trait used by `CommaSeparated` which contains a list of all options
/// supported by the option group.
pub trait WasmtimeOption: Sized + Send + Sync + Clone + 'static {
const OPTIONS: &'static [OptionDesc<Self>];
}

pub struct OptionDesc<T> {
pub name: OptName,
pub docs: &'static str,
Expand Down
46 changes: 46 additions & 0 deletions docs/cli-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ For example, adding `--optimize opt-level=0` to a `wasmtime compile` subcommand
will turn off most optimizations for the generated code.

## CLI options using TOML file

Most key-value options that can be provided using the `--optimize`, `--codegen`,
`--debug`, `--wasm`, and `--wasi` flags can also be provided using a TOML
file using the `--config <FILE>` cli flag, by putting the key-value inside a TOML
Expand All @@ -368,3 +369,48 @@ key-value pairs as you want in the TOML file.
Options on the CLI take precedent over options specified in a configuration
file, meaning they're allowed to shadow configuration values in a TOML
configuration file.

## CLI options using environment variables

Shared CLI options are additionally supported when passed in environment
variables. Each option can be individually specified as an environment variable
or as a group of options. For example

```console
wasmtime compile --optimize opt-level=0
```

is equivalent to

```console
export WASMTIME_OPTIMIZE_OPT_LEVEL=0
wasmtime compile
```

is equivalent to

```console
export WASMTIME_OPTIMIZE=opt-level=0
wasmtime compile
```

Specific environment variables override more general ones, so for example an
optimization level of 1 is used in this case:

```console
export WASMTIME_OPTIMIZE=opt-level=0
export WASMTIME_OPTIMIZE_OPT_LEVEL=1
wasmtime compile
```

Additionally command-line parameters override what's specified in environment
variables, additionally using the optimization level of 1 in this case:

```console
export WASMTIME_OPTIMIZE_OPT_LEVEL=0
wasmtime compile --optimize opt-level=1
```

Environment variables names are "WASMTIME_" followed by the group name, such as
"OPTIMIZE", followed by specific options. The syntax for option values in
environment variables is the same for those specified in arguments.
Loading
Loading