CXX-Qt 及其编写的应用程序可以编译为 WebAssembly,但存在一些限制。以下是关于如何为 WASM 目标构建的详细说明。
你需要安装 Qt for WebAssembly。下一篇将展示已测试的版本。
此外,如果尚未完成,请从此处克隆 emsdk git 仓库。
使用正确的版本
用于构建 CXX-Qt 及其程序的 Emscripten 版本应与用于构建 Qt for WebAssembly 的版本匹配。这是因为 Emscripten 不保证不同版本之间的 ABI 兼容性,因此使用不同版本不能保证完全正常工作,甚至可能根本无法工作。
以下是相关的 Qt 和 Emscripten 版本,以及它们当前是否与 CXX-Qt for WebAssembly 兼容:
Qt 版本 | Emscripten 版本 |
---|---|
6.2 | 2.0.14 |
6.3 | 3.0.0 |
6.4 | 3.1.14 |
6.5 | 3.1.25 |
6.6 | 3.1.37 |
设置 emsdk
确定要使用的 Qt 和 Emscripten 版本后,导航到 emsdk 仓库的根目录并运行以下命令:
$ ./emsdk install <emscripten 版本>
$ ./emsdk activate <emscripten 版本>
$ source ./emsdk_env.sh
例如,如果你要使用 Qt 6.4,对应的 Emscripten 版本是 3.1.14,因此第一条命令将是:
$ ./emsdk install 3.1.14
在 Windows 上,第三步(设置环境变量,类似于 Unix 环境中的 source 命令)是不必要的,因为所需的环境设置已经完成。
工具链
使用 CMake 配置时,需要将 CMAKE_TOOLCHAIN_FILE 变量设置为正确的工具链文件。例如,如果在 WebAssembly 上使用 Qt 6.4.2,工具链文件通常位于 /path/to/Qt/6.4.2/wasm_32/lib/cmake/Qt6/qt.toolchain.cmake。这将设置 CMake 使用正确的 Qt 路径、编译器、链接器等。
通常,这不需要手动完成。使用与所选 Qt WASM 版本捆绑的 qt-cmake 二进制文件将自动为你设置工具链文件。
例如,如果使用 Qt 6.4.2:
$ /path/to/Qt/6.4.2/wasm_32/bin/qt-cmake -B build .
然而,在 Qt 6.3 及以下版本中,捆绑的 CMake 版本为 3.22,而 CXX-Qt 至少需要 3.24 版本。对于这些 Qt 版本,需要使用更新的 CMake 二进制文件进行配置,因此需要将 CMAKE_TOOLCHAIN_FILE 传递给 cmake 命令。
如果使用不同的 CMake 二进制文件,请执行以下操作:
$ cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/qt.toolchain.cmake -B build .
对于 Qt 6.5 或更高版本,请使用 wasm_singlethread 工具链。对于早于 6.5 的版本,请使用 wasm_32。
Qt 6.5 及更高版本中提供的 wasm_multithread 工具链目前不受支持。更多信息请参阅本页底部的“已知问题”部分。
为 WebAssembly 编译项目
要为使用 CXX-Qt crate 的项目构建 WebAssembly,请首先按照“使用正确的版本”和“设置 emsdk”部分中的说明进行操作。
CMakeLists.txt
在为 wasm 编译 CXX-Qt 项目时,必须将 Rust 目标设置为 wasm32-unknown-emscripten,并且项目必须配置为使用 POSIX 线程。确保你已经通过 rustup target add wasm32-unknown-emscripten 安装了 Emscripten 目标。
set(Rust_CARGO_TARGET wasm32-unknown-emscripten)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
使用 CMake 时,add_executable 在针对 wasm 时不会输出 HTML 文件。为了渲染 HTML 文件,必须使用 qt_add_executable 代替。假设项目有一个 CMake 标志 BUILD_WASM 用于切换 wasm 和本地构建,可以编写以下内容:
if(BUILD_WASM)qt_add_executable(${APP_NAME} ${SOURCE_FILES})
else()add_executable(${APP_NAME} ${SOURCE_FILES})
endif()
配置、构建和运行
按照“工具链”部分中的说明配置构建目录。
现在,在构建目录上运行 cmake --build 以编译和链接项目。这可以是任何 CMake 二进制文件;此处使用操作系统包即可:
$ cmake --build build
然后可以像这样运行构建的应用程序:
$ emrun ./build/path/to/<appname>.html
从源代码编译 CXX-Qt WASM
如果你是从源代码编译 CXX-Qt,工作流程类似。首先,按照“使用正确的版本”和“设置 emsdk”部分中的说明进行操作。
CXX-Qt 仓库根目录中的 CMakeLists.txt 文件有一个选项 BUILD_WASM,用于切换 WebAssembly 构建。只需使用正确的 emsdk 和工具链编译并将此选项设置为 ON,即可为 WebAssembly 构建库和示例。
构建
在继续之前,请阅读“工具链”部分。然后导航到 CXX-Qt 仓库的根目录。
如果使用与 Qt for WebAssembly 捆绑的 qt-cmake 二进制文件,请运行以下命令来配置 CXX-Qt:
$ /path/to/qt-cmake -DBUILD_WASM=ON -B build .
如果使用不同的 CMake 二进制文件,请执行以下操作:
$ <cmake 二进制文件> -DCMAKE_TOOLCHAIN_FILE=/path/to/qt.toolchain.cmake -DBUILD_WASM=ON -B build .
最后,在配置的构建目录上运行 cmake --build 以编译和链接项目及示例。这可以是任何 CMake 二进制文件;此处使用操作系统包即可:
$ cmake --build build
然后可以像这样运行 qml_minimal 示例:
$ emrun ./build/examples/qml_minimal/example_qml_minimal.html
可用的示例
并非所有示例当前都支持 WASM 构建。
示例 | 是否可用 |
---|---|
qml-minimal-no-cmake | ❌ 不可用 |
demo_threading | ❌ 不可用 |
qml_features | ✅ 可用 |
qml_minimal | ✅ 可用 |
更多信息请参阅本页底部的“已知问题”部分。
已知问题
wasm_multithread 工具链
CXX-Qt 目前无法使用 wasm_multithread 版本的 Qt 构建。
wasm-ld: error: --shared-memory is disallowed by qml_minimal-e6f36338b0e1fa5c.17g6vcid2nczsjj0.rcgu.o because it was not compiled with 'atomics' or 'bulk-memory' features.
此问题与 libc crate 中的 pthread 有关。手动使用 -pthread 编译 cxx 和 libc crate 可能会解决此问题。
仅使用 cargo 构建
示例 qml-minimal-no-cmake 无法使用 cargo 为 WebAssembly 构建,尝试在没有 cmake 的情况下使用 cargo 构建将无法工作。这是由于 libc crate 的上游问题,它不支持 wasm 并可能导致构建失败。
cannot find function `pthread_kill` in crate `libc`
demo_threading 示例
示例 demo_threading 无法为 WebAssembly 构建,这是由于 async-std 的上游问题,它不支持 wasm。在 Linux 上,观察到的构建失败是由于 socket2 使用其 unix.rs 文件来针对 Unix 环境而不是 wasm 环境,导致来自 unix.rs 的错误消息如下:
error[E0433]: failed to resolve: use of undeclared type `IovLen`
socket2 是 async-io 的依赖项,而 async-io 又是 async-std 的依赖项。
有关在 async-std 的 GitHub 仓库中支持 wasm 的讨论正在进行中,进展可以在此处跟踪。