| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 1 | # Support for Emscripten Cross Build |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 2 | |
| Andrea Gelmini | b926ba6 | 2023-02-13 12:23:32 +0100 | [diff] [blame] | 3 | This subdirectory provides support for building LibreOffice as WASM, with the Emscripten toolchain. |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 4 | |
| Tor Lillqvist | 187d3b6 | 2023-02-06 11:57:15 +0200 | [diff] [blame] | 5 | You can build LibreOffice for WASM for two separate purposes: 1) |
| 6 | Either to produce a WASM binary of LibreOffice as such, using Qt5 for |
| 7 | its GUI, or 2) just compiling LibreOffice core ("LibreOffice |
| 8 | Technology") to WASM without any UI for use in other software that |
| 9 | provides the UI, like Collabora Online built as WASM. |
| Tor Lillqvist | 6f6b879 | 2023-02-02 13:38:02 +0200 | [diff] [blame] | 10 | |
| 11 | The first purpose was the original reason for the WASM port and this |
| 12 | document was originally written with that in mind. For the second |
| 13 | purpose, look towards the end of the document for the section |
| 14 | "Building headless LibreOffice as WASM for use in another product". |
| 15 | |
| 16 | ## Status of LibreOffice as WASM with Qt |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 17 | |
| Stephan Bergmann | 7d5c625 | 2024-07-26 11:17:36 +0200 | [diff] [blame] | 18 | Configure `--with-package-format=emscripten` to have `workdir/installation/LibreOffice/emscripten` |
| 19 | populated with just the relevant files from `instdir`. |
| 20 | |
| Jan-Marek Glogowski | 12a6b57c | 2022-01-10 04:50:03 +0100 | [diff] [blame] | 21 | The build generates a Writer-only LO build. You should be able to run either |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 22 | |
| Moritz Duge | c888329 | 2024-08-28 16:47:46 +0200 | [diff] [blame] | 23 | $ emrun --hostname 127.0.0.1 --serve_after_close workdir/installation/LibreOffice/emscripten/qt_soffice.html |
| 24 | $ emrun --hostname 127.0.0.1 --serve_after_close workdir/LinkTarget/Executable/qt_vcldemo.html |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 25 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 26 | REMINDER: Always start new tabs in the browser, reload might fail / cache! |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 27 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 28 | ## Setup for the LO WASM build (with Qt) |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 29 | |
| Stephan Bergmann | 77ed394 | 2025-11-21 14:19:21 +0100 | [diff] [blame] | 30 | We're using Qt 5.15.2 with Emscripten 4.0.10. There are a bunch of Qt patches |
| Moritz Duge | 4307043 | 2024-04-03 12:04:04 +0200 | [diff] [blame] | 31 | to fix the most grave bugs. Also there's rapid development in Emscripten, so |
| Andrea Gelmini | 38ed810 | 2024-04-05 14:19:25 +0200 | [diff] [blame] | 32 | using another version often causes arbitrary problems. |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 33 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 34 | - See below under Docker build for another build option |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 35 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 36 | ### Setup emscripten |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 37 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 38 | <https://emscripten.org/docs/getting_started/index.html> |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 39 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 40 | git clone https://github.com/emscripten-core/emsdk.git |
| Stephan Bergmann | 77ed394 | 2025-11-21 14:19:21 +0100 | [diff] [blame] | 41 | ./emsdk install 4.0.10 |
| 42 | ./emsdk activate 4.0.10 |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 43 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 44 | Example `bashrc` scriptlet: |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 45 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 46 | EMSDK_ENV=$HOME/Development/libreoffice/git_emsdk/emsdk_env.sh |
| 47 | [ -f "$EMSDK_ENV" ] && \. "$EMSDK_ENV" 1>/dev/null 2>&1 |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 48 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 49 | ### Setup Qt |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 50 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 51 | <https://doc.qt.io/qt-5/wasm.html> |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 52 | |
| Jan-Marek Glogowski | 12a6b57c | 2022-01-10 04:50:03 +0100 | [diff] [blame] | 53 | Most of the information from <https://doc.qt.io/qt-6/wasm.html> is still valid for Qt5; |
| 54 | generally the Qt6 WASM documentation is much better, because it incorporated many |
| 55 | information from the Qt Wiki. |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 56 | |
| Andrea Gelmini | 5ef2a7d | 2022-01-21 12:30:42 +0100 | [diff] [blame] | 57 | FWIW: Qt 5.15 LTS is not maintained publicly and Qt WASM has quite a few bugs. Most |
| Tor Lillqvist | 52c3744 | 2022-12-14 21:38:08 +0200 | [diff] [blame] | 58 | WASM fixes from Qt 6 are needed for Qt 5.15 too. Allotropia offers a Qt repository |
| 59 | with the necessary patches cherry-picked. |
| Jan-Marek Glogowski | 12a6b57c | 2022-01-10 04:50:03 +0100 | [diff] [blame] | 60 | |
| Moritz Duge | 4307043 | 2024-04-03 12:04:04 +0200 | [diff] [blame] | 61 | With "-opensource -confirm-license" you agree to the open source license. |
| 62 | |
| Tor Lillqvist | 52c3744 | 2022-12-14 21:38:08 +0200 | [diff] [blame] | 63 | git clone https://github.com/allotropia/qt5.git |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 64 | cd qt5 |
| Stephan Bergmann | 6e6451c | 2024-08-12 13:04:48 +0200 | [diff] [blame] | 65 | git checkout 5.15.2+wasm |
| Jan-Marek Glogowski | 12a6b57c | 2022-01-10 04:50:03 +0100 | [diff] [blame] | 66 | ./init-repository --module-subset=qtbase |
| Stephan Bergmann | 22ce8ed | 2024-05-28 15:16:32 +0200 | [diff] [blame] | 67 | ./configure -opensource -confirm-license -xplatform wasm-emscripten -feature-thread -prefix <whatever> QMAKE_CFLAGS+=-sSUPPORT_LONGJMP=wasm QMAKE_CXXFLAGS+=-sSUPPORT_LONGJMP=wasm |
| Jan-Marek Glogowski | 12a6b57c | 2022-01-10 04:50:03 +0100 | [diff] [blame] | 68 | make -j<CORES> module-qtbase |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 69 | |
| Stephan Bergmann | 6e6451c | 2024-08-12 13:04:48 +0200 | [diff] [blame] | 70 | Note that `5.15.2+wasm` is a branch that is expected to contain further fixes as they become |
| 71 | necessary. |
| 72 | |
| Stephan Bergmann | 146fafa | 2024-08-07 15:01:25 +0200 | [diff] [blame] | 73 | Do not include `-fwasm-exceptions` in the above `QMAKE_CXXFLAGS`, see |
| 74 | <https://emscripten.org/docs/api_reference/emscripten.h.html#c.emscripten_set_main_loop> "Note: |
| 75 | Currently, using the new Wasm exception handling and simulate_infinite_loop == true at the same time |
| 76 | does not work yet in C++ projects that have objects with destructors on the stack at the time of the |
| 77 | call." (Also see the EMSCRIPTEN-specific HACK in soffice_main, desktop/source/app/sofficemain.cxx, |
| 78 | for what we need to do to work around that.) |
| 79 | |
| Jan-Marek Glogowski | 12a6b57c | 2022-01-10 04:50:03 +0100 | [diff] [blame] | 80 | Optionally you can add the configure flag "-compile-examples". But then you also have to |
| 81 | patch at least mkspecs/wasm-emscripten/qmake.conf with EXIT_RUNTIME=0, otherwise they will |
| 82 | fail to run. In addition, building with examples will break with some of them, but at that |
| Moritz Duge | 4307043 | 2024-04-03 12:04:04 +0200 | [diff] [blame] | 83 | point Qt already works and also most examples. Or just skip them. Other interesting flags |
| 84 | might be "-nomake tests -no-pch -ccache". |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 85 | |
| Moritz Duge | 4307043 | 2024-04-03 12:04:04 +0200 | [diff] [blame] | 86 | Linking takes quite a long time, because emscripten-finalize rewrites the whole WASM files with |
| 87 | some options. This way the LO WASM possibly needs 64GB RAM. For faster link times add |
| 88 | "-s WASM_BIGINT=1", change to ASSERTIONS=1 and use -g3 to prevent rewriting the WASM file and |
| 89 | generating source maps (see emscripten.py, finalize_wasm, and avoid modify_wasm = True). This is |
| 90 | just needed for Qt examples, as LO already uses the correct flags! |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 91 | |
| Moritz Duge | 4307043 | 2024-04-03 12:04:04 +0200 | [diff] [blame] | 92 | It's needed to install Qt5 to the chosen prefix. Else LO won't find all needed files in the |
| 93 | right place. For installation you can do |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 94 | |
| Jan-Marek Glogowski | 12a6b57c | 2022-01-10 04:50:03 +0100 | [diff] [blame] | 95 | make -j<CORES> install |
| 96 | or |
| 97 | make -j8 -C qtbase/src install_subtargets |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 98 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 99 | Current Qt fails to start the demo webserver: <https://bugreports.qt.io/browse/QTCREATORBUG-24072> |
| 100 | |
| Jan-Marek Glogowski | 12a6b57c | 2022-01-10 04:50:03 +0100 | [diff] [blame] | 101 | Use `emrun --serve_after_close` to run Qt WASM demos. |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 102 | |
| Stephan Bergmann | e0178cf | 2024-08-18 15:45:26 +0200 | [diff] [blame] | 103 | Qt builds some 3rd-party libraries that it brings along (e.g., qt5/qtbase/src/3rdparty/freetype) and |
| 104 | compiles its own code against the C/C++ include files of those 3rd-party libraries. But when we |
| 105 | link LO, we link against our own versions of those libraries' archives (e.g., |
| 106 | workdir/UnpackedTarball/freetype/instdir/lib/libfreetype.a), not against the Qt ones (e.g., |
| 107 | $QT5DIR/lib/libqtfreetype.a). This mismatch between the include files that Qt is compiled against, |
| 108 | vs. the archive actually linked in, seems to not cause issues in practice. (If it did, we could |
| 109 | either try to make both Qt and LO link against e.g. -sUSE_FREETYPE from emscripten-ports, or we |
| Andrea Gelmini | 1565181 | 2024-08-19 07:53:53 +0200 | [diff] [blame] | 110 | could move Qt from a prerequisite to a proper external/qt5 LO module built during the LO build, and |
| Andrea Gelmini | 56c33b8 | 2024-08-19 07:53:12 +0200 | [diff] [blame] | 111 | hack its configuration to build against LO's external/freetype etc. The former approach, building Qt |
| Stephan Bergmann | e0178cf | 2024-08-18 15:45:26 +0200 | [diff] [blame] | 112 | with -sUSE_FREETYPE, is even tried in qtbase/src/gui/configure.json, but apparently fails for |
| 113 | reasons not studied further yet, cf. Qt's config.log.) |
| 114 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 115 | ### Setup LO |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 116 | |
| Jan-Marek Glogowski | 12a6b57c | 2022-01-10 04:50:03 +0100 | [diff] [blame] | 117 | `autogen.sh` is patched to use emconfigure. That basically sets various |
| 118 | environment vars, especially `EMMAKEN_JUST_CONFIGURE`, which will create the |
| 119 | correct output file names, checked by `configure` (`a.out`). |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 120 | |
| Jan-Marek Glogowski | 79365d6 | 2022-03-26 20:46:56 +0100 | [diff] [blame] | 121 | There's a distro config for WASM, but it just provides --host=wasm32-local-emscripten, which |
| 122 | should be enough setup. The build itself is a cross build and the cross-toolset just depends |
| 123 | on a minimal toolset (gcc, libc-dev, flex, bison); all else is build from source, because the |
| 124 | final result is not depending on the build system at all. |
| Thorsten Behrens | d7e5d19 | 2021-04-25 16:02:10 +0200 | [diff] [blame] | 125 | |
| 126 | Recommended configure setup is thusly: |
| 127 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 128 | * grab defaults |
| 129 | `--with-distro=LibreOfficeWASM32` |
| Thorsten Behrens | d7e5d19 | 2021-04-25 16:02:10 +0200 | [diff] [blame] | 130 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 131 | * local config |
| Moritz Duge | 4307043 | 2024-04-03 12:04:04 +0200 | [diff] [blame] | 132 | `QT5DIR=/dir/of/qt5/install/prefix` |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 133 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 134 | * if you want to use ccache on both sides of the build |
| Ilmari Lauhakangas | 14f8c60 | 2025-03-27 20:04:54 +0200 | [diff] [blame] | 135 | ``` |
| 136 | --with-build-platform-configure-options=--enable-ccache |
| 137 | --enable-ccache |
| 138 | ``` |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 139 | |
| Jan-Marek Glogowski | 12a6b57c | 2022-01-10 04:50:03 +0100 | [diff] [blame] | 140 | FWIW: it's also possible to build an almost static Linux LibreOffice by just using |
| 141 | --disable-dynloading --enable-customtarget-components. System externals are still |
| 142 | linked dynamically, but everything else is static. |
| 143 | |
| 144 | ### "Deploying" soffice.wasm |
| 145 | |
| Ilmari Lauhakangas | 14f8c60 | 2025-03-27 20:04:54 +0200 | [diff] [blame] | 146 | ``` |
| 147 | tar -chf wasm.tar --xform 's/.*program/lo-wasm/' instdir/program/soffice.* \ |
| 148 | instdir/program/qt* |
| 149 | ``` |
| Jan-Marek Glogowski | 12a6b57c | 2022-01-10 04:50:03 +0100 | [diff] [blame] | 150 | |
| Andrea Gelmini | 5ef2a7d | 2022-01-21 12:30:42 +0100 | [diff] [blame] | 151 | Your HTTP server needs to provide additional headers: |
| Jan-Marek Glogowski | 12a6b57c | 2022-01-10 04:50:03 +0100 | [diff] [blame] | 152 | * add_header Cross-Origin-Opener-Policy same-origin |
| 153 | * add_header Cross-Origin-Embedder-Policy require-corp |
| 154 | |
| 155 | The default html to use should be qt_soffice.html |
| 156 | |
| 157 | ### Debugging setup |
| 158 | |
| 159 | Since a few months you can use DWARF information embedded by LLVM into the WASM |
| 160 | to debug WASM in Chrome. You need to enable an experimental feature and install |
| 161 | an additional extension. The whole setup is described in: |
| 162 | |
| 163 | https://developer.chrome.com/blog/wasm-debugging-2020/ |
| 164 | |
| 165 | This way you don't need source maps (much faster linking!) and can resolve local |
| 166 | WASM variables to C++ names! |
| 167 | |
| 168 | Per default, the WASM debug build splits the DWARF information into an additional |
| 169 | WASM file, postfixed '.debug.wasm'. |
| 170 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 171 | ### Using Docker to cross-build with emscripten |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 172 | |
| 173 | If you prefer a controlled environment (sadly emsdk install/activate |
| 174 | is _not_ stable over time, as e.g. nodejs versions evolve), that is |
| 175 | easy to replicate across different machines - consider the docker |
| 176 | images we're providing. |
| 177 | |
| 178 | Config/setup file see |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 179 | <https://git.libreoffice.org/lode/+/ccb36979563635b51215477455953252c99ec013> |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 180 | |
| 181 | Run |
| 182 | |
| Ilmari Lauhakangas | 14f8c60 | 2025-03-27 20:04:54 +0200 | [diff] [blame] | 183 | ``` |
| 184 | docker-compose build |
| 185 | ``` |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 186 | |
| 187 | in the lode/docker dir to get the container prepared. Run |
| 188 | |
| Ilmari Lauhakangas | 14f8c60 | 2025-03-27 20:04:54 +0200 | [diff] [blame] | 189 | ``` |
| 190 | PARALLELISM=4 BUILD_OPTIONS= BUILD_TARGET=build docker-compose run --rm \ |
| 191 | -e PARALLELISM -e BUILD_TARGET -e BUILD_OPTIONS builder |
| 192 | ``` |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 193 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 194 | to perform an actual `srcdir != builddir` build; the container mounts |
| 195 | checked-out git repo and output dir via `docker-compose.yml` (so make |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 196 | sure the path names there match your setup): |
| 197 | |
| 198 | The lode setup expects, inside the lode/docker subdir, the following directories: |
| 199 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 200 | - core (`git checkout`) |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 201 | - workdir (the output dir - gets written into) |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 202 | - cache (`ccache tree`) |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 203 | - tarballs (external project tarballs gets written and cached there) |
| 204 | |
| Sarper Akdemir | 841f898 | 2023-08-07 14:41:32 +0300 | [diff] [blame] | 205 | ### UNO bindings with Embind |
| 206 | |
| 207 | Right now there's a very rough implementation in place. With lots of different |
| 208 | bits unimplemented. And it _might_ be leaking memory. i.e. Lots of room for |
| 209 | improvement! ;) |
| 210 | |
| 211 | Some usage examples through javascript of the current implementation: |
| 212 | ```js |
| 213 | // inserts a string at the start of the Writer document. |
| Stephan Bergmann | 91bedca | 2024-07-18 11:44:45 +0200 | [diff] [blame] | 214 | Module.uno_init.then(function() { |
| 215 | const css = Module.uno.com.sun.star; |
| Stephan Bergmann | dd332f7 | 2024-07-25 16:59:33 +0200 | [diff] [blame] | 216 | let xModel = Module.getCurrentModelFromViewSh(); |
| 217 | if (xModel === null || !css.text.XTextDocument.query(xModel)) { |
| 218 | const desktop = css.frame.Desktop.create(Module.getUnoComponentContext()); |
| 219 | const args = new Module.uno_Sequence_com$sun$star$beans$PropertyValue( |
| 220 | 0, Module.uno_Sequence.FromSize); |
| 221 | xModel = css.frame.XComponentLoader.query(desktop).loadComponentFromURL( |
| Stephan Bergmann | 56e620e | 2026-04-02 13:17:01 +0200 | [diff] [blame] | 222 | 'file:///TODO/example.odt', '_default', 0, args); |
| Stephan Bergmann | dd332f7 | 2024-07-25 16:59:33 +0200 | [diff] [blame] | 223 | args.delete(); |
| 224 | } |
| Stephan Bergmann | 91bedca | 2024-07-18 11:44:45 +0200 | [diff] [blame] | 225 | const xTextDocument = css.text.XTextDocument.query(xModel); |
| 226 | const xText = xTextDocument.getText(); |
| 227 | const xTextCursor = xText.createTextCursor(); |
| 228 | xTextCursor.setString("string here!"); |
| 229 | }); |
| Sarper Akdemir | 841f898 | 2023-08-07 14:41:32 +0300 | [diff] [blame] | 230 | ``` |
| 231 | |
| 232 | ```js |
| 233 | // changes each paragraph of the Writer document to a random color. |
| Stephan Bergmann | 91bedca | 2024-07-18 11:44:45 +0200 | [diff] [blame] | 234 | Module.uno_init.then(function() { |
| 235 | const css = Module.uno.com.sun.star; |
| Stephan Bergmann | dd332f7 | 2024-07-25 16:59:33 +0200 | [diff] [blame] | 236 | let xModel = Module.getCurrentModelFromViewSh(); |
| 237 | if (xModel === null || !css.text.XTextDocument.query(xModel)) { |
| 238 | const desktop = css.frame.Desktop.create(Module.getUnoComponentContext()); |
| 239 | const args = new Module.uno_Sequence_com$sun$star$beans$PropertyValue( |
| 240 | 0, Module.uno_Sequence.FromSize); |
| 241 | xModel = css.frame.XComponentLoader.query(desktop).loadComponentFromURL( |
| Stephan Bergmann | 56e620e | 2026-04-02 13:17:01 +0200 | [diff] [blame] | 242 | 'file:///TODO/example.odt', '_default', 0, args); |
| Stephan Bergmann | dd332f7 | 2024-07-25 16:59:33 +0200 | [diff] [blame] | 243 | args.delete(); |
| 244 | } |
| Stephan Bergmann | 91bedca | 2024-07-18 11:44:45 +0200 | [diff] [blame] | 245 | const xTextDocument = css.text.XTextDocument.query(xModel); |
| 246 | const xText = xTextDocument.getText(); |
| 247 | const xEnumAccess = css.container.XEnumerationAccess.query(xText); |
| 248 | const xParaEnumeration = xEnumAccess.createEnumeration(); |
| 249 | while (xParaEnumeration.hasMoreElements()) { |
| 250 | const next = xParaEnumeration.nextElement(); |
| 251 | const xParagraph = css.text.XTextRange.query(next.get()); |
| 252 | const xParaProps = css.beans.XPropertySet.query(xParagraph); |
| 253 | const color = new Module.uno_Any( |
| 254 | Module.uno_Type.Long(), Math.floor(Math.random() * 0xFFFFFF)); |
| 255 | xParaProps.setPropertyValue("CharColor", color); |
| 256 | next.delete(); |
| 257 | color.delete(); |
| 258 | } |
| 259 | }); |
| Sarper Akdemir | 841f898 | 2023-08-07 14:41:32 +0300 | [diff] [blame] | 260 | ``` |
| 261 | |
| Stephan Bergmann | cccc983 | 2024-08-14 16:54:49 +0200 | [diff] [blame] | 262 | If you enter the above examples into the browser console, you need to enter them into the console of |
| Stephan Bergmann | 9416baf | 2024-10-30 11:00:58 +0100 | [diff] [blame] | 263 | the first web worker thread, which is the LO main thread since we use -sPROXY_TO_PTHREAD, not |
| Stephan Bergmann | cccc983 | 2024-08-14 16:54:49 +0200 | [diff] [blame] | 264 | into the console of the browser's main thread. |
| Sarper Akdemir | 841f898 | 2023-08-07 14:41:32 +0300 | [diff] [blame] | 265 | |
| Stephan Bergmann | cccc983 | 2024-08-14 16:54:49 +0200 | [diff] [blame] | 266 | Alternatively, you can do the following: Put an example into some file like `example.js` that you |
| 267 | put next to the `qt_soffice.html` that you serve to the browser (i.e., in |
| 268 | `workdir/installation/LibreOffice/emscripten/`). Create another small JS snippet file like |
| 269 | `include.js` (which is only needed during the build) containing |
| 270 | ``` |
| 271 | Module.uno_scripts = ['./example.js']; |
| 272 | ``` |
| 273 | And rebuild LO configured with an additional |
| Stephan Bergmann | b88b5f5 | 2024-08-20 17:52:49 +0200 | [diff] [blame] | 274 | `EMSCRIPTEN_EXTRA_SOFFICE_PRE_JS=/...path-to.../include.js`. |
| Sarper Akdemir | 841f898 | 2023-08-07 14:41:32 +0300 | [diff] [blame] | 275 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 276 | ## Tools for problem diagnosis |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 277 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 278 | * `nm -s` should list the symbols in the archive, based on the index generated by ranlib. |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 279 | If you get linking errors that archive has no index. |
| 280 | |
| 281 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 282 | ## Emscripten filesystem access with threads |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 283 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 284 | This is closed, but not really fixed IMHO: |
| 285 | |
| 286 | - <https://github.com/emscripten-core/emscripten/issues/3922> |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 287 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 288 | ## Dynamic libraries `/` modules in emscripten |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 289 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 290 | There is a good summary in: |
| 291 | |
| 292 | - <https://bugreports.qt.io/browse/QTBUG-63925> |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 293 | |
| 294 | Summary: you can't use modules and threads. |
| 295 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 296 | This is mentioned at the end of: |
| 297 | |
| 298 | - <https://github.com/emscripten-core/emscripten/wiki/Linking> |
| 299 | |
| 300 | The usage of `MAIN_MODULE` and `SIDE_MODULE` has other problems, a major one IMHO is symbol resolution at runtime only. |
| 301 | So this works really more like plugins in the sense of symbol resolution without dependencies `/` rpath. |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 302 | |
| Andrea Gelmini | 5f10242 | 2021-05-05 23:38:21 +0200 | [diff] [blame] | 303 | There is some clang-level dynamic-linking in progress (WASM dlload). The following link is already a bit old, |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 304 | but I found it a god summary of problems to expect: |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 305 | |
| 306 | - <https://iandouglasscott.com/2019/07/18/experimenting-with-webassembly-dynamic-linking-with-clang/> |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 307 | |
| 308 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 309 | ## Mixed information, links, problems, TODO |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 310 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 311 | More info on Qt WASM emscripten pthreads: |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 312 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 313 | - <https://wiki.qt.io/Qt_for_WebAssembly#Multithreading_Support> |
| 314 | |
| 315 | WASM needs `-pthread` at compile, not just link time for atomics support. Alternatively you can provide |
| 316 | `-s USE_PTHREADS=1`, but both don't seem to work reliable, so best provide both. |
| 317 | <https://github.com/emscripten-core/emscripten/issues/10370> |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 318 | |
| 319 | The output file must have the prefix .o, otherwise the WASM files will get a |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 320 | `node.js` shebang (!) and ranlib won't be able to index the library (link errors). |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 321 | |
| 322 | Qt with threads has further memory limit. From Qt configure: |
| Ilmari Lauhakangas | 14f8c60 | 2025-03-27 20:04:54 +0200 | [diff] [blame] | 323 | ``` |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 324 | Project MESSAGE: Setting PTHREAD_POOL_SIZE to 4 |
| 325 | Project MESSAGE: Setting TOTAL_MEMORY to 1GB |
| Ilmari Lauhakangas | 14f8c60 | 2025-03-27 20:04:54 +0200 | [diff] [blame] | 326 | ``` |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 327 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 328 | You can actually allocate 4GB: |
| 329 | |
| 330 | - <https://bugzilla.mozilla.org/show_bug.cgi?id=1392234> |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 331 | |
| 332 | LO uses a nested event loop to run dialogs in general, but that won't work, because you can't drive |
| 333 | the browser event loop. like VCL does with the system event loop in the various VCL backends. |
| 334 | Changing this will need some major work (basically dropping Application::Execute). |
| 335 | |
| 336 | But with the know problems with exceptions and threads, this might change: |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 337 | |
| 338 | - <https://github.com/emscripten-core/emscripten/pull/11518> |
| 339 | - <https://github.com/emscripten-core/emscripten/issues/11503> |
| 340 | - <https://github.com/emscripten-core/emscripten/issues/11233> |
| 341 | - <https://github.com/emscripten-core/emscripten/issues/12035> |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 342 | |
| 343 | We're also using emconfigure at the moment. Originally I patched emscripten, because it |
| Andrea Gelmini | 5f10242 | 2021-05-05 23:38:21 +0200 | [diff] [blame] | 344 | wouldn't create the correct a.out file for C++ configure tests. Later I found that |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 345 | the `emconfigure` sets `EMMAKEN_JUST_CONFIGURE` to work around the problem. |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 346 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 347 | ICU bug: |
| 348 | |
| 349 | - <https://github.com/emscripten-core/emscripten/issues/10129> |
| 350 | |
| 351 | Alternative, probably: |
| 352 | |
| 353 | - <https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Intl> |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 354 | |
| 355 | There is a wasm64, but that still uses 32bit pointers! |
| 356 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 357 | Old outdated docs: |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 358 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 359 | - <https://wiki.documentfoundation.org/Development/Emscripten> |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 360 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 361 | Reverted patch: |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 362 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 363 | - <https://cgit.freedesktop.org/libreoffice/core/commit/?id=0e21f6619c72f1e17a7b0a52b6317810973d8a3e> |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 364 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 365 | Generally <https://emscripten.org/docs/porting>: |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 366 | |
| Hossein | d19299f | 2021-10-10 19:59:20 +0200 | [diff] [blame] | 367 | - <https://emscripten.org/docs/porting/guidelines/api_limitations.html#api-limitations> |
| 368 | - <https://emscripten.org/docs/porting/files/file_systems_overview.html#file-system-overview> |
| 369 | - <https://emscripten.org/docs/porting/pthreads.html> |
| 370 | - <https://emscripten.org/docs/porting/emscripten-runtime-environment.html> |
| 371 | |
| 372 | This will be interesting: |
| 373 | |
| 374 | - <https://emscripten.org/docs/getting_started/FAQ.html#how-do-i-run-an-event-loop> |
| 375 | |
| 376 | This didn't help much yet: |
| 377 | |
| 378 | - <https://github.com/emscripten-ports> |
| 379 | |
| 380 | Emscripten supports standalone WASI binaries: |
| 381 | |
| 382 | - <https://github.com/emscripten-core/emscripten/wiki/WebAssembly-Standalone> |
| 383 | - <https://www.qt.io/qt-examples-for-webassembly> |
| 384 | - <http://qtandeverything.blogspot.com/2017/06/qt-for-web-assembly.html> |
| 385 | - <http://qtandeverything.blogspot.com/2020/> |
| 386 | - <https://emscripten.org/docs/api_reference/Filesystem-API.html> |
| 387 | - <https://discuss.python.org/t/add-a-webassembly-wasm-runtime/3957/12> |
| 388 | - <http://git.savannah.gnu.org/cgit/config.git> |
| 389 | - <https://webassembly.org/specs/> |
| 390 | - <https://developer.chrome.com/docs/native-client/> |
| 391 | - <https://emscripten.org/docs/getting_started/downloads.html> |
| 392 | - <https://github.com/openpgpjs/openpgpjs/blob/master/README.md#getting-started> |
| 393 | - <https://developer.mozilla.org/en-US/docs/WebAssembly/Using_the_JavaScript_API> |
| 394 | - <https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-intro.md> |
| 395 | - <https://www.ip6.li/de/security/x.509_kochbuch/openssl-fuer-webassembly-compilieren> |
| 396 | - <https://emscripten.org/docs/introducing_emscripten/about_emscripten.html#about-emscripten-porting-code> |
| 397 | - <https://emscripten.org/docs/compiling/Building-Projects.html> |
| Jan-Marek Glogowski | 3dd7e77 | 2021-04-23 14:04:06 +0200 | [diff] [blame] | 398 | |
| Stephan Bergmann | 5b1df77 | 2024-07-24 17:26:46 +0200 | [diff] [blame] | 399 | ### Threads and the event loop |
| 400 | |
| 401 | The Emscripten emulation of pthreads requires the JS main thread event loop to be able to promptly |
| 402 | respond both when spawning and when exiting a pthread. But the Qt5 event loop runs on the JS main |
| 403 | thread, so the JS main thread event loop is blocked while a LO VCL Task is executed. And our |
| 404 | pthreads are typically spawned and joined from within such Task executions, which means that the JS |
| 405 | main thread event loop is not available to reliably perform those Emscripten pthread operations. |
| 406 | |
| 407 | For pthread spawning, the solution is to set -sPTHREAD_POOL_SIZE to a sufficiently large value, so |
| 408 | that each of our pthread spawning requests during an inappropriate time finds a pre-spawned JS |
| 409 | Worker available. |
| 410 | |
| 411 | There are patterns (like, at the time of writing this, the configmgr::Components::WriteThread) where |
| 412 | a pthread can get spawned and joined and then re-spawned (and re-joined) multiple times during a |
| 413 | single VCL Task execution (i.e., without the JS main thread event loop having a chance to get in |
| Andrea Gelmini | 566fbd5 | 2024-07-25 09:03:27 +0200 | [diff] [blame] | 414 | between any of those operations). But as the underlying Emscripten pthread exiting operations will |
| Stephan Bergmann | 5b1df77 | 2024-07-24 17:26:46 +0200 | [diff] [blame] | 415 | therefore queue up, the pthread spawning operations will eventually run out of -sPTHREAD_POOL_SIZE |
| 416 | pre-spawned JS Workers. The solution here is to change our pthread usage patterns accordingly, so |
| 417 | that such pthreads are rather kept running than being joined and re-spawned. |
| 418 | |
| 419 | (-sPROXY_TO_PTHREAD would move the Qt5 event loop off the JS main thread, which should elegantly |
| 420 | solve all of the above issues. But Qt5 just doesn't appear to be prepared to run on anything but |
| 421 | the JS main thread; e.g., it tries to access the global JS `window` object in various places, which |
| 422 | is available on the JS main thread but not in a JS Worker.) |
| 423 | |
| Tor Lillqvist | 6f6b879 | 2023-02-02 13:38:02 +0200 | [diff] [blame] | 424 | ## Building headless LibreOffice as WASM for use in another product |
| 425 | |
| 426 | ### Set up Emscripten |
| 427 | |
| 428 | Follow the instructions in the first part of this document. |
| 429 | |
| 430 | ### No Qt needed. |
| 431 | |
| Tor Lillqvist | 187d3b6 | 2023-02-06 11:57:15 +0200 | [diff] [blame] | 432 | You don't need any dependencies other than those that normally are |
| 433 | downloaded and compiled when building LibreOffice. |
| Tor Lillqvist | 6f6b879 | 2023-02-02 13:38:02 +0200 | [diff] [blame] | 434 | |
| 435 | ### Set up LO |
| 436 | |
| 437 | For instance, this autogen.input works for me: |
| 438 | |
| Ilmari Lauhakangas | 14f8c60 | 2025-03-27 20:04:54 +0200 | [diff] [blame] | 439 | ``` |
| 440 | --disable-debug |
| 441 | --enable-sal-log |
| 442 | --disable-crashdump |
| 443 | --host=wasm32-local-emscripten |
| 444 | --disable-gui |
| 445 | --with-wasm-module=writer |
| 446 | --with-package-format=emscripten |
| 447 | ``` |
| Tor Lillqvist | 6f6b879 | 2023-02-02 13:38:02 +0200 | [diff] [blame] | 448 | |
| Tor Lillqvist | 297c71c | 2023-02-13 11:04:30 +0200 | [diff] [blame] | 449 | For building LO core for use in COWASM, it is known to work to use |
| 450 | Emscripten 3.1.30 (and not just 2.0.31 which is what the LO+Qt5 work |
| Stephan Bergmann | 3172945 | 2024-03-06 12:49:43 +0100 | [diff] [blame] | 451 | has been using in the past). |
| Tor Lillqvist | 297c71c | 2023-02-13 11:04:30 +0200 | [diff] [blame] | 452 | |
| Tor Lillqvist | 6f6b879 | 2023-02-02 13:38:02 +0200 | [diff] [blame] | 453 | ### That's all |
| 454 | |
| 455 | After all, in this case you are building LO core headless for it to be used by other software. |
| Tor Lillqvist | 187d3b6 | 2023-02-06 11:57:15 +0200 | [diff] [blame] | 456 | |
| 457 | Note that a soffice.wasm will be built, but that is just because of |
| 458 | how the makefilery has been set up. We do need the soffice.data file |
| 459 | that contains the in-memory file system needed by the LibreOffice |
| 460 | Technology core code during run-time, though. That is at the moment |
| 461 | built as a side-effect when building soffice.wasm. |