“前几篇文章你不是还在写C++的wasm笔记,为什么突然开始使用Zig了?”
实际上由于C++古老的语言特性导致有的时候必须把声明和定义分开来写非常繁琐,而且在Emscripten环境下要添加第三方的库比较麻烦,所以我想改到一个自带包管理的语言。于是就首先捡起以前学到一半被劝退的Rust,这个国庆我花了几天时间把rust的教程看完了,但我又一次被这个反人类语言极致的复杂性劝退了,也就有了上一篇文章的内容。
在群友的推荐下我发现Zig这个类C的语言好像还行,于是抱着试一试的心态来学习一下,也就有了这篇笔记。不过不代表我之后就会用这个语言写项目,还有待观察。
这篇笔记的主要内容不是怎么编写Zig语言,而是如何把一个Zig项目编译成wasm模块,因此需要先有一点点的zig基础才能看懂。现在zig的文档和社区生态还不是很完善,且有的东西还在变化,有的东西是查不到或者很难查到的,我花了一些时间摸索才搞明白一些要点,这就是写这篇笔记的原因。
环境准备
如果还没有zig环境,可以用scoop快速部署,只需执行两个脚本即可
安装scoop
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
用scoop安装最新版的zig,我写这篇笔记的时候zig版本是0.14.0-dev.1798,部分特性有可能在以后改变。
scoop bucket add versions
scoop install versions/zig-dev
以上即可完成zig环境的安装。顺带一提,使用scoop update zig-dev
可以更新zig-dev的版本。
简单的示例
最简单快速的方式就是创建一个zig文件,然后使用命令把它编译成wasm文件。
main.zig 文件(由于代码高亮插件没有支持这个语言,而且以下示例看着挺像rust的,所以先用rust高亮来凑活一下)
const std = @import("std");
pub fn main() !void {
const poi: u16 = 666;
std.debug.print("poi: {}\n", .{poi});
}
然后使用命令编译
zig build-exe main.zig -target wasm32-wasi
此时目录下就会多出一个main.wasm和一个main.wasm.o文件(以后不同版本可能有所区别),和Emscripten不同的是它不会帮你生成一个胶水js文件供你直接加载。
这里我们使用的是wasi接口的wasm,如果你的运行环境是wasmtime的话,那么就可以直接执行这个wasm文件了。
生成的结果使用nodejs也可以运行,但要在nodejs中运行它,我们得自己为加载wasm写一些代码,还好也不多:
start.js 文件
const { readFile } = require('node:fs/promises');
const { WASI } = require('node:wasi');
const { argv, env } = require('node:process');
const { join } = require('node:path');
const wasi = new WASI({
version: 'preview1',//该项必须定义,见文档:
// https://nodejs.org/docs/latest-v20.x/api/wasi.html#new-wasioptions
args: argv,//该项不是必须的,用于把node运行参数传给wasm程序
env,//该项不是必须的,用传递环境变量
//其它参数也见上面的文档链接
//注意这是一个较新的特性,不同nodejs版本的接口可能会有区别,注意选择对应的文档版本
});
(async () => {
const wasm = await WebAssembly.compile(
await readFile(join(__dirname, 'main.wasm')),
);
const instance = await WebAssembly.instantiate(wasm, wasi.getImportObject());
wasi.start(instance);
})();
使用nodejs执行以上js文件将得到输出结果:poi: 666
继续阅读[WebAssembly]使用Zig语言制作wasm模块 →