使用 Emscripten 进行交叉构建 - WebAssembly 和 asm.js

本示例演示了如何使用 Emscripten 和 Conan 交叉构建一个简单的 C++ 项目。

Conan 支持 WASM 交叉编译,让您能够灵活地针对不同的 JavaScript/WebAssembly 运行时环境在浏览器中进行目标定位。

我们建议为每个目标创建单独的 Conan 配置文件。以下是推荐的配置文件和有关如何使用它们的说明。

设置 WebAssembly (WASM) 的 Conan 配置文件

[settings]
arch=wasm
build_type=Release
compiler=emcc
compiler.cppstd=17
compiler.libcxx=libc++
# Optional settings to enable multithreading (see note below)
# compiler.threads=posix
compiler.version=4.0.10
os=Emscripten

[tool_requires]
emsdk/4.0.10

[conf]
# Optional settings to enable memory allocation
tools.build:exelinkflags=['-sALLOW_MEMORY_GROWTH=1', '-sMAXIMUM_MEMORY=4GB', '-sINITIAL_MEMORY=64MB']
tools.build:sharedlinkflags=['-sALLOW_MEMORY_GROWTH=1', '-sMAXIMUM_MEMORY=4GB', '-sINITIAL_MEMORY=64MB']

注意

Conan 还支持构建 asm.js 目标,这在今天被认为已过时。

asm.js 和 WASM 有什么区别?

  • asm.js 是针对速度优化的 JavaScript 子集。它完全受所有浏览器(甚至旧版本)支持,并编译成一个大型 .js 文件。

  • WebAssembly (WASM) 是一种二进制格式,加载和执行速度更快,体积更小。大多数现代浏览器都支持它,通常建议用于新项目。WASMasm.js 相比,也更容易与原生浏览器 API 集成。

即使 Emscripten 不是真正的运行时环境(如 Linux 或 Windows),它也是一个工具链生态系统的一部分,可以将 C/C++ 编译为 WebAssembly (WASM) 和 asm.js。

Conan 使用 os=Emscripten

  • 与工具链对齐:Emscripten 集成了编译器、运行时胶水和 JavaScript 环境,使其可以实际作为“类似操作系统”的目标进行处理。

  • 支持向后兼容性:Conan Center Index 中的许多配方使用 os=Emscripten 来启用或禁用专门针对 Emscripten 的功能和依赖项。

  • 保持稳定性:更改此设置会破坏依赖于它的配方,并会使与替代 WASM 工具链的兼容性复杂化。

注意

wasm arch 指的是 WASM 32 位 目标架构,这是默认设置。如果您希望针对 WASM64,请在您的配置文件中设置 arch=wasm64请注意,WASM64 仍然是实验性的,需要 Node.js v20+ 和支持它的浏览器。

重要

根据 emscripten 文档,Emscripten 支持两种多线程 API

  • POSIX 线程 API (posix 在 conan 配置文件中)

  • Wasm Workers API (wasm_workers 在 conan 配置文件中)

这两个 API 彼此不兼容,并且与不带线程支持编译的二进制文件不兼容。这种不兼容性需要在编译器的二进制模型中对线程使用进行建模,从而允许 conan 区分使用线程编译的二进制文件和不使用线程编译的二进制文件。

如果配置文件中配置了线程,Conan 将自动设置编译器和链接器标志以启用线程。

上述配置文件使用来自 Conan Center Index 仓库emsdk 包,该包提供 Emscripten SDK,包括 emccem++ 以及 emrunnode 等工具。

如果您希望使用系统安装的 Emscripten 而不是 Conan 提供的 Emscripten,则可以将 tool_requires 替换为自定义 compiler_executablesbuildenv

[conf]
tools.build:compiler_executables={'c':'/path/to/emcc', 'cpp':'/path/to/em++'}

[buildenv]
CC=emcc
CXX=em++
AR=emar
NM=emnm
RANLIB=emranlib
STRIP=emstrip

这样 conan 就可以配置 emsdk 本地安装,以便从 CMakeMesonAutotools 或其他构建系统中使用。

在某些情况下,您可能还需要 Emscripten.cmake 工具链文件来实现高级场景。此工具链已包含在我们的打包 emsdk 中,但如果您正在使用自己的 Emscripten 安装,则可以使用 tools.cmake.cmaketoolchain:user_toolchain 并提供指向您工具链文件的绝对路径来在配置文件中指定它。

注意

先前配置文件中的 tools.build:exelinkflagstools.build:sharedlinkflags 仅为建议,用户可以修改它们或在 CMakeLists.txt 文件中使用 set_target_properties() 命令定义它们的值。

  • 通过启用 ALLOW_MEMORY_GROWTH,我们允许运行时通过调用 emscripten_resize_heap() 在运行时动态增长其内存。如果没有此标志,则内存将在启动时分配,并且无法增长。

  • MAXIMUM_MEMORYINITIAL_MEMORY 值指定 Emscripten 运行时的最大和初始内存大小。可以根据您的应用程序的需求调整这些值。

    请注意,arch=wasm64 具有理论上的 exabytes 最大内存大小,但运行时当前将其限制为 16GB,而 arch=wasm32 具有 4GB 的最大内存大小,arch=asm.js 具有 2GB 的最大内存大小。

重要

emcc 编译器不能保证不同版本(包括补丁)之间的 ABI 兼容性。为了确保在 Emscripten 版本发生更改时生成新的 package_id,建议相应地更新您配置文件中的 compiler.version 设置。

这将确保基于 Emscripten 版本生成软件包 ID,从而允许 Conan 检测工具链中的更改并相应地重建项目。