Update WIT tooling used by Wasmtime (#5565)
* Update WIT tooling used by Wasmtime This commit updates the WIT tooling, namely the wasm-tools family of crates, with recent updates. Notably: * bytecodealliance/wasm-tools#867 * bytecodealliance/wasm-tools#871 This updates index spaces in components and additionally bumps the minimum required version of the component binary format to be consumed by Wasmtime (because of the index space changes). Additionally WIT tooling now fully supports `use`. Note that WIT tooling doesn't, at this time, fully support packages and depending on remotely defined WIT packages. Currently WIT still needs to be vendored in the project. It's hoped that future work with `cargo component` and possible integration here could make the story about depending on remotely-defined WIT more ergonomic and streamlined. * Fix `bindgen!` codegen tests * Add a test for `use` paths an implement support * Update to crates.io versions of wasm-tools * Uncomment codegen tests
This commit is contained in:
@@ -3,14 +3,14 @@ use std::path::{Path, PathBuf};
|
||||
use syn::parse::{Error, Parse, ParseStream, Result};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::{braced, token, Ident, Token};
|
||||
use wasmtime_wit_bindgen::Opts;
|
||||
use wit_parser::{Document, World};
|
||||
use wasmtime_wit_bindgen::{Opts, TrappableError};
|
||||
use wit_parser::{PackageId, Resolve, UnresolvedPackage, WorldId};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Config {
|
||||
opts: Opts, // ...
|
||||
world: World,
|
||||
files: Vec<String>,
|
||||
opts: Opts,
|
||||
resolve: Resolve,
|
||||
world: WorldId,
|
||||
files: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
pub fn expand(input: &Config) -> Result<TokenStream> {
|
||||
@@ -21,93 +21,162 @@ pub fn expand(input: &Config) -> Result<TokenStream> {
|
||||
));
|
||||
}
|
||||
|
||||
let src = input.opts.generate(&input.world);
|
||||
let src = input.opts.generate(&input.resolve, input.world);
|
||||
let mut contents = src.parse::<TokenStream>().unwrap();
|
||||
|
||||
// Include a dummy `include_str!` for any files we read so rustc knows that
|
||||
// we depend on the contents of those files.
|
||||
let cwd = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
for file in input.files.iter() {
|
||||
contents.extend(
|
||||
format!(
|
||||
"const _: &str = include_str!(r#\"{}\"#);\n",
|
||||
Path::new(&cwd).join(file).display()
|
||||
)
|
||||
.parse::<TokenStream>()
|
||||
.unwrap(),
|
||||
format!("const _: &str = include_str!(r#\"{}\"#);\n", file.display())
|
||||
.parse::<TokenStream>()
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(contents)
|
||||
}
|
||||
|
||||
enum Source {
|
||||
Path(String),
|
||||
Inline(String),
|
||||
}
|
||||
|
||||
impl Parse for Config {
|
||||
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||
let call_site = Span::call_site();
|
||||
let mut opts = Opts::default();
|
||||
let mut world = None;
|
||||
let mut ret = Config::default();
|
||||
let mut source = None;
|
||||
|
||||
if input.peek(token::Brace) {
|
||||
let document = if input.peek(token::Brace) {
|
||||
let content;
|
||||
syn::braced!(content in input);
|
||||
let fields = Punctuated::<Opt, Token![,]>::parse_terminated(&content)?;
|
||||
let mut document = None;
|
||||
for field in fields.into_pairs() {
|
||||
match field.into_value() {
|
||||
Opt::Path(path) => {
|
||||
if world.is_some() {
|
||||
return Err(Error::new(path.span(), "cannot specify second world"));
|
||||
Opt::Path(s) => {
|
||||
if source.is_some() {
|
||||
return Err(Error::new(s.span(), "cannot specify second source"));
|
||||
}
|
||||
world = Some(ret.parse(path)?);
|
||||
source = Some(Source::Path(s.value()));
|
||||
}
|
||||
Opt::Inline(span, w) => {
|
||||
if world.is_some() {
|
||||
return Err(Error::new(span, "cannot specify second world"));
|
||||
Opt::World(s) => {
|
||||
if document.is_some() {
|
||||
return Err(Error::new(s.span(), "cannot specify second document"));
|
||||
}
|
||||
world = Some(w);
|
||||
document = Some(parse_doc(&s.value(), &mut world));
|
||||
}
|
||||
Opt::Tracing(val) => ret.opts.tracing = val,
|
||||
Opt::Async(val) => ret.opts.async_ = val,
|
||||
Opt::TrappableErrorType(val) => ret.opts.trappable_error_type = val,
|
||||
Opt::Inline(s) => {
|
||||
if source.is_some() {
|
||||
return Err(Error::new(s.span(), "cannot specify second source"));
|
||||
}
|
||||
source = Some(Source::Inline(s.value()));
|
||||
}
|
||||
Opt::Tracing(val) => opts.tracing = val,
|
||||
Opt::Async(val) => opts.async_ = val,
|
||||
Opt::TrappableErrorType(val) => opts.trappable_error_type = val,
|
||||
}
|
||||
}
|
||||
match (document, &source) {
|
||||
(Some(doc), _) => doc,
|
||||
(None, Some(Source::Inline(_))) => "macro-input".to_string(),
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
call_site,
|
||||
"must specify a `world` to generate bindings for",
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let s = input.parse::<syn::LitStr>()?;
|
||||
world = Some(ret.parse(s)?);
|
||||
}
|
||||
ret.world = world.ok_or_else(|| {
|
||||
Error::new(
|
||||
call_site,
|
||||
"must specify a `*.wit` file to generate bindings for",
|
||||
)
|
||||
})?;
|
||||
Ok(ret)
|
||||
let document = input.parse::<syn::LitStr>()?;
|
||||
if input.parse::<Option<syn::token::In>>()?.is_some() {
|
||||
source = Some(Source::Path(input.parse::<syn::LitStr>()?.value()));
|
||||
}
|
||||
parse_doc(&document.value(), &mut world)
|
||||
};
|
||||
let (resolve, pkg, files) =
|
||||
parse_source(&source).map_err(|err| Error::new(call_site, format!("{err:?}")))?;
|
||||
let doc = resolve.packages[pkg]
|
||||
.documents
|
||||
.get(&document)
|
||||
.copied()
|
||||
.ok_or_else(|| {
|
||||
Error::new(call_site, format!("no document named `{document}` found"))
|
||||
})?;
|
||||
|
||||
let world = match &world {
|
||||
Some(name) => resolve.documents[doc]
|
||||
.worlds
|
||||
.get(name)
|
||||
.copied()
|
||||
.ok_or_else(|| Error::new(call_site, format!("no world named `{name}` found")))?,
|
||||
None => resolve.documents[doc].default_world.ok_or_else(|| {
|
||||
Error::new(call_site, format!("no default world found in `{document}`"))
|
||||
})?,
|
||||
};
|
||||
Ok(Config {
|
||||
opts,
|
||||
resolve,
|
||||
world,
|
||||
files,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn parse(&mut self, path: syn::LitStr) -> Result<World> {
|
||||
let span = path.span();
|
||||
let path = path.value();
|
||||
let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||
let path = manifest_dir.join(path);
|
||||
self.files.push(path.to_str().unwrap().to_string());
|
||||
World::parse_file(path).map_err(|e| Error::new(span, e))
|
||||
fn parse_source(source: &Option<Source>) -> anyhow::Result<(Resolve, PackageId, Vec<PathBuf>)> {
|
||||
let mut resolve = Resolve::default();
|
||||
let mut files = Vec::new();
|
||||
let root = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||
let mut parse = |path: &Path| -> anyhow::Result<_> {
|
||||
if path.is_dir() {
|
||||
let (pkg, sources) = resolve.push_dir(&path)?;
|
||||
files = sources;
|
||||
Ok(pkg)
|
||||
} else {
|
||||
let pkg = UnresolvedPackage::parse_file(path)?;
|
||||
files.extend(pkg.source_files().map(|s| s.to_owned()));
|
||||
resolve.push(pkg, &Default::default())
|
||||
}
|
||||
};
|
||||
let pkg = match source {
|
||||
Some(Source::Inline(s)) => resolve.push(
|
||||
UnresolvedPackage::parse("macro-input".as_ref(), &s)?,
|
||||
&Default::default(),
|
||||
)?,
|
||||
Some(Source::Path(s)) => parse(&root.join(&s))?,
|
||||
None => parse(&root.join("wit"))?,
|
||||
};
|
||||
|
||||
Ok((resolve, pkg, files))
|
||||
}
|
||||
|
||||
fn parse_doc(s: &str, world: &mut Option<String>) -> String {
|
||||
match s.find('.') {
|
||||
Some(pos) => {
|
||||
*world = Some(s[pos + 1..].to_string());
|
||||
s[..pos].to_string()
|
||||
}
|
||||
None => s.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
mod kw {
|
||||
syn::custom_keyword!(path);
|
||||
syn::custom_keyword!(inline);
|
||||
syn::custom_keyword!(path);
|
||||
syn::custom_keyword!(tracing);
|
||||
syn::custom_keyword!(trappable_error_type);
|
||||
syn::custom_keyword!(world);
|
||||
}
|
||||
|
||||
enum Opt {
|
||||
World(syn::LitStr),
|
||||
Path(syn::LitStr),
|
||||
Inline(Span, World),
|
||||
Inline(syn::LitStr),
|
||||
Tracing(bool),
|
||||
Async(bool),
|
||||
TrappableErrorType(Vec<(String, String, String)>),
|
||||
TrappableErrorType(Vec<TrappableError>),
|
||||
}
|
||||
|
||||
impl Parse for Opt {
|
||||
@@ -118,14 +187,13 @@ impl Parse for Opt {
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(Opt::Path(input.parse()?))
|
||||
} else if l.peek(kw::inline) {
|
||||
let span = input.parse::<kw::inline>()?.span;
|
||||
input.parse::<kw::inline>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
let s = input.parse::<syn::LitStr>()?;
|
||||
let world = Document::parse("<macro-input>".as_ref(), &s.value())
|
||||
.map_err(|e| Error::new(s.span(), e))?
|
||||
.into_world()
|
||||
.map_err(|e| Error::new(s.span(), e))?;
|
||||
Ok(Opt::Inline(span, world))
|
||||
Ok(Opt::Inline(input.parse()?))
|
||||
} else if l.peek(kw::world) {
|
||||
input.parse::<kw::world>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
Ok(Opt::World(input.parse()?))
|
||||
} else if l.peek(kw::tracing) {
|
||||
input.parse::<kw::tracing>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
@@ -141,7 +209,16 @@ impl Parse for Opt {
|
||||
let _lbrace = braced!(contents in input);
|
||||
let fields: Punctuated<(String, String, String), Token![,]> =
|
||||
contents.parse_terminated(trappable_error_field_parse)?;
|
||||
Ok(Opt::TrappableErrorType(fields.into_iter().collect()))
|
||||
Ok(Opt::TrappableErrorType(
|
||||
fields
|
||||
.into_iter()
|
||||
.map(|(wit_owner, wit_name, rust_name)| TrappableError {
|
||||
wit_owner: Some(wit_owner),
|
||||
wit_name,
|
||||
rust_name,
|
||||
})
|
||||
.collect(),
|
||||
))
|
||||
} else {
|
||||
Err(l.error())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user