Ubuntu24 搭建 Emscripten 环境
1. 准备
安装 cmake, python3 和 git 依赖:
// Install Pythonsudo apt-get install python3// Install CMake (optional, only needed for tests and building Binaryen or LLVM)sudo apt-get install cmake// Install gitsudo apt-get install git
2. 安装 Emscripten SDK
从 github 上克隆最新的代码到本地:
// Get the emsdk repogit clone https://github.com/emscripten-core/emsdk.git
这里如果你不想用克隆仓库, 也可以直接去 Github 网站上直接下载对应的源代码压缩包即可.
获取到最新的代码之后, 执行以下命令来安装最新的依赖的工具:
// Enter that directorycd emsdk// Fetch the latest version of the emsdk (not needed the first time you clone)git pull// Download and install the latest SDK tools../emsdk install latest// Make the "latest" SDK "active" for the current user. (writes .emscripten file)./emsdk activate latest// Activate PATH and other environment variables in the current terminalsource ./emsdk_env.sh
如果一切顺利的话, 安装工作就完成了。
3. Hello World
让我们尝试一个简单的例子, 以确保我们安装的环境能正常工作。
使用 Emscripten 进行编译时有许多选项,但最主要的场景有两个:
- 编译到 Wasm 并创建相应的 HTML 来运行我们的代码,再加上在 Web 环境中运行 Wasm 所需的所有 JavaScript "胶水" 代码。
 - 编译到 Wasm, 然后创建 JavaScript.
 
3.1 创建 HTML 和相应的 JavaScript 代码
- 首先,我们需要一段 c 代码来编译. 例如:
 
#include <stdio.h>int main() {    printf("Hello World\n");    return 0;}
- 解析来在命令行中使用 emsdk 来编译上面的代码:
 
emcc hello.c -o hello.html
-o html.html 是告诉 Emscripten 生成可以运行我们代码的 HTML 页面, 包括 Javscript 胶水语言和 Wasm 模块.
成功执行上述命令之后, 当前目录中的文件应该看起来像这样子:
hello.wasm: Wasm 二进制代码模块hello.js: 包含用于在原生 C 函数和 JavaScript/Wasm 之间翻译的粘合代码hello.html: 一个用于加载、编译和实例化 Wasm 代码的 HTML 文件,并在浏览器中显示其输出
如何运行该程序
你需要将生成的代码部署到一个 http 服务器上, 然后从一个支持 WebAssembly 的浏览中加载该页面,便可以运行。
不要直接使用本地浏览器打开该 html 文件,这样的话它将不工作,你可能会遇到这个错误: both async and sync fetching of the wasm failed
关于如何搭建一个本地测试 http 服务器,请参考: 如何搭建一个本地测试 http 服务器
3.2 编译 C 代码给 JavaScript 调用
如果你想在 JavaScript 中调用 C 代码中定义的函数,可以使用 Emscripten ccall() 函数和 EMSCRIPTEN_KEEPALIVE 声明,这会将你的函数添加到导出的函数列表中(exported functions)(参考 Why do functions in my C/C++ source code vanish when I compile to WebAssembly).
让我们看看怎么做.
- 创建一个新目录,在目录中创建 
hello3.c, 内容如下: 
#include <stdio.h>#include <emscripten/emscripten.h>int main() {    printf("Hello World\n");    return 0;}#ifdef __cplusplus#define EXTERN extern "C"#else#define EXTERN#endifEXTERN EMSCRIPTEN_KEEPALIVE void myFunction(int argc, char ** argv) {    printf("MyFunction Called\n");}
默认情况下,Emscripten 生成的代码始终只调用 main 函数,其他函数将作为死代码被删除。将 EMSCRIPTEN_KEEPALIVE 放在函数名称之前可以防止这种情况发生。
这里需要导入 emscripten.h 库才能使用 EMSCRIPTEN_KEEPALIVE。
- 创建 
html_template/shell_minimal.html, 文件内容是{{{ SCRIPT }}}. - 使用如下命令进行编译。
 
emcc -o hello3.html hello3.c --shell-file html_template/shell_minimal.html -s NO_EXIT_RUNTIME=1 -s "EXPORTED_RUNTIME_METHODS=['ccall']"
NO_EXIT_RUNTIME=1: 我们需要这个编译选项,否则当 main 函数退出时, runtime 将会被关闭,我们将无法再调用其他方法.
- 打开你的 
hello3.html文件,并编辑. - 在第一个 
<script type="text/javascript">之前添加一个<button>元素,如下: 
<button id="my-button">Run myFunction</button>
- 在第一个 
<script>元素的末尾添加以下代码: 
document.getElementById("my-button").addEventListener("click", () => {  alert("check console");  const result = Module.ccall(    "myFunction", // name of C function    null, // return type    null, // argument types    null, // arguments  );});
上述代码展示了如何使用 ccall 调用 c 语言中到处的方法.