Skip to content

Commit 188166a

Browse files
alexcrichtoneduardomourar
authored andcommitted
Refactor Wasmtime CLI to support components (bytecodealliance#6836)
This commit refactors the `wasmtime` CLI executable to be able to support not only compiling components but additionally executing components. While I was doing this I've additionally added a new `--preview2` argument to enable using the new experimental implementation of preview1 based on preview2 type/structs. This is off-by-default but is expected to become the default in the future. Some notable features of this change are: * The preview1-implemented-with-preview2 module now sports `add_to_linker_{async,sync}` to replace the previous `add_to_linker` which always did async. * Some trait bounds in the preview1-implemented-with-preview2 module are simplified. * Some minor changes were made to `wiggle`'s macros to support a "block on" that isn't the default wiggle dummy executor (as now we actually do need Tokio) * Many options related to core wasm `Linker` configuration, such as `--default-values-unknown-imports`, are not implemented for components at this time. When used with components these options return an error. * Construction of WASI contexts has been refactored to pass around fewer arguments to avoid threading through lots of values for both preview1 and preview2. * Reading the input to the Wasmtime CLI has been updated to read the input in the `run` subcommand before handing it off to the `wasmtime` crate's API to enable the CLI to use the contents of what's loaded to determine what to do next. * Our generic `./ci/run-tests.sh` script has been updated to pass the `--features component-model` flag to ensure that this CLI support is tested during the normal test suite. * The CLI support for `wasi-nn` supports components as well as core wasm modules.
1 parent c233dad commit 188166a

File tree

15 files changed

+677
-269
lines changed

15 files changed

+677
-269
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ wasmtime-wasi = { workspace = true, features = ["exit"] }
3333
wasmtime-wasi-nn = { workspace = true, optional = true }
3434
wasmtime-wasi-threads = { workspace = true, optional = true }
3535
wasmtime-wasi-http = { workspace = true, optional = true }
36+
wasmtime-runtime = { workspace = true }
3637
clap = { workspace = true, features = ["color", "suggestions", "derive"] }
3738
anyhow = { workspace = true }
3839
target-lexicon = { workspace = true }

ci/run-tests.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ cargo test \
44
--features "test-programs/test_programs" \
55
--features wasi-threads \
66
--features wasi-http \
7+
--features component-model \
78
--workspace \
89
--exclude 'wasmtime-wasi-*' \
910
--exclude wasi-tests \

crates/test-programs/tests/wasi-http-modules.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl WasiHttpView for Ctx {
6363
async fn instantiate_module(module: Module, ctx: Ctx) -> Result<(Store<Ctx>, Func), anyhow::Error> {
6464
let mut linker = Linker::new(&ENGINE);
6565
wasmtime_wasi_http::add_to_linker(&mut linker)?;
66-
wasmtime_wasi::preview2::preview1::add_to_linker(&mut linker)?;
66+
wasmtime_wasi::preview2::preview1::add_to_linker_async(&mut linker)?;
6767

6868
let mut store = Store::new(&ENGINE, ctx);
6969

crates/test-programs/tests/wasi-preview1-host-in-preview2.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use tempfile::TempDir;
44
use wasmtime::{Config, Engine, Linker, Store};
55
use wasmtime_wasi::preview2::{
66
pipe::MemoryOutputPipe,
7-
preview1::{add_to_linker, WasiPreview1Adapter, WasiPreview1View},
7+
preview1::{add_to_linker_async, WasiPreview1Adapter, WasiPreview1View},
88
DirPerms, FilePerms, IsATTY, Table, WasiCtx, WasiCtxBuilder, WasiView,
99
};
1010

@@ -34,7 +34,7 @@ async fn run(name: &str, inherit_stdio: bool) -> Result<()> {
3434
let stderr = MemoryOutputPipe::new();
3535
let r = {
3636
let mut linker = Linker::new(&ENGINE);
37-
add_to_linker(&mut linker)?;
37+
add_to_linker_async(&mut linker)?;
3838

3939
// Create our wasi context.
4040
// Additionally register any preopened directories if we have them.

crates/wasi-nn/src/wit.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ impl gen::inference::Host for WasiNnCtx {
128128
}
129129
}
130130

131+
impl gen::errors::Host for WasiNnCtx {}
132+
133+
impl gen::tensor::Host for WasiNnCtx {}
134+
131135
impl TryFrom<gen::graph::GraphEncoding> for crate::backend::BackendKind {
132136
type Error = UsageError;
133137
fn try_from(value: gen::graph::GraphEncoding) -> Result<Self, Self::Error> {

crates/wasi/src/preview2/preview1.rs

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ impl WasiPreview1Adapter {
215215

216216
// Any context that needs to support preview 1 will impl this trait. They can
217217
// construct the needed member with WasiPreview1Adapter::new().
218-
pub trait WasiPreview1View: Send + Sync + WasiView {
218+
pub trait WasiPreview1View: WasiView {
219219
fn adapter(&self) -> &WasiPreview1Adapter;
220220
fn adapter_mut(&mut self) -> &mut WasiPreview1Adapter;
221221
}
@@ -390,23 +390,18 @@ trait WasiPreview1ViewExt:
390390

391391
impl<T: WasiPreview1View + preopens::Host> WasiPreview1ViewExt for T {}
392392

393-
pub fn add_to_linker<
394-
T: WasiPreview1View
395-
+ bindings::cli::environment::Host
396-
+ bindings::cli::exit::Host
397-
+ bindings::filesystem::types::Host
398-
+ bindings::filesystem::preopens::Host
399-
+ bindings::sync_io::poll::poll::Host
400-
+ bindings::random::random::Host
401-
+ bindings::io::streams::Host
402-
+ bindings::clocks::monotonic_clock::Host
403-
+ bindings::clocks::wall_clock::Host,
404-
>(
393+
pub fn add_to_linker_async<T: WasiPreview1View>(
405394
linker: &mut wasmtime::Linker<T>,
406395
) -> anyhow::Result<()> {
407396
wasi_snapshot_preview1::add_to_linker(linker, |t| t)
408397
}
409398

399+
pub fn add_to_linker_sync<T: WasiPreview1View>(
400+
linker: &mut wasmtime::Linker<T>,
401+
) -> anyhow::Result<()> {
402+
sync::add_wasi_snapshot_preview1_to_linker(linker, |t| t)
403+
}
404+
410405
// Generate the wasi_snapshot_preview1::WasiSnapshotPreview1 trait,
411406
// and the module types.
412407
// None of the generated modules, traits, or types should be used externally
@@ -425,6 +420,32 @@ wiggle::from_witx!({
425420
errors: { errno => trappable Error },
426421
});
427422

423+
mod sync {
424+
use anyhow::Result;
425+
use std::future::Future;
426+
427+
wiggle::wasmtime_integration!({
428+
witx: ["$CARGO_MANIFEST_DIR/witx/wasi_snapshot_preview1.witx"],
429+
target: super,
430+
block_on[in_tokio]: {
431+
wasi_snapshot_preview1::{
432+
fd_advise, fd_close, fd_datasync, fd_fdstat_get, fd_filestat_get, fd_filestat_set_size,
433+
fd_filestat_set_times, fd_read, fd_pread, fd_seek, fd_sync, fd_readdir, fd_write,
434+
fd_pwrite, poll_oneoff, path_create_directory, path_filestat_get,
435+
path_filestat_set_times, path_link, path_open, path_readlink, path_remove_directory,
436+
path_rename, path_symlink, path_unlink_file
437+
}
438+
},
439+
errors: { errno => trappable Error },
440+
});
441+
442+
// Small wrapper around `in_tokio` to add a `Result` layer which is always
443+
// `Ok`
444+
fn in_tokio<F: Future>(future: F) -> Result<F::Output> {
445+
Ok(crate::preview2::in_tokio(future))
446+
}
447+
}
448+
428449
impl wiggle::GuestErrorType for types::Errno {
429450
fn success() -> Self {
430451
Self::Success

crates/wasi/src/preview2/table.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,3 +285,9 @@ impl Table {
285285
})
286286
}
287287
}
288+
289+
impl Default for Table {
290+
fn default() -> Self {
291+
Table::new()
292+
}
293+
}

crates/wiggle/generate/src/config.rs

Lines changed: 23 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use {
2-
proc_macro2::Span,
2+
proc_macro2::{Span, TokenStream},
33
std::{collections::HashMap, iter::FromIterator, path::PathBuf},
44
syn::{
55
braced, bracketed,
@@ -61,14 +61,21 @@ impl Parse for ConfigField {
6161
input.parse::<Token![async]>()?;
6262
input.parse::<Token![:]>()?;
6363
Ok(ConfigField::Async(AsyncConf {
64-
blocking: false,
64+
block_with: None,
6565
functions: input.parse()?,
6666
}))
6767
} else if lookahead.peek(kw::block_on) {
6868
input.parse::<kw::block_on>()?;
69+
let block_with = if input.peek(syn::token::Bracket) {
70+
let content;
71+
let _ = bracketed!(content in input);
72+
content.parse()?
73+
} else {
74+
quote::quote!(wiggle::run_in_dummy_executor)
75+
};
6976
input.parse::<Token![:]>()?;
7077
Ok(ConfigField::Async(AsyncConf {
71-
blocking: true,
78+
block_with: Some(block_with),
7279
functions: input.parse()?,
7380
}))
7481
} else if lookahead.peek(kw::wasmtime) {
@@ -381,16 +388,16 @@ impl std::fmt::Debug for UserErrorConfField {
381388
#[derive(Clone, Default, Debug)]
382389
/// Modules and funcs that have async signatures
383390
pub struct AsyncConf {
384-
blocking: bool,
391+
block_with: Option<TokenStream>,
385392
functions: AsyncFunctions,
386393
}
387394

388-
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
395+
#[derive(Clone, Debug)]
389396
pub enum Asyncness {
390397
/// Wiggle function is synchronous, wasmtime Func is synchronous
391398
Sync,
392399
/// Wiggle function is asynchronous, but wasmtime Func is synchronous
393-
Blocking,
400+
Blocking { block_with: TokenStream },
394401
/// Wiggle function and wasmtime Func are asynchronous.
395402
Async,
396403
}
@@ -402,10 +409,10 @@ impl Asyncness {
402409
_ => false,
403410
}
404411
}
405-
pub fn is_blocking(&self) -> bool {
412+
pub fn blocking(&self) -> Option<&TokenStream> {
406413
match self {
407-
Self::Blocking => true,
408-
_ => false,
414+
Self::Blocking { block_with } => Some(block_with),
415+
_ => None,
409416
}
410417
}
411418
pub fn is_sync(&self) -> bool {
@@ -429,10 +436,11 @@ impl Default for AsyncFunctions {
429436

430437
impl AsyncConf {
431438
pub fn get(&self, module: &str, function: &str) -> Asyncness {
432-
let a = if self.blocking {
433-
Asyncness::Blocking
434-
} else {
435-
Asyncness::Async
439+
let a = match &self.block_with {
440+
Some(block_with) => Asyncness::Blocking {
441+
block_with: block_with.clone(),
442+
},
443+
None => Asyncness::Async,
436444
};
437445
match &self.functions {
438446
AsyncFunctions::Some(fs) => {
@@ -577,54 +585,12 @@ impl Parse for WasmtimeConfig {
577585

578586
impl Parse for WasmtimeConfigField {
579587
fn parse(input: ParseStream) -> Result<Self> {
580-
let lookahead = input.lookahead1();
581-
if lookahead.peek(kw::target) {
588+
if input.peek(kw::target) {
582589
input.parse::<kw::target>()?;
583590
input.parse::<Token![:]>()?;
584591
Ok(WasmtimeConfigField::Target(input.parse()?))
585-
586-
// The remainder of this function is the ConfigField impl, wrapped in
587-
// WasmtimeConfigField::Core. This is required to get the correct lookahead error.
588-
} else if lookahead.peek(kw::witx) {
589-
input.parse::<kw::witx>()?;
590-
input.parse::<Token![:]>()?;
591-
Ok(WasmtimeConfigField::Core(ConfigField::Witx(
592-
WitxConf::Paths(input.parse()?),
593-
)))
594-
} else if lookahead.peek(kw::witx_literal) {
595-
input.parse::<kw::witx_literal>()?;
596-
input.parse::<Token![:]>()?;
597-
Ok(WasmtimeConfigField::Core(ConfigField::Witx(
598-
WitxConf::Literal(input.parse()?),
599-
)))
600-
} else if lookahead.peek(kw::errors) {
601-
input.parse::<kw::errors>()?;
602-
input.parse::<Token![:]>()?;
603-
Ok(WasmtimeConfigField::Core(ConfigField::Error(
604-
input.parse()?,
605-
)))
606-
} else if lookahead.peek(Token![async]) {
607-
input.parse::<Token![async]>()?;
608-
input.parse::<Token![:]>()?;
609-
Ok(WasmtimeConfigField::Core(ConfigField::Async(AsyncConf {
610-
blocking: false,
611-
functions: input.parse()?,
612-
})))
613-
} else if lookahead.peek(kw::block_on) {
614-
input.parse::<kw::block_on>()?;
615-
input.parse::<Token![:]>()?;
616-
Ok(WasmtimeConfigField::Core(ConfigField::Async(AsyncConf {
617-
blocking: true,
618-
functions: input.parse()?,
619-
})))
620-
} else if lookahead.peek(kw::mutable) {
621-
input.parse::<kw::mutable>()?;
622-
input.parse::<Token![:]>()?;
623-
Ok(WasmtimeConfigField::Core(ConfigField::Mutable(
624-
input.parse::<syn::LitBool>()?.value,
625-
)))
626592
} else {
627-
Err(lookahead.error())
593+
Ok(WasmtimeConfigField::Core(input.parse()?))
628594
}
629595
}
630596
}

crates/wiggle/generate/src/wasmtime.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,14 @@ fn generate_func(
142142
}
143143
}
144144

145-
Asyncness::Blocking => {
145+
Asyncness::Blocking { block_with } => {
146146
quote! {
147147
linker.func_wrap(
148148
#module_str,
149149
#field_str,
150150
move |mut caller: wiggle::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| -> wiggle::anyhow::Result<#ret_ty> {
151151
let result = async { #body };
152-
wiggle::run_in_dummy_executor(result)?
152+
#block_with(result)?
153153
},
154154
)?;
155155
}

src/commands/compile.rs

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -98,25 +98,13 @@ impl CompileCommand {
9898
output
9999
});
100100

101-
// If the component-model proposal is enabled and the binary we're
102-
// compiling looks like a component, tested by sniffing the first 8
103-
// bytes with the current component model proposal.
104-
#[cfg(feature = "component-model")]
105-
{
106-
if let Ok(wasmparser::Chunk::Parsed {
107-
payload:
108-
wasmparser::Payload::Version {
109-
encoding: wasmparser::Encoding::Component,
110-
..
111-
},
112-
..
113-
}) = wasmparser::Parser::new(0).parse(&input, true)
114-
{
115-
fs::write(output, engine.precompile_component(&input)?)?;
116-
return Ok(());
117-
}
118-
}
119-
fs::write(output, engine.precompile_module(&input)?)?;
101+
let output_bytes = if wasmparser::Parser::is_component(&input) {
102+
engine.precompile_component(&input)?
103+
} else {
104+
engine.precompile_module(&input)?
105+
};
106+
fs::write(&output, output_bytes)
107+
.with_context(|| format!("failed to write output: {}", output.display()))?;
120108

121109
Ok(())
122110
}

0 commit comments

Comments
 (0)