Use structopt instead of docopt.
This commit refactors the Wasmtime CLI tools to use `structopt` instead of `docopt`. The `wasmtime` tool now has the following subcommands: * `config new` - creates a new Wasmtime configuration file. * `run` - runs a WebAssembly module. * `wasm2obj` - translates a Wasm module to native object file. * `wast` - runs a test script file. If no subcommand is specified, the `run` subcommand is used. Thus, `wasmtime foo.wasm` should continue to function as expected. The `wasm2obj` and `wast` tools still exist, but delegate to the same implementation as the `wasmtime` subcommands. The standalone `wasm2obj` and `wast` tools may be removed in the future in favor of simply using `wasmtime`. Included in this commit is a breaking change to the default Wasmtime configuration file: it has been renamed from `wasmtime-cache-config.toml` to simply `config.toml`. The new name is less specific which will allow for additional (non-cache-related) settings in the future. There are some breaking changes to improve command line UX: * The `--cache-config` option has been renamed to `--config`. * The `--create-config-file` option has moved to the `config new` subcommand. As a result, the `wasm2obj` and `wast` tools cannot be used to create a new config file. * The short form of the `--optimize` option has changed from `-o` to `-O` for consistency. * The `wasm2obj` command takes the output object file as a required positional argument rather than the former required output *option* (e.g. `wasmtime wasm2obj foo.wasm foo.obj`).
This commit is contained in:
49
src/commands/config.rs
Normal file
49
src/commands/config.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
//! The module that implements the `wasmtime config` command.
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use structopt::StructOpt;
|
||||
use wasmtime_environ::cache_create_new_config;
|
||||
|
||||
const CONFIG_NEW_AFTER_HELP: &str =
|
||||
"If no file path is specified, the system configuration file path will be used.";
|
||||
|
||||
/// Controls Wasmtime configuration settings
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(name = "run")]
|
||||
pub enum ConfigCommand {
|
||||
/// Creates a new Wasmtime configuration file
|
||||
#[structopt(after_help = CONFIG_NEW_AFTER_HELP)]
|
||||
New(ConfigNewCommand),
|
||||
}
|
||||
|
||||
impl ConfigCommand {
|
||||
/// Executes the command.
|
||||
pub fn execute(&self) -> Result<()> {
|
||||
match self {
|
||||
Self::New(c) => c.execute(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new Wasmtime configuration file
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(name = "new", after_help = CONFIG_NEW_AFTER_HELP)]
|
||||
pub struct ConfigNewCommand {
|
||||
/// The path of the new configuration file
|
||||
#[structopt(index = 1, value_name = "FILE_PATH")]
|
||||
path: Option<String>,
|
||||
}
|
||||
|
||||
impl ConfigNewCommand {
|
||||
/// Executes the command.
|
||||
pub fn execute(&self) -> Result<()> {
|
||||
let path = cache_create_new_config(self.path.as_ref()).map_err(|e| anyhow!(e))?;
|
||||
|
||||
println!(
|
||||
"Successfully created a new configuation file at '{}'.",
|
||||
path.display()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
372
src/commands/run.rs
Normal file
372
src/commands/run.rs
Normal file
@@ -0,0 +1,372 @@
|
||||
//! The module that implements the `wasmtime run` command.
|
||||
|
||||
use crate::{init_file_per_thread_logger, pick_compilation_strategy, CommonOptions};
|
||||
use anyhow::{bail, Context as _, Result};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ffi::{OsStr, OsString},
|
||||
fmt::Write,
|
||||
fs::File,
|
||||
path::{Component, Path, PathBuf},
|
||||
};
|
||||
use structopt::{clap::AppSettings, StructOpt};
|
||||
use wasi_common::preopen_dir;
|
||||
use wasmtime::{Config, Engine, HostRef, Instance, Module, Store};
|
||||
use wasmtime_environ::cache_init;
|
||||
use wasmtime_interface_types::ModuleData;
|
||||
use wasmtime_wasi::{
|
||||
create_wasi_instance, old::snapshot_0::create_wasi_instance as create_wasi_instance_snapshot_0,
|
||||
};
|
||||
#[cfg(feature = "wasi-c")]
|
||||
use wasmtime_wasi_c::instantiate_wasi_c;
|
||||
#[cfg(feature = "wasi-c")]
|
||||
use wasmtime_wasi_c::instantiate_wasi_c;
|
||||
|
||||
fn parse_module(s: &OsStr) -> Result<PathBuf, OsString> {
|
||||
// Do not accept wasmtime subcommand names as the module name
|
||||
match s.to_str() {
|
||||
Some("help") | Some("config") | Some("run") | Some("wasm2obj") | Some("wast") => {
|
||||
Err("module name cannot be the same as a subcommand".into())
|
||||
}
|
||||
_ => Ok(s.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_env_var(s: &str) -> Result<(String, String)> {
|
||||
let parts: Vec<_> = s.splitn(2, '=').collect();
|
||||
if parts.len() != 2 {
|
||||
bail!("must be of the form `key=value`");
|
||||
}
|
||||
Ok((parts[0].to_owned(), parts[1].to_owned()))
|
||||
}
|
||||
|
||||
fn parse_map_dirs(s: &str) -> Result<(String, String)> {
|
||||
let parts: Vec<&str> = s.split("::").collect();
|
||||
if parts.len() != 2 {
|
||||
bail!("must contain exactly one double colon ('::')");
|
||||
}
|
||||
Ok((parts[0].into(), parts[1].into()))
|
||||
}
|
||||
|
||||
/// Runs a WebAssembly module
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(name = "run", setting = AppSettings::TrailingVarArg)]
|
||||
pub struct RunCommand {
|
||||
#[structopt(flatten)]
|
||||
common: CommonOptions,
|
||||
|
||||
/// Grant access to the given host directory
|
||||
#[structopt(long = "dir", number_of_values = 1, value_name = "DIRECTORY")]
|
||||
dirs: Vec<String>,
|
||||
|
||||
/// Pass an environment variable to the program
|
||||
#[structopt(long = "env", number_of_values = 1, value_name = "NAME=VAL", parse(try_from_str = parse_env_var))]
|
||||
vars: Vec<(String, String)>,
|
||||
|
||||
/// The name of the function to run
|
||||
#[structopt(long, value_name = "FUNCTION")]
|
||||
invoke: Option<String>,
|
||||
|
||||
/// Grant access to a guest directory mapped as a host directory
|
||||
#[structopt(long = "mapdir", number_of_values = 1, value_name = "GUEST_DIR::HOST_DIR", parse(try_from_str = parse_map_dirs))]
|
||||
map_dirs: Vec<(String, String)>,
|
||||
|
||||
/// The path of the WebAssembly module to run
|
||||
#[structopt(
|
||||
index = 1,
|
||||
required = true,
|
||||
value_name = "WASM_MODULE",
|
||||
parse(try_from_os_str = parse_module),
|
||||
)]
|
||||
module: PathBuf,
|
||||
|
||||
/// Load the given WebAssembly module before the main module
|
||||
#[structopt(
|
||||
long = "preload",
|
||||
number_of_values = 1,
|
||||
value_name = "MODULE_PATH",
|
||||
parse(from_os_str)
|
||||
)]
|
||||
preloads: Vec<PathBuf>,
|
||||
|
||||
/// Enable the wasi-c implementation of `wasi_unstable`
|
||||
#[structopt(long = "wasi-c")]
|
||||
enable_wasi_c: bool,
|
||||
|
||||
// NOTE: this must come last for trailing varargs
|
||||
/// The arguments to pass to the module
|
||||
#[structopt(value_name = "ARGS")]
|
||||
module_args: Vec<String>,
|
||||
}
|
||||
|
||||
impl RunCommand {
|
||||
/// Executes the command.
|
||||
pub fn execute(&self) -> Result<()> {
|
||||
let log_config = if self.common.debug {
|
||||
pretty_env_logger::init();
|
||||
None
|
||||
} else {
|
||||
let prefix = "wasmtime.dbg.";
|
||||
init_file_per_thread_logger(prefix);
|
||||
Some(prefix)
|
||||
};
|
||||
|
||||
let errors = cache_init(
|
||||
!self.common.disable_cache,
|
||||
self.common.config.as_ref(),
|
||||
log_config,
|
||||
);
|
||||
|
||||
if !errors.is_empty() {
|
||||
let mut message = String::new();
|
||||
writeln!(message, "Cache initialization failed. Errors:")?;
|
||||
for e in errors {
|
||||
writeln!(message, " -> {}", e)?;
|
||||
}
|
||||
bail!(message);
|
||||
}
|
||||
|
||||
let mut config = Config::new();
|
||||
config
|
||||
.cranelift_debug_verifier(cfg!(debug_assertions))
|
||||
.debug_info(self.common.debug_info)
|
||||
.wasm_simd(self.common.enable_simd)
|
||||
.strategy(pick_compilation_strategy(
|
||||
self.common.cranelift,
|
||||
self.common.lightbeam,
|
||||
)?)?;
|
||||
|
||||
if self.common.optimize {
|
||||
config.cranelift_opt_level(wasmtime::OptLevel::Speed);
|
||||
}
|
||||
|
||||
let engine = Engine::new(&config);
|
||||
let store = HostRef::new(Store::new(&engine));
|
||||
let mut module_registry = HashMap::new();
|
||||
|
||||
// Make wasi available by default.
|
||||
let preopen_dirs = self.compute_preopen_dirs()?;
|
||||
let argv = self.compute_argv();
|
||||
|
||||
let wasi_unstable = HostRef::new(if self.enable_wasi_c {
|
||||
#[cfg(feature = "wasi-c")]
|
||||
{
|
||||
let global_exports = store.borrow().global_exports().clone();
|
||||
let handle = instantiate_wasi_c(global_exports, &preopen_dirs, &argv, &self.vars)?;
|
||||
Instance::from_handle(&store, handle)
|
||||
}
|
||||
#[cfg(not(feature = "wasi-c"))]
|
||||
{
|
||||
bail!("wasi-c feature not enabled at build time")
|
||||
}
|
||||
} else {
|
||||
create_wasi_instance_snapshot_0(&store, &preopen_dirs, &argv, &self.vars)?
|
||||
});
|
||||
|
||||
let wasi_snapshot_preview1 = HostRef::new(create_wasi_instance(
|
||||
&store,
|
||||
&preopen_dirs,
|
||||
&argv,
|
||||
&self.vars,
|
||||
)?);
|
||||
|
||||
module_registry.insert("wasi_unstable".to_owned(), wasi_unstable);
|
||||
module_registry.insert("wasi_snapshot_preview1".to_owned(), wasi_snapshot_preview1);
|
||||
|
||||
// Load the preload wasm modules.
|
||||
for preload in self.preloads.iter() {
|
||||
Self::instantiate_module(&store, &module_registry, preload)
|
||||
.with_context(|| format!("failed to process preload at `{}`", preload.display()))?;
|
||||
}
|
||||
|
||||
// Load the main wasm module.
|
||||
self.handle_module(&store, &module_registry)
|
||||
.with_context(|| format!("failed to run main module `{}`", self.module.display()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_preopen_dirs(&self) -> Result<Vec<(String, File)>> {
|
||||
let mut preopen_dirs = Vec::new();
|
||||
|
||||
for dir in self.dirs.iter() {
|
||||
preopen_dirs.push((
|
||||
dir.clone(),
|
||||
preopen_dir(dir).with_context(|| format!("failed to open directory '{}'", dir))?,
|
||||
));
|
||||
}
|
||||
|
||||
for (guest, host) in self.map_dirs.iter() {
|
||||
preopen_dirs.push((
|
||||
guest.clone(),
|
||||
preopen_dir(host)
|
||||
.with_context(|| format!("failed to open directory '{}'", host))?,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(preopen_dirs)
|
||||
}
|
||||
|
||||
fn compute_argv(&self) -> Vec<String> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
// Add argv[0], which is the program name. Only include the base name of the
|
||||
// main wasm module, to avoid leaking path information.
|
||||
result.push(
|
||||
self.module
|
||||
.components()
|
||||
.next_back()
|
||||
.map(Component::as_os_str)
|
||||
.and_then(OsStr::to_str)
|
||||
.unwrap_or("")
|
||||
.to_owned(),
|
||||
);
|
||||
|
||||
// Add the remaining arguments.
|
||||
for arg in self.module_args.iter() {
|
||||
result.push(arg.clone());
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn instantiate_module(
|
||||
store: &HostRef<Store>,
|
||||
module_registry: &HashMap<String, HostRef<Instance>>,
|
||||
path: &Path,
|
||||
) -> Result<(HostRef<Instance>, HostRef<Module>, Vec<u8>)> {
|
||||
// Read the wasm module binary either as `*.wat` or a raw binary
|
||||
let data = wat::parse_file(path.to_path_buf())?;
|
||||
|
||||
let module = HostRef::new(Module::new(store, &data)?);
|
||||
|
||||
// Resolve import using module_registry.
|
||||
let imports = module
|
||||
.borrow()
|
||||
.imports()
|
||||
.iter()
|
||||
.map(|i| {
|
||||
let module_name = i.module();
|
||||
if let Some(instance) = module_registry.get(module_name) {
|
||||
let field_name = i.name();
|
||||
if let Some(export) = instance.borrow().find_export_by_name(field_name) {
|
||||
Ok(export.clone())
|
||||
} else {
|
||||
bail!(
|
||||
"Import {} was not found in module {}",
|
||||
field_name,
|
||||
module_name
|
||||
)
|
||||
}
|
||||
} else {
|
||||
bail!("Import module {} was not found", module_name)
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let instance = HostRef::new(match Instance::new(store, &module, &imports) {
|
||||
Ok(instance) => instance,
|
||||
Err(trap) => bail!("Failed to instantiate {:?}: {:?}", path, trap),
|
||||
});
|
||||
|
||||
Ok((instance, module, data))
|
||||
}
|
||||
|
||||
fn handle_module(
|
||||
&self,
|
||||
store: &HostRef<Store>,
|
||||
module_registry: &HashMap<String, HostRef<Instance>>,
|
||||
) -> Result<()> {
|
||||
let (instance, module, data) =
|
||||
Self::instantiate_module(store, module_registry, &self.module)?;
|
||||
|
||||
// If a function to invoke was given, invoke it.
|
||||
if let Some(name) = self.invoke.as_ref() {
|
||||
let data = ModuleData::new(&data)?;
|
||||
self.invoke_export(instance, &data, name)?;
|
||||
} else if module
|
||||
.borrow()
|
||||
.exports()
|
||||
.iter()
|
||||
.any(|export| export.name().is_empty())
|
||||
{
|
||||
// Launch the default command export.
|
||||
let data = ModuleData::new(&data)?;
|
||||
self.invoke_export(instance, &data, "")?;
|
||||
} else {
|
||||
// If the module doesn't have a default command export, launch the
|
||||
// _start function if one is present, as a compatibility measure.
|
||||
let data = ModuleData::new(&data)?;
|
||||
self.invoke_export(instance, &data, "_start")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn invoke_export(
|
||||
&self,
|
||||
instance: HostRef<Instance>,
|
||||
data: &ModuleData,
|
||||
name: &str,
|
||||
) -> Result<()> {
|
||||
use wasm_webidl_bindings::ast;
|
||||
use wasmtime_interface_types::Value;
|
||||
|
||||
let mut handle = instance.borrow().handle().clone();
|
||||
|
||||
// Use the binding information in `ModuleData` to figure out what arguments
|
||||
// need to be passed to the function that we're invoking. Currently we take
|
||||
// the CLI parameters and attempt to parse them into function arguments for
|
||||
// the function we'll invoke.
|
||||
let binding = data.binding_for_export(&mut handle, name)?;
|
||||
if !binding.param_types()?.is_empty() {
|
||||
eprintln!(
|
||||
"warning: using `--invoke` with a function that takes arguments \
|
||||
is experimental and may break in the future"
|
||||
);
|
||||
}
|
||||
let mut args = self.module_args.iter();
|
||||
let mut values = Vec::new();
|
||||
for ty in binding.param_types()? {
|
||||
let val = match args.next() {
|
||||
Some(s) => s,
|
||||
None => bail!("not enough arguments for `{}`", name),
|
||||
};
|
||||
values.push(match ty {
|
||||
// TODO: integer parsing here should handle hexadecimal notation
|
||||
// like `0x0...`, but the Rust standard library currently only
|
||||
// parses base-10 representations.
|
||||
ast::WebidlScalarType::Long => Value::I32(val.parse()?),
|
||||
ast::WebidlScalarType::LongLong => Value::I64(val.parse()?),
|
||||
ast::WebidlScalarType::UnsignedLong => Value::U32(val.parse()?),
|
||||
ast::WebidlScalarType::UnsignedLongLong => Value::U64(val.parse()?),
|
||||
|
||||
ast::WebidlScalarType::Float | ast::WebidlScalarType::UnrestrictedFloat => {
|
||||
Value::F32(val.parse()?)
|
||||
}
|
||||
ast::WebidlScalarType::Double | ast::WebidlScalarType::UnrestrictedDouble => {
|
||||
Value::F64(val.parse()?)
|
||||
}
|
||||
ast::WebidlScalarType::DomString => Value::String(val.to_string()),
|
||||
t => bail!("unsupported argument type {:?}", t),
|
||||
});
|
||||
}
|
||||
|
||||
// Invoke the function and then afterwards print all the results that came
|
||||
// out, if there are any.
|
||||
let results = data
|
||||
.invoke_export(&instance, name, &values)
|
||||
.with_context(|| format!("failed to invoke `{}`", name))?;
|
||||
if !results.is_empty() {
|
||||
eprintln!(
|
||||
"warning: using `--invoke` with a function that returns values \
|
||||
is experimental and may break in the future"
|
||||
);
|
||||
}
|
||||
for result in results {
|
||||
println!("{}", result);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
205
src/commands/wasm2obj.rs
Normal file
205
src/commands/wasm2obj.rs
Normal file
@@ -0,0 +1,205 @@
|
||||
//! The module that implements the `wasmtime wasm2obj` command.
|
||||
|
||||
use crate::{init_file_per_thread_logger, pick_compilation_strategy, CommonOptions};
|
||||
use anyhow::{anyhow, bail, Context as _, Result};
|
||||
use faerie::Artifact;
|
||||
use std::{
|
||||
fmt::Write,
|
||||
fs::File,
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
};
|
||||
use structopt::{clap::AppSettings, StructOpt};
|
||||
use target_lexicon::Triple;
|
||||
use wasmtime::Strategy;
|
||||
use wasmtime_debug::{emit_debugsections, read_debuginfo};
|
||||
#[cfg(feature = "lightbeam")]
|
||||
use wasmtime_environ::Lightbeam;
|
||||
use wasmtime_environ::{
|
||||
cache_init, entity::EntityRef, settings, settings::Configurable, wasm::DefinedMemoryIndex,
|
||||
Compiler, Cranelift, ModuleEnvironment, ModuleVmctxInfo, Tunables, VMOffsets,
|
||||
};
|
||||
use wasmtime_jit::native;
|
||||
use wasmtime_obj::emit_module;
|
||||
|
||||
/// The after help text for the `wasm2obj` command.
|
||||
pub const WASM2OBJ_AFTER_HELP: &str =
|
||||
"The translation is dependent on the environment chosen.\n\
|
||||
The default is a dummy environment that produces placeholder values.";
|
||||
|
||||
fn parse_target(s: &str) -> Result<Triple> {
|
||||
Triple::from_str(&s).map_err(|e| anyhow!(e))
|
||||
}
|
||||
|
||||
/// Translates a WebAssembly module to native object file
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(
|
||||
name = "wasm2obj",
|
||||
version = env!("CARGO_PKG_VERSION"),
|
||||
setting = AppSettings::ColoredHelp,
|
||||
after_help = WASM2OBJ_AFTER_HELP,
|
||||
)]
|
||||
pub struct WasmToObjCommand {
|
||||
#[structopt(flatten)]
|
||||
common: CommonOptions,
|
||||
|
||||
/// The path of the WebAssembly module to translate
|
||||
#[structopt(index = 1, value_name = "MODULE_PATH", parse(from_os_str))]
|
||||
module: PathBuf,
|
||||
|
||||
/// The path of the output object file
|
||||
#[structopt(index = 2, value_name = "OUTPUT_PATH")]
|
||||
output: String,
|
||||
|
||||
/// The target triple; default is the host triple
|
||||
#[structopt(long, value_name = "TARGET", parse(try_from_str = parse_target))]
|
||||
target: Option<Triple>,
|
||||
}
|
||||
|
||||
impl WasmToObjCommand {
|
||||
/// Executes the command.
|
||||
pub fn execute(&self) -> Result<()> {
|
||||
let log_config = if self.common.debug {
|
||||
pretty_env_logger::init();
|
||||
None
|
||||
} else {
|
||||
let prefix = "wasm2obj.dbg.";
|
||||
init_file_per_thread_logger(prefix);
|
||||
Some(prefix)
|
||||
};
|
||||
|
||||
let errors = cache_init(
|
||||
!self.common.disable_cache,
|
||||
self.common.config.as_ref(),
|
||||
log_config,
|
||||
);
|
||||
|
||||
if !errors.is_empty() {
|
||||
let mut message = String::new();
|
||||
writeln!(message, "Cache initialization failed. Errors:")?;
|
||||
for e in errors {
|
||||
writeln!(message, " -> {}", e)?;
|
||||
}
|
||||
bail!(message);
|
||||
}
|
||||
|
||||
self.handle_module()
|
||||
}
|
||||
|
||||
fn handle_module(&self) -> Result<()> {
|
||||
let strategy = pick_compilation_strategy(self.common.cranelift, self.common.lightbeam)?;
|
||||
|
||||
let data = wat::parse_file(&self.module).context("failed to parse module")?;
|
||||
|
||||
let isa_builder = match self.target.as_ref() {
|
||||
Some(target) => native::lookup(target.clone())?,
|
||||
None => native::builder(),
|
||||
};
|
||||
let mut flag_builder = settings::builder();
|
||||
|
||||
// There are two possible traps for division, and this way
|
||||
// we get the proper one if code traps.
|
||||
flag_builder.enable("avoid_div_traps").unwrap();
|
||||
|
||||
if self.common.enable_simd {
|
||||
flag_builder.enable("enable_simd").unwrap();
|
||||
}
|
||||
|
||||
if self.common.optimize {
|
||||
flag_builder.set("opt_level", "speed").unwrap();
|
||||
}
|
||||
|
||||
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
||||
|
||||
let mut obj = Artifact::new(isa.triple().clone(), self.output.clone());
|
||||
|
||||
// TODO: Expose the tunables as command-line flags.
|
||||
let tunables = Tunables::default();
|
||||
|
||||
let (
|
||||
module,
|
||||
module_translation,
|
||||
lazy_function_body_inputs,
|
||||
lazy_data_initializers,
|
||||
target_config,
|
||||
) = {
|
||||
let environ = ModuleEnvironment::new(isa.frontend_config(), tunables);
|
||||
|
||||
let translation = environ
|
||||
.translate(&data)
|
||||
.context("failed to translate module")?;
|
||||
|
||||
(
|
||||
translation.module,
|
||||
translation.module_translation.unwrap(),
|
||||
translation.function_body_inputs,
|
||||
translation.data_initializers,
|
||||
translation.target_config,
|
||||
)
|
||||
};
|
||||
|
||||
// TODO: use the traps information
|
||||
let (compilation, relocations, address_transform, value_ranges, stack_slots, _traps) =
|
||||
match strategy {
|
||||
Strategy::Auto | Strategy::Cranelift => Cranelift::compile_module(
|
||||
&module,
|
||||
&module_translation,
|
||||
lazy_function_body_inputs,
|
||||
&*isa,
|
||||
self.common.debug_info,
|
||||
),
|
||||
#[cfg(feature = "lightbeam")]
|
||||
Strategy::Lightbeam => Lightbeam::compile_module(
|
||||
&module,
|
||||
&module_translation,
|
||||
lazy_function_body_inputs,
|
||||
&*isa,
|
||||
generate_debug_info,
|
||||
),
|
||||
#[cfg(not(feature = "lightbeam"))]
|
||||
Strategy::Lightbeam => bail!("lightbeam support not enabled"),
|
||||
other => bail!("unsupported compilation strategy {:?}", other),
|
||||
}
|
||||
.context("failed to compile module")?;
|
||||
|
||||
let module_vmctx_info = {
|
||||
let ofs = VMOffsets::new(target_config.pointer_bytes(), &module);
|
||||
let memory_offset =
|
||||
ofs.vmctx_vmmemory_definition_base(DefinedMemoryIndex::new(0)) as i64;
|
||||
ModuleVmctxInfo {
|
||||
memory_offset,
|
||||
stack_slots,
|
||||
}
|
||||
};
|
||||
|
||||
emit_module(
|
||||
&mut obj,
|
||||
&module,
|
||||
&compilation,
|
||||
&relocations,
|
||||
&lazy_data_initializers,
|
||||
&target_config,
|
||||
)
|
||||
.map_err(|e| anyhow!(e))
|
||||
.context("failed to emit module")?;
|
||||
|
||||
if self.common.debug_info {
|
||||
let debug_data = read_debuginfo(&data);
|
||||
emit_debugsections(
|
||||
&mut obj,
|
||||
&module_vmctx_info,
|
||||
target_config,
|
||||
&debug_data,
|
||||
&address_transform,
|
||||
&value_ranges,
|
||||
)
|
||||
.context("failed to emit debug sections")?;
|
||||
}
|
||||
|
||||
// FIXME: Make the format a parameter.
|
||||
let file = File::create(Path::new(&self.output)).context("failed to create object file")?;
|
||||
obj.write(file).context("failed to write object file")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
83
src/commands/wast.rs
Normal file
83
src/commands/wast.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
//! The module that implements the `wasmtime wast` command.
|
||||
|
||||
use crate::{init_file_per_thread_logger, pick_compilation_strategy, CommonOptions};
|
||||
use anyhow::{bail, Context as _, Result};
|
||||
use std::{fmt::Write, path::PathBuf};
|
||||
use structopt::{clap::AppSettings, StructOpt};
|
||||
use wasmtime::{Config, Engine, HostRef, Store};
|
||||
use wasmtime_environ::cache_init;
|
||||
use wasmtime_wast::WastContext;
|
||||
|
||||
/// Runs a WebAssembly test script file
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(
|
||||
name = "wast",
|
||||
version = env!("CARGO_PKG_VERSION"),
|
||||
setting = AppSettings::ColoredHelp,
|
||||
)]
|
||||
pub struct WastCommand {
|
||||
#[structopt(flatten)]
|
||||
common: CommonOptions,
|
||||
|
||||
/// The path of the WebAssembly test script to run
|
||||
#[structopt(required = true, value_name = "SCRIPT_FILE", parse(from_os_str))]
|
||||
scripts: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl WastCommand {
|
||||
/// Executes the command.
|
||||
pub fn execute(&self) -> Result<()> {
|
||||
let log_config = if self.common.debug {
|
||||
pretty_env_logger::init();
|
||||
None
|
||||
} else {
|
||||
let prefix = "wast.dbg.";
|
||||
init_file_per_thread_logger(prefix);
|
||||
Some(prefix)
|
||||
};
|
||||
|
||||
let errors = cache_init(
|
||||
!self.common.disable_cache,
|
||||
self.common.config.as_ref(),
|
||||
log_config,
|
||||
);
|
||||
|
||||
if !errors.is_empty() {
|
||||
let mut message = String::new();
|
||||
writeln!(message, "Cache initialization failed. Errors:")?;
|
||||
for e in errors {
|
||||
writeln!(message, " -> {}", e)?;
|
||||
}
|
||||
bail!(message);
|
||||
}
|
||||
|
||||
let mut config = Config::new();
|
||||
config
|
||||
.cranelift_debug_verifier(cfg!(debug_assertions))
|
||||
.debug_info(self.common.debug_info)
|
||||
.wasm_simd(self.common.enable_simd)
|
||||
.strategy(pick_compilation_strategy(
|
||||
self.common.cranelift,
|
||||
self.common.lightbeam,
|
||||
)?)?;
|
||||
|
||||
if self.common.optimize {
|
||||
config.cranelift_opt_level(wasmtime::OptLevel::Speed);
|
||||
}
|
||||
|
||||
let store = HostRef::new(Store::new(&Engine::new(&config)));
|
||||
let mut wast_context = WastContext::new(store);
|
||||
|
||||
wast_context
|
||||
.register_spectest()
|
||||
.expect("error instantiating \"spectest\"");
|
||||
|
||||
for script in self.scripts.iter() {
|
||||
wast_context
|
||||
.run_file(script)
|
||||
.with_context(|| format!("failed to run script file '{}'", script.display()))?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user