1.1 使用emsdk命令行工具安装Emscripten
Emscripten包含了将C/C++代码编译为WebAssembly所需的完整工具集(LLVM、Node.js、Python、Java等),它不依赖于任何其他的编译器环境。
emsdk是一组基于Python 2的脚本,Mac系统自带Python,无需安装
1.1.1 新建一个demo文件夹,然后通过git下载安装工具包
git clone https://github.com/juj/emsdk.git
1.1.2 安装并激活
进入到安装包的文件夹,分别执行以下命令
git pull
./emsdk install latest
./emsdk activate latest
把emcc命令添加至环境变量,新建一个控制台,切换至emsdk所在的目录,执行以下命令:
source ./emsdk_env.sh
注意:每次重新登陆或者新建 Shell 窗口,都要执行一次这行命令source ./emsdk_env.sh,然后本控制台窗口才能使用emcc命令。
1.1.3 验证安装是否成功
emcc是Emscripten的核心命令,正确安装激活并设置环境变量后,执行emcc -v可以查看版本信息:
> emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 2.0.17
clang version 13.0.0 (/opt/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project 3b677b81cec7b3c5132aee8fccc30252d87deb69)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /Users/liuxiaofan/WorkSpace/vipkid/emscripten/emsdk/upstream/bin
1.2 Hello, wolrd!
先跑一个最基本的demo
1.2.1 生成wasm
新建一个名为hello.cc的C源文件,为了正确标识中文字符串,将其保存为UTF8编码:
//hello.cc
#include <stdio.h>
int main() {
printf("Hello, wolrd!");
return 0;
}
执行编译命令生成wasm文件:
emcc hello.cc
目录下将得到两个文件:a.out.wasm以及a.out.js。其中a.out.wasm为C源文件编译后形成的WebAssembly汇编文件;a.out.js是Emscripten生成的胶水代码。
使用-o选项可以指定emcc的输出文件,执行下列命令:
emcc hello.cc -o hello.js
注意:现在如果改变了hello.cc文件再次编译的话会报错。因为编译出的wasm默认情况下不会退出运行时,所以每次编译的时候加上-s EXIT_RUNTIME=1 即可。如下:
emcc index.cc -o index.js -s EXIT_RUNTIME=1
1.2.2 在网页中测试
新建一个index.html的网页文件:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Emscripten:你好,世界!</title>
</head>
<body>
<script src="hello.js"></script>
</body>
</html>
本地起一个web服务,让localhost的目录指向本demo的目录,然后打开控制发现成功打印出来Hello, wolrd!
例如全局安装live-server,然后执行live-server –port=8888启动一个本地服务
npm i -g live-server
live-server --port=8888
1.2.3 在Node.js中测试
Emscripten自带了Node.js环境,因此我们可以直接使用node来测试刚才的程序:
> node hello.js
Hello, wolrd!
1.3 胶水代码初探
简单了解一下Emscripten生成的JavaScript胶水代码hello.js
1.3.1 WebAssembly汇编模块载入
WebAssembly汇编模块(既.wasm文件)的载入后经过一系列的操作,最终被实例化,然后赋值了给全局对象Module的子对象asm。
function receiveInstance(instance, module) {
... ...
Module['asm'] = exports;
console.log(Module['asm']); //我们在这添加一行Log在控制台里面打印出来看看这个对象里面都有哪些属性和方法
... ...
我当前的版本打印出来的结果是这样的,以后随着版本的升级可能会有变化。注意里面有个main方法
1.3.2 导出函数封装
在Emscripten中,C函数导出时,函数名前会添加下划线“_”,由此可知上述代码分别提供了main()以及malloc()函数的封装;我们可以在浏览器控制台中手动执行_main()以及Module._main()对此进行检验:如果报错,请忽略,接着往下看就明白了。
1.3.3 异步加载
经过我的测验,直接在控制台中执行_main()以及Module._main()会报错
Assertion failed: you need to wait for the runtime to be ready (e.g. wait for main() to be called)
解决的基本思路是在Module初始化前,向Module中注入一个名为onRuntimeInitialized的方法,Emscripten的Runtime就绪后,将会回调该方法。
<body>
<script>
Module = {};
Module.onRuntimeInitialized = function() {
//do sth.
Module._main();
}
</script>
<script src="hello.js"></script>
</body>
如上面的代码,先监听RuntimeInitialized,再执行Module._main()就好了。