使用 Emscripten 进行交叉编译 - WebAssembly 和 asm.js¶
本示例演示了如何使用 Emscripten 和 Conan 交叉编译一个简单的 C++ 项目。
Conan 支持 WASM 交叉编译,让您能够灵活地针对浏览器中的不同 JavaScript/WebAssembly 运行时。
我们建议为每个目标创建单独的 Conan profile。以下是推荐的 profiles 和使用它们进行构建的说明。
为 WebAssembly (WASM) 设置 Conan profile¶
[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) 是一种二进制格式,加载和执行速度更快,文件也更小。大多数现代浏览器都支持它,并且通常推荐用于新项目。与 asm.js 相比,WASM 也更容易与原生浏览器 API 集成。
尽管 Emscripten 不是一个真正的运行时环境(如 Linux 或 Windows),但它是一个工具链生态系统的一部分,该生态系统将 C/C++ 编译为 WebAssembly (WASM) 和 asm.js。
Conan 使用 os=Emscripten 来
对齐工具链:Emscripten 集成了编译器、运行时粘合代码和 JavaScript 环境,因此将其视为“类 OS”目标是可行的。
支持向后兼容:Conan Center Index 中的许多配方都使用
os=Emscripten来启用或禁用特定于 Emscripten 的功能和依赖项。保持稳定性:更改此设置会破坏依赖它的配方,并使与替代 WASM 工具链的兼容性复杂化。
注意
wasm arch 指的是 WASM 32-bit 目标架构,这是默认设置。如果您想针对 WASM64,请在您的 profile 中将 arch=wasm64。请注意,WASM64 仍处于实验阶段,需要 Node.js v20+ 和支持它的浏览器。
重要
根据 emscripten 文档,Emscripten 支持两种多线程 API:
POSIX 线程 API(conan profile 中的
posix)Wasm Workers API(conan profile 中的
wasm_workers)
这两种 API 彼此不兼容,并且与未启用多线程支持而编译的二进制文件不兼容。这种不兼容性使得有必要在编译器的二进制模型中模拟多线程的使用,从而使 conan 能够区分已启用多线程编译和未启用多线程编译的二进制文件。
如果配置文件中已配置多线程,Conan 将自动设置编译器和链接器标志以启用多线程。
上述 profiles 使用 Conan Center Index 仓库 中的 emsdk 包,该包提供了 Emscripten SDK,包括 emcc、em++ 以及 emrun 和 node 等工具。
如果您希望使用系统中安装的 Emscripten 而不是 Conan 提供的 Emscripten,可以将 tool_requires 替换为自定义的 compiler_executables 和 buildenv。
[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 本地安装,以便从 CMake、Meson、Autotools 或其他构建系统使用。
在某些情况下,您可能还需要 Emscripten.cmake 工具链文件来处理高级场景。此工具链已包含在我们的打包 emsdk 中,但如果您使用的是自己的 Emscripten 安装,则可以通过使用 tools.cmake.cmaketoolchain:user_toolchain 并提供工具链文件的绝对路径,在 profile 中指定它。
注意
先前 profiles 中的 tools.build:exelinkflags 和 tools.build:sharedlinkflags 是建议设置,但用户可以修改它们,或者在 CMakeLists.txt 文件中使用 set_target_properties() 命令定义它们的值。
通过启用
ALLOW_MEMORY_GROWTH,我们允许运行时通过调用emscripten_resize_heap()在运行时动态地增长其内存。没有此标志,内存将在启动时分配,并且无法增长。MAXIMUM_MEMORY和 `INITIAL_MEMORY` 值指定了 Emscripten 运行时的最大和初始内存大小。这些值可以根据您的应用程序需求进行调整。请注意,
arch=wasm64理论上的最大内存大小为 exabytes,但目前运行时将其限制为 16GB,而arch=wasm32的最大内存大小为 4GB,arch=asm.js的最大内存大小为 2GB。
重要
emcc 编译器不保证不同版本(包括补丁版本)之间存在任何 ABI 兼容性。为确保在 Emscripten 版本更改时生成新的 package_id,建议相应地更新 profile 中的 compiler.version 设置。
这将确保 package ID 基于 Emscripten 版本生成,从而允许 Conan 检测 Emscripten 工具链的变化并相应地重新构建项目。