Wasmer takes WebAssembly libraries mainstream with WAI

Michael Bryan

Integrating with other languages and distributing binaries has always raised the WebAssembly's barrier to entry for the average developer, and at Wasmer our goal is to make trivial creating universal libraries that work anywhere.

Today, we are happy to announce Wasmer Pack, a tool which integrates with the WebAssembly Package Manager and WebAssembly Interfaces ("WAI") to create packages that can be imported from other languages.

/images/blog/use-wai.png

Note: WAI builds on top of the WebAssembly Interface Types specification and its first working implementation, wit-bindgen. Unfortunately, the maintainers behind wit-bindgen were reluctant to allow integration of Wasmer upstream, so we've forked the project under a new name.

A key difference between WAI and wit-bindgen is the focus on stability - people should be able to start using WAI right now.

The WAI addition to the WebAssembly Package Manager streamlines the way developers use WebAssembly in their applications by automatically generating installable packages for your language of choice.

In fact, you can see it in action right now with the vscode-wasm plugin which uses the automatically generated wabt WAI bindings, used by more than 94 thousand developers worldwide!

WAPM is not tied to just the WAI format though, we are working to allow integrating any kind of Wasm bindings into the Package Manager (such as Extism)... if you maintain a binding format we want to hear from you!

How Do I Create a Universal WebAssembly Library with Rust and WAI?

Everything revolves around WAI files.

These define the WebAssembly Interfaces your library will expose, and from there our tooling will generate some glue code that makes it easier to implement.

First, create a new Rust project and add the wai-bindgen-rust crate as a dependency.

$ cargo new --lib tutorial-01
$ cd tutorial-01
$ cargo add wai-bindgen-rust

Now, let's define a WAI file that lets us add two floating point numbers.

// calculator.wai

/// Add two numbers.
add: func(a: float32, b: float32) -> float32

We can use the wai_bindgen_rust::export!() macro to "export" this interface (i.e. make it available to some host application).

// src/lib.rs

// generate the WAI glue code under the `calculator` module
wai_bindgen_rust::export!("calculator.wai");

// Create a type to attach our functionalty to
struct Calculator;

// Implement the trait generated by our glue code
impl crate::calculator::Calculator for Calculator {
	fn add(a: f32, b: f32) -> f32 { a + b }
}

💡 You can use cargo expand to see the generated code and the Calculator trait's definition (cargo install cargo-expand).

Let’s publish it!

Publishing requires installing wapm with the Wasmer installer and the cargo wapm helper installed (cargo install cargo-wapm). If you haven't already, make sure to run wapm login to log into your WAPM account (don't forget to sign up if you haven't already).

Now we're set up, we'll need to update Cargo.toml so this package can be published to WAPM.

# Cargo.toml
[package]
name = "tutorial-01"
version = "0.1.0"
description = "A simple calculator"

[package.metadata.wapm]
namespace = "<YOUR_USERNAME>"  # The namespace to publish it to
abi = "none" # How to compile the crate. "none" is "wasm32-unknown-unknown"
bindings = { wai-version = "0.2.0", exports = "calculator.wai" }

Now we need to tell the Rust compiler to generate a cdylib ("C-compatible dynamic library"). It's also a good idea to add the rlib crate type so integration tests can import the library as a Rust dependency.

# Cargo.toml
[lib]
crate-type = ["cdylib", "rlib"]

And publish!

$ cargo wapm

Note: WAI also works with other languages, such as C or C++. If you want to publish packages to WAPM with it, you will need to use wapm publish instead of cargo wapm.

Consuming WAPM Packages in your codebase

Let's add this wai/tutorial-01 package to a JavaScript project.

First, we'll need to create a new JavaScript package and add the wai/tutorial-01 package as a dependency.

$ wasmer add --yarn wai/tutorial-01

This runs yarn add under the hood. Depending on the project, you might use the --npm flag to do npm install or --pip for pip install.

Now, let's create a script.

import { bindings } from "wai/tutorial-01";

async function main() {
	const calculator = await bindings.calculator();
	const four = calculator.add(2, 2);
	console.log("2 + 2 =", four);
}

main();

Or, if you want to do it in python:

$ wasmer add --pip wai/tutorial-01
from tutorial_01 import bindings

calculator = bindings.calculator()
print("2+2 = ", calculator.add(2.0, 2.0))

What's Next?

If you've been hesitant to use WebAssembly because it's hard to get started, go ahead and check out our tutorial series!

WAPM uses the Wasmer Pack project to generate these native packages. Feel free to browse the source code, or create tickets on the issue tracker if you have any questions.

We'd love to hear from projects looking to integrate WAPM packages into their own apps. If this sounds like you, reach out on Slack, by email, or the Wasmer Pack issue tracker, and we'll help you get started.