IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [WebAssembly]初学笔记 安装环境和尝试编译

    罗佳(博主)发表于 2024-08-12 08:38:23
    love 0

    以前也尝试过学WASM,但由于当时没有应用场景所以安装完环境就弃坑了,这次来好好学习一下用法。这篇笔记是边学边写的,如果有错误或者需要补充的地方请留言。

    WASM通常需要使用其他高级语言编写后编译为wasm二进制文件交给运行时去运行,但如果你的头够铁,也可以尝试手写wasm指令,或者通过wasm的文本格式理解它的底层运行原理。

    本笔记在Windows平台上使用C++编写源码编译为wasm,所以需要安装emscripten工具包。除了C++以外,Rust也是一个推荐选项,而其它语言目前还没有非常完备的支持。

    安装Emscripten

    这是用于把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。

    为了符合通常的开发习惯,因此接下来所有的编译过程都会添加以下编译选项:

    • -sMODULARIZE=1,作用是让生成的js文件作为模块导出,并且使用start.js作为执行入口来导入我们的编译结果再执行。
    • -sEXPORT_ES6,指定该参数将生成es6格式的模块,不指定会生成UMD格式的模块,为了浏览器和node之间的通用性,我们均导出es6模块。
    • –sASSERTIONS,作用是显示更详细的错误信息,以便我们调试。在编译发布版本时不用加这个,也不应该加这个。
    • 关于更多的编译选项,可以查看官方文档:https://emscripten.org/docs/tools_reference/settings_reference.html。

    有一个参数在这里我不会去使用,但是在很多地方会出现却没说明为什么要设置这个值, 所以我觉得需要特别说明一下:-sWASM=0|1|2,这个参数有三个值可选

    • -sWASM=0:编译器将不会生成wasm文件,仅仅把C++编译为Javascript代码。
    • -sWASM=1(默认):编译器将生成一个wasm文件和一个js加载器文件,生成的两个文件搭配使用,缺一不可。
    • -sWASM=2:编译器同样会生成一个wasm文件和一个js文件,但这个js文件除了能够加载wasm文件以外也包含了C++源码中的全部逻辑,用于在运行环境无法加载wasm时以js替代执行。

    为了之后方便编译,建议把编译命令编写成 脚本文件 或者写进 package.json的scripts条目 里以便直接调用。

    第一个WASM程序

    打开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了,接下来我会继续写几篇笔记以完成更复杂的任务。



沪ICP备19023445号-2号
友情链接