以前也尝试过学WASM,但由于当时没有应用场景所以安装完环境就弃坑了,这次来好好学习一下用法。这篇笔记是边学边写的,如果有错误或者需要补充的地方请留言。
WASM通常需要使用其他高级语言编写后编译为wasm二进制文件交给运行时去运行,但如果你的头够铁,也可以尝试手写wasm指令,或者通过wasm的文本格式理解它的底层运行原理。
本笔记在Windows平台上使用C++编写源码编译为wasm,所以需要安装emscripten工具包。除了C++以外,Rust也是一个推荐选项,而其它语言目前还没有非常完备的支持。
这是用于把C/C++编译到wasm的工具包。
Emscripten的项目地址在:https://github.com/emscripten-core/emscripten
文档地址在:https://emscripten.org/index.html
安装方式官方手册在:https://emscripten.org/docs/getting_started/downloads.html,这里我简单介绍一下,但还以官方手册为准,我不会同步更新。
首先把emscripten仓库克隆到任意位置,然后进入该目录
git clone --depth 1 https://github.com/emscripten-core/emsdk.git cd emsdk
然后依次执行以下命令进行安装
emsdk.bat install latest emsdk.bat activate latest --permanent
上面的` –permanent`参数是为了让emsdk的命令在全局可用,否则你需要在每个命令环境中都执行一次activate。执行完了之后可以关闭当前的命令窗口,然后找个地方开始准备写我们的代码了。
新建一个test目录,在里面新建一个test.cpp文件(用于编写源码)和一个start.js文件(用于导入并运行wasm)以及一个空的dist目录(用于存放编译结果),我将在test.cpp中编写我们所有的测试代码。
首先进行一些说明:通过emcc编译出来的wasm程序默认会自带一个可以直接执行的js文件作为wasm文件的加载器,把这个生成的js文件引入网页或者使用nodejs执行都可以得到它的执行结果,但现在开发项目通常都会把各种外部库作为模块导入,而不是直接引入到全局作用域,因此你需要添加一些编译选项来让emcc导出一个模块化的加载器,使其可以被其它代码import。
为了符合通常的开发习惯,因此接下来所有的编译过程都会添加以下编译选项:
有一个参数在这里我不会去使用,但是在很多地方会出现却没说明为什么要设置这个值, 所以我觉得需要特别说明一下:-sWASM=0|1|2,这个参数有三个值可选
为了之后方便编译,建议把编译命令编写成 脚本文件 或者写进 package.json的scripts条目 里以便直接调用。
打开test.cpp文件,在里面写入以下测试代码:
#include <iostream> int main() { std::cout << "Hello, World!" << std::endl; return 0; }
然后执行命令编译:emcc -sMODULARIZE=1 -sASSERTIONS -sEXPORT_ES6 -o dist/test.mjs test.cpp
执行完后应当在dist目录中生成了两个文件,一个test.mjs和一个test.wasm(注意,这两个文件是搭配使用的,需要始终放在同一个目录里),如果没有或者编译过程出错了的话就说明前面的自己根据错误搜搜,这里就当已经成功编译了。
然后打开start.js,写入以下内容:
(async ()=>{ const {default:wasm}=await import('./dist/test.mjs'); const test=await wasm(); console.log(test); })();
都保存后,直接执行node start.js
,你将能在结果中看到”Hello, World!”以及wasm实例对象的内容。其中`_main`是emcc导出的cpp文件中对应的main函数,加载器js文件会自动执行该方法,如果你不需要自动执行任何函数,可以在cpp文件中不写main函数,编译器的默认选项会自动忽略它。
到这能够成功跑起来就已经算是能够使用wasm了,接下来我会继续写几篇笔记以完成更复杂的任务。