26 个重大版本
0.30.0 | 2024 年 8 月 12 日 |
---|---|
0.28.0 | 2024 年 7 月 16 日 |
0.23.0 | 2024 年 3 月 27 日 |
0.16.0 | 2023 年 12 月 5 日 |
0.4.0 | 2023 年 3 月 7 日 |
#302 in WebAssembly
每月 746 次下载
605KB
6.5K SLoC
关于
该项目是一套为编译到 WebAssembly 并使用 组件模型 的语言设计的绑定生成器。绑定使用 *.wit
文件 描述,这些文件指定导入、导出,并促进绑定定义之间的重用。
wit-bindgen
存储库目前专注于 客户端 程序,即编译到 WebAssembly 的程序。在宿主中执行组件不在本存储库中管理,有关如何执行的一些选项在 下面描述。本存储库中开发的语言包括 Rust、C、Java (TeaVM Java)、Go (TinyGo) 和 C#。如果您遇到任何问题,请随时 提交问题 或在我们 Zulip 上聊天。
WIT 作为 IDL
项目 wit-bindgen
广泛使用了 WIT 定义来描述导入和导出。由 WIT 支持的项目直接映射到组件模型,这使得由本地编译器产生的核心 WebAssembly 二进制文件可以转换为组件。WebAssembly 二进制文件的所有导入和导出都必须使用 WIT 进行描述。一个示例文件看起来像
package example:host;
world host {
import print: func(msg: string);
export run: func();
}
这描述了一个“世界”,其中包含了 WebAssembly 组件将可用的导入和导出。在这种情况下,宿主将提供一个 print
函数,而组件本身将提供一个 run
函数。
WIT 中的功能也可以组织到 interface
中
package example:my-game;
interface my-plugin-api {
record coord {
x: u32,
y: u32,
}
get-position: func() -> coord;
set-position: func(pos: coord);
record monster {
name: string,
hp: u32,
pos: coord,
}
monsters: func() -> list<monster>;
}
world my-game {
import print: func(msg: string);
import my-plugin-api;
export run: func();
}
这里,my-plugin-api
接口封装了一组函数、类型等。然后可以通过 my-plugin-api
命名空间将它们全部导入到 my-game
世界中。一个 WIT 文档和世界的结构将影响每个语言的生成绑定。
有关 WIT 及其语法的更多信息,请参阅 WIT 在线文档 以及其 上游参考。
创建组件
wit-bindgen
的最终目标是便于创建 组件。一旦创建了组件,就可以将其移交给任何宿主运行时进行执行。然而,目前没有任何语言支持以原生方式创建组件,因此 wit-bindgen
只是创建组件过程中的一个组成部分。对于编译语言的组件构建过程的一般概述是
- 使用
wit-bindgen
为该语言生成代表指定 API 的绑定的源代码。然后,由本地编译器编译此源代码,并由用户编写的代码使用。 - 使用本地语言工具链生成一个核心 WebAssembly 模块。这个核心 wasm 模块是组件的“主体”,包含了所有编译成 WebAssembly 的用户定义代码。目前最常用的编译目标是
wasm32-wasip1
。 - 使用
wasm-tools
项目(特别是wasm-tools component new
子命令)将输出核心 wasm 模块转换为组件。这将消耗本地的核心 wasm 输出并将其包装成组件模型二进制格式。
每个步骤的精确工具和命令各不相同,具体取决于语言,但这是基本思路。有了组件,二进制文件就可以交给宿主运行时执行。
创建组件:WASI
今天创建组件时,WASI 是一个重要的考虑因素。目前所有具有 WASI 支持的语言的本地工具链都在使用 wasi_snapshot_preview1
版本的 WASI。这个 WASI 定义是用历史 *.witx
文件完成的,并且与组件模型不兼容。然而,有一种方法可以创建使用 wasi_snapshot_preview1
API 的模块的组件。
《wasm-tools component new》子命令接受一个--adapt
参数,该参数作为一种方法,用于用组件模型API填充非组件模型API,例如wasi_snapshot_preview1
。Wasmtime运行时在每次发布时都会发布适配器模块,这些模块适用于--adapt
,以WASI 0.2的形式实现wasi_snapshot_preview1
。在Wasmtime的发布页面上,您将看到三个模块可供选择
wasi_snapshot_preview1.command.wasm
- 用于CLI应用程序。wasi_snapshot_preview1.reactor.wasm
- 用于没有main
函数的应用程序,例如响应事件的进程。wasi_snapshot_preview1.proxy.wasm
- 用于输入到wasmtime serve
的应用程序,例如。
只需要一个适配器,并确保查找最新版本。
支持的客户端语言
《wit-bindgen》项目主要关注客户端语言,这些语言被编译成WebAssembly。这里列出的每种语言在核心wasm层(例如,针对当前核心wasm规范)都已有原生支持在WebAssembly中执行。这里还列出了每个语言的简要说明,说明如何使用它。
以下每个项目都假设您的项目根目录中存在以下*.wit
文件。
// wit/host.wit
package example:host;
world host {
import print: func(msg: string);
export run: func();
}
客户端:Rust
Rust编译器支持本地的wasm32-wasip1
目标,可以通过以下方式将其添加到任何基于rustup
的工具链中
rustup target add wasm32-wasip1
为了编译wasi动态库,必须在Cargo.toml
文件中添加以下内容
[lib]
crate-type = ["cdylib"]
项目可以通过执行以下命令来依赖于wit-bindgen
cargo add wit-bindgen
WIT文件目前被添加到与您的Cargo.toml
文件相邻的wit/
文件夹中。使用此文件的示例代码如下
// src/lib.rs
// Use a procedural macro to generate bindings for the world we specified in
// `host.wit`
wit_bindgen::generate!({
// the name of the world in the `*.wit` input file
world: "host",
});
// Define a custom type and implement the generated `Guest` trait for it which
// represents implementing all the necessary exported interfaces for this
// component.
struct MyHost;
impl Guest for MyHost {
fn run() {
print("Hello, world!");
}
}
// export! defines that the `MyHost` struct defined below is going to define
// the exports of the `world`, namely the `run` function.
export!(MyHost);
通过使用cargo expand
或cargo doc
,您还可以探索生成的代码。如果wit-bindgen
中存在错误,生成的绑定无法编译,或者在生成的代码中存在错误(这可能是wit-bindgen
中的错误),您可以使用环境变量WIT_BINDGEN_DEBUG=1
来帮助调试。
然后可以使用以下命令构建该项目
cargo build --target wasm32-wasip1
wasm-tools component new ./target/wasm32-wasip1/debug/my-project.wasm \
-o my-component.wasm --adapt ./wasi_snapshot_preview1.reactor.wasm
这会创建一个my-component.wasm
文件,该文件可以在任何组件运行时中执行。使用wasm-tools
,您还可以检查该二进制文件,例如推断出组件的WIT世界
wasm-tools component wit my-component.wasm
# world my-component {
# import print: func(msg: string)
# export run: func()
# }
在这种情况下,如预期的那样,它与输入世界相同。
客户端:C/C++
C和C++代码可以使用WASI SDK项目进行编译,以wasm32-wasip1
为目标。该仓库中的版本包含了预编译的clang
二进制文件,这些文件已预先配置为编译WebAssembly。
要在C和C++中开始,会为你的项目生成一个*.c
和一个*.h
头文件。这些文件是用这个仓库中的wit-bindgen
CLI命令生成的。
wit-bindgen c ./wit
# Generating "host.c"
# Generating "host.h"
# Generating "host_component_type.o"
使用这个方法的示例代码如下
// my-component.c
#include "host.h"
void host_run() {
host_string_t my_string;
host_string_set(&my_string, "Hello, world!");
host_print(&my_string);
}
然后可以使用WASI SDK中的clang
进行编译,并使用以下命令将其组装成一个组件
clang host.c host_component_type.o my-component.c -o my-core.wasm -mexec-model=reactor
wasm-tools component new ./my-core.wasm -o my-component.wasm
与Rust类似,你可以检查输出二进制文件
wasm-tools component wit ./my-component.wasm
访客:Java
Java字节码可以使用TeaVM-WASI编译成WebAssembly。使用此生成器,wit-bindgen
将生成*.java
文件,这些文件可以与任何JVM语言(例如Java、Kotlin、Clojure、Scala等)一起使用。
访客:TinyGo
你可以使用TinyGo编译器将Go代码编译成一个Wasm模块。例如,以下命令将main.go
编译成WASI模块
tinygo build-目标=wasi main.go
注意:当前的TinyGo
bindgen
需要TinyGo版本v0.27.0或更高版本。
当使用wit-bindgen tiny-go
bindgen时,将生成*.go
和*.h
C头文件供你的项目使用。这些文件是用这个仓库中的wit-bindgen
CLI命令生成的。
wit-bindgen tiny-go ./wit
# Generating "host.go"
# Generating "host.c"
# Generating "host.h"
# Generating "host_component_type.o"
如果你的Go代码使用了result
或option
类型,将生成一个额外的Go文件host_types.go
。该文件包含与WIT文件中的result
和option
类型相对应的Go类型。
使用生成的Go代码的示例如下
初始化Go
go mod init example.com
创建你的Go主文件
// my-component.go
package main
import (
api "example.com/api"
)
func init() {
a := HostImpl{}
api.SetHost(a)
}
type HostImpl struct {
}
func (e HostImpl) Run() {
api.HostPrint("Hello, world!")
}
//go:generate wit-bindgen tiny-go wit --out-dir=api
func main() {}
此设置允许你调用go generate
,它将生成Go代码的绑定到api
目录。之后,你可以使用TinyGo编译器将Go代码编译成WASI模块。最后,你可以使用wasm-tools
组件化模块。
go generate # generate bindings for Go
tinygo build -target=wasi -o main.wasm my-component.go # compile
wasm-tools component embed --world host ./wit main.wasm -o main.embed.wasm # create a component
wasm-tools component new main.embed.wasm --adapt wasi_snapshot_preview1.command.wasm -o main.component.wasm
wasm-tools validate main.component.wasm --features component-model
访客:其他语言
希望有一天可以使用wit-bindgen
或通用组件支持其他语言,如JS、Ruby、Python等。如果你有兴趣为这些语言之一贡献生成器,建议在zulip上联系。但是,值得注意的是,将解释型语言转换为组件与编译型语言(例如Rust或C/C++)的工作方式有很大不同。预计第一个解释型语言将需要大量的设计工作,但一旦实现,其他语言可以相对快速地按照第一个设计进行。
CLI安装
要安装此工具的CLI(这并非使用它的唯一方法),请运行以下cargo命令。这将允许你为任何受支持的语言生成绑定。
cargo install wit-bindgen-cli
此CLI 不稳定,可能会更改,请不要期待它稳定或依赖其稳定性。如果您想依赖它,请通过Zulip联系我们,以便我们为您的情况找到更好的替代方案。
组件的宿主运行时
wit-bindgen
项目旨在帮助生成组件,但一旦组件在您手中,下一步就是实际在某处执行它。这不在wit-bindgen
本身的范围内,但以下是一些资源和运行时,可以帮助您使用组件。
-
Rust:[wasmtime](https://docs.rs/wasmtime)crate是一个原生组件运行时的实现,可以运行任何WIT
world
。它还提供了一个[bindgen!](https://docs.rs/wasmtime/latest/wasmtime/component/macro.bindgen.html)宏,与该存储库中的[generate!](https://component-model.bytecodealliance.org/design/wit.html)宏类似。此宏接收一个WIT包作为输入,并为运行时生成基于trait
的绑定,以实现和使用。 -
JS:[jco](https://github.com/bytecodealliance/jco)项目可以用来在JS中执行组件,无论是在网页上还是在浏览器之外的运行时(如
node
)中。该项目通过提取组成组件的核心WebAssembly模块,并生成JS粘合剂以在主机和这些模块之间进行交互,为单个具体组件生成polyfill以在JS环境中执行。 -
Python:[wasmtime-py](https://github.com/bytecodealliance/wasmtime-py)项目[在PyPI](http://pypi.ac.cn/project/wasmtime/)上的
bindgen
模式与JS集成类似。给定一个具体组件,它将生成Python源代码,使用Wasmtime的嵌入式核心WebAssembly支持与组件交互。 -
工具:[wasm-tools](https://github.com/bytecodealliance/wasm-tools)项目可以用来检查和修改组件的低级细节。例如,如前所述,您可以使用
wasm-tools component wit
检查组件的WIT-based接口。您还可以使用wasm-tools compose
将两个组件链接在一起。
请注意,上述运行时通常旨在与任意组件一起工作,而不仅仅是那些由wit-bindgen
创建的组件。这也不一定是执行组件的所有可能性的详尽列表。
构建和测试
要构建CLI
cargo build
有关如何在测试文档中运行测试的更多信息,请参阅测试文档。
版本控制和发布
此存储库中的crate和CLI目前都使用版本0.X.Y
进行版本控制,其中Y
通常是0
,而X
在发布时通常会递增。这意味着,随着此处的发展,更改以可能破坏API的方式发布。
此外,此存储库目前没有严格的发布时间表。根据需要发布。如果您想进行发布,请随时通过Zulip、提交问题、在PR上留下评论或以其他方式联系维护者。
对于维护者,发布流程如下
- 转到此链接
- 在UI中单击“运行工作流程”。
- 使用默认的
bump
参数,然后点击“运行工作流程” - 等待 CI 创建一个 PR。你可以通过“操作”选项卡查看是否出错。
- 当 PR 打开时,关闭它然后重新打开。不要提问。
- 审查 PR,批准它,然后排队合并。
这就够了,但一定要密切关注 CI,以防出现任何问题。
许可证
本项目在 Apache 2/Apache 2 加 LLVM 例外/MIT 许可证下三重许可。这样做的原因是
- Apache 2/MIT 在 Rust 生态系统中很常见。
- Apache 2/MIT 用于 Rust 标准库,并且部分代码可能会迁移到那里。
- 部分代码可能用于编译器输出,Apache 2 加 LLVM 例外许可证对此很有用。
更多详情请参阅
贡献
除非你明确声明,否则你提交给本项目以供包含的任何贡献,根据 Apache 2/Apache 2 加 LLVM 例外/MIT 许可证的定义,应按上述方式许可,不附加任何额外条款或条件。
依赖项
~8MB
~139K SLoC