|
| 1 | +<!-- |
| 2 | +SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]> |
| 3 | +SPDX-FileContributor: Matt Aber <[email protected]> |
| 4 | +
|
| 5 | +SPDX-License-Identifier: MIT OR Apache-2.0 |
| 6 | +--> |
| 7 | + |
| 8 | +# Building for WebAssembly |
| 9 | + |
| 10 | +CXX-Qt and applications written with it can be compiled for WebAssembly, with a few limitations. Below you will find detailed instructions regarding how to build for the WASM target. |
| 11 | + |
| 12 | +You will need to have Qt for WebAssembly installed. The next section shows versions that have been tested. |
| 13 | + |
| 14 | +Additionally, if you haven't already, clone the `emsdk` git repo from [here](https://github.com/emscripten-core/emsdk). |
| 15 | + |
| 16 | +## Using Correct Versions |
| 17 | + |
| 18 | +The version of Emscripten used to build CXX-Qt and programs using it should match the one that was used to build Qt for WebAssembly. This is because Emscripten does not guarantee ABI compatibility between versions, so using different versions is not guaranteed to work fully or even at all. |
| 19 | + |
| 20 | +Here are the associated Qt and Emscripten versions, and whether they are currently working with CXX-Qt for WebAssembly: |
| 21 | + |
| 22 | +Qt|Emscripten |
| 23 | +-|- |
| 24 | +6.2|2.0.14 |
| 25 | +6.3|3.0.0 |
| 26 | +6.4|3.1.14 |
| 27 | +6.5|3.1.25 |
| 28 | +6.6|3.1.37 |
| 29 | + |
| 30 | +Info about other Qt and emscripten versions can be found in the [Qt documentation](https://doc.qt.io/qt-6/wasm.html). |
| 31 | + |
| 32 | +## Setting Up `emsdk` |
| 33 | + |
| 34 | +Once you know which Qt and Emscripten versions you will use, navigate to the root directory of the `emsdk` repo and run the following commands: |
| 35 | + |
| 36 | +```bash |
| 37 | +$ ./emsdk install <emscripten version> |
| 38 | +$ ./emsdk activate <emscripten version> |
| 39 | +$ source ./emsdk_env.sh |
| 40 | +``` |
| 41 | + |
| 42 | +For example, if you are going to use Qt 6.4, the corresponding version of Emscripten is 3.1.14, so the first command will be: |
| 43 | + |
| 44 | +```bash |
| 45 | +$ ./emsdk install 3.1.14 |
| 46 | +``` |
| 47 | + |
| 48 | +On Windows, the third step, which sets up environment variables (`source` command above on Unix-like environments) is unnecessary because the required environment setup will already be done. |
| 49 | + |
| 50 | +## Toolchains |
| 51 | + |
| 52 | +When configuring with CMake, the `CMAKE_TOOLCHAIN_FILE` variable needs to be set to the correct toolchain file; for example, if using Qt 6.4.2 on WebAssembly, the toolchain file is typically located at `/path/to/Qt/6.4.2/wasm_32/lib/cmake/Qt6/qt.toolchain.cmake`. This will set CMake up to use the correct Qt path, compiler, linker, and so forth. |
| 53 | + |
| 54 | +Generally, this does not need to be done manually. Using the `qt-cmake` binary bundled with your selected version of Qt WASM will set the toolchain file for you. |
| 55 | + |
| 56 | +For example, if using Qt 6.4.2: |
| 57 | + |
| 58 | +```bash |
| 59 | +$ /path/to/Qt/6.4.2/wasm_32/bin/qt-cmake -B build . |
| 60 | +``` |
| 61 | + |
| 62 | +However, in Qt 6.3 and below, the bundled CMake is version 3.22, while CXX-Qt requires at least version 3.24. For these versions of Qt, a more up-to-date CMake binary needs to be used to configure, so `CMAKE_TOOLCHAIN_FILE` needs to be passed into the `cmake` command. |
| 63 | + |
| 64 | +If using a different CMake binary, instead do this: |
| 65 | + |
| 66 | +```bash |
| 67 | +$ cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/qt.toolchain.cmake -B build . |
| 68 | +``` |
| 69 | + |
| 70 | +For Qt 6.5 or later, use the `wasm_singlethread` toolchain. For versions earlier than 6.5 use `wasm_32`. |
| 71 | + |
| 72 | +The `wasm_multithread` toolchain available in 6.5 and later is currently not supported. For more information, see the [Known Issues](#known-issues) section at the bottom of this page. |
| 73 | + |
| 74 | +## Compiling Your Project for WebAssembly |
| 75 | + |
| 76 | +To build for WebAssembly in a project that uses CXX-Qt crates, first follow the instructions in the [Using Correct Versions](#using-correct-versions) and [Setting Up `emsdk`](#setting-up-emsdk) sections. |
| 77 | + |
| 78 | +### CMakeLists.txt |
| 79 | + |
| 80 | +When compiling a CXX-Qt project for wasm, the Rust target must be set to `wasm32-unknown-emscripten`, and the project must be configured to use POSIX threads. Make sure you have the Emscripten target for `rustc` with `rustup target add wasm-unknown-emscripten`. |
| 81 | + |
| 82 | +```cmake |
| 83 | +set(Rust_CARGO_TARGET wasm32-unknown-emscripten) |
| 84 | +set(THREADS_PREFER_PTHREAD_FLAG ON) |
| 85 | +find_package(Threads REQUIRED) |
| 86 | +``` |
| 87 | + |
| 88 | +Using CMake, `add_executable` will not output an HTML file when targeting wasm. In order to render an HTML file, one must use `qt_add_executable` in its place. Assuming a project has a CMake flag `BUILD_WASM` to toggle wasm and native builds, one could write the following: |
| 89 | + |
| 90 | +```cmake |
| 91 | +if(BUILD_WASM) |
| 92 | + qt_add_executable(${APP_NAME} ${SOURCE_FILES}) |
| 93 | +else() |
| 94 | + add_executable(${APP_NAME} ${SOURCE_FILES}) |
| 95 | +endif() |
| 96 | +``` |
| 97 | + |
| 98 | +### Configure, Build, and Run |
| 99 | + |
| 100 | +Configure your build directory following the instructions in the [Toolchains](#toolchains) section. |
| 101 | + |
| 102 | +Now, run `cmake --build` on the build directory to compile and link the project. This can be any CMake binary; here the OS package works just fine: |
| 103 | + |
| 104 | +```bash |
| 105 | +$ cmake --build build |
| 106 | +``` |
| 107 | + |
| 108 | +You can then run your built application like so: |
| 109 | + |
| 110 | +```bash |
| 111 | +$ emrun ./build/path/to/<appname>.html |
| 112 | +``` |
| 113 | + |
| 114 | +## Compiling CXX-Qt WASM from Source |
| 115 | + |
| 116 | +If you are compiling CXX-Qt from source, the workflow is similar. First, follow the instructions in the [Using Correct Versions](#using-correct-versions) and [Setting Up `emsdk`](#setting-up-emsdk) sections. |
| 117 | + |
| 118 | +The `CMakeLists.txt` file at the root of the CXX-Qt repository has an option `BUILD_WASM` to toggle WebAssembly builds. Simply compiling with the correct emsdk and toolchain and flipping this option `ON` should build the libraries and examples for WebAssembly. |
| 119 | + |
| 120 | +### Building |
| 121 | + |
| 122 | +Read the [Toolchains](#toolchains) section before proceeding. Then navigate to the root directory of the CXX-Qt repo. |
| 123 | + |
| 124 | +If using the `qt-cmake` binary packaged with your version of Qt for WebAssembly, run the following command to configure CXX-Qt: |
| 125 | + |
| 126 | +```bash |
| 127 | +$ /path/to/qt-cmake -DBUILD_WASM=ON -B build . |
| 128 | +``` |
| 129 | + |
| 130 | +If using a different CMake binary, instead do this: |
| 131 | + |
| 132 | +```bash |
| 133 | +$ <cmake binary> -DCMAKE_TOOLCHAIN_FILE=/path/to/qt.toolchain.cmake -DBUILD_WASM=ON -B build . |
| 134 | +``` |
| 135 | + |
| 136 | +Finally, run `cmake --build` on the configured build directory to compile and link the project and examples. This can be any CMake binary; here the OS package works just fine: |
| 137 | + |
| 138 | +```bash |
| 139 | +$ cmake --build build |
| 140 | +``` |
| 141 | + |
| 142 | +Then you can run the `qml_minimal` example like so: |
| 143 | + |
| 144 | +```bash |
| 145 | +$ emrun ./build/examples/qml_minimal/example_qml_minimal.html |
| 146 | +``` |
| 147 | + |
| 148 | +### Working Examples |
| 149 | + |
| 150 | +Not all of the examples are currently supported for WASM builds. |
| 151 | + |
| 152 | +Example|Working |
| 153 | +-|- |
| 154 | +`qml-minimal-no-cmake`|❌ broken |
| 155 | +`demo_threading`|❌ broken |
| 156 | +`qml_features`|✅ working |
| 157 | +`qml_minimal`|✅ working |
| 158 | + |
| 159 | +For more information, see the [Known Issues](#known-issues) section at the bottom of this page. |
| 160 | + |
| 161 | +## Known Issues |
| 162 | + |
| 163 | +### `wasm_multithread` toolchain |
| 164 | + |
| 165 | +CXX-Qt will currently not build with `wasm_multithread` versions of Qt. |
| 166 | + |
| 167 | +```console |
| 168 | +wasm-ld: error: --shared-memory is disallowed by qml_minimal-e6f36338b0e1fa5c.17g6vcid2nczsjj0.rcgu.o |
| 169 | + because it was not compiled with 'atomics' or 'bulk-memory' features. |
| 170 | +``` |
| 171 | + |
| 172 | +This issue is related to `pthread` in the `libc` crate. It is possible that manually compiling `cxx` and `libc` crates with `-pthread` may solve this. |
| 173 | + |
| 174 | +### `cargo`-only builds |
| 175 | + |
| 176 | +The example `qml-minimal-no-cmake` will not build for WebAssembly with `cargo`, and attempts to build with `cargo` without `cmake` will not work. This is due to an upstream issue with the `libc` crate, which does not support wasm and can cause breakage. |
| 177 | + |
| 178 | +```console |
| 179 | +cannot find function `pthread_kill` in crate `libc` |
| 180 | +``` |
| 181 | + |
| 182 | +### `demo_threading` example |
| 183 | + |
| 184 | +The example `demo_threading` will not build for WebAssembly due to an upstream issue with `async-std`, which does not support wasm. On Linux, the observed breakage is due to `socket2` using its `unix.rs` file to target a Unix rather than wasm environment, resulting in error messages from `unix.rs` like the following: |
| 185 | + |
| 186 | +```console |
| 187 | +error[E0433]: failed to resolve: use of undeclared type `IovLen` |
| 188 | +``` |
| 189 | + |
| 190 | +`socket2` is a dependency of `async-io`, which is a dependency of `async-std`. |
| 191 | + |
| 192 | +There is discussion around supporting wasm in the GitHub repository for `async-std`, and the progress is being tracked [here](https://github.com/async-rs/async-std/issues/220). |
0 commit comments