2021-10-10 19:59:20 +02:00
|
|
|
# Support for Emscripten Cross Build
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2023-02-13 12:23:32 +01:00
|
|
|
This subdirectory provides support for building LibreOffice as WASM, with the Emscripten toolchain.
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2023-02-06 11:57:15 +02:00
|
|
|
You can build LibreOffice for WASM for two separate purposes: 1)
|
|
|
|
Either to produce a WASM binary of LibreOffice as such, using Qt5 for
|
|
|
|
its GUI, or 2) just compiling LibreOffice core ("LibreOffice
|
|
|
|
Technology") to WASM without any UI for use in other software that
|
|
|
|
provides the UI, like Collabora Online built as WASM.
|
2023-02-02 13:38:02 +02:00
|
|
|
|
|
|
|
The first purpose was the original reason for the WASM port and this
|
|
|
|
document was originally written with that in mind. For the second
|
|
|
|
purpose, look towards the end of the document for the section
|
|
|
|
"Building headless LibreOffice as WASM for use in another product".
|
|
|
|
|
|
|
|
## Status of LibreOffice as WASM with Qt
|
2021-10-10 19:59:20 +02:00
|
|
|
|
2024-07-26 11:17:36 +02:00
|
|
|
Configure `--with-package-format=emscripten` to have `workdir/installation/LibreOffice/emscripten`
|
|
|
|
populated with just the relevant files from `instdir`.
|
|
|
|
|
2022-01-10 04:50:03 +01:00
|
|
|
The build generates a Writer-only LO build. You should be able to run either
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2024-08-28 16:47:46 +02:00
|
|
|
$ emrun --hostname 127.0.0.1 --serve_after_close workdir/installation/LibreOffice/emscripten/qt_soffice.html
|
|
|
|
$ emrun --hostname 127.0.0.1 --serve_after_close workdir/LinkTarget/Executable/qt_vcldemo.html
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
REMINDER: Always start new tabs in the browser, reload might fail / cache!
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
## Setup for the LO WASM build (with Qt)
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2024-03-06 12:49:43 +01:00
|
|
|
We're using Qt 5.15.2 with Emscripten 3.1.46. There are a bunch of Qt patches
|
2024-04-03 12:04:04 +02:00
|
|
|
to fix the most grave bugs. Also there's rapid development in Emscripten, so
|
2024-04-05 14:19:25 +02:00
|
|
|
using another version often causes arbitrary problems.
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
- See below under Docker build for another build option
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
### Setup emscripten
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
<https://emscripten.org/docs/getting_started/index.html>
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
git clone https://github.com/emscripten-core/emsdk.git
|
2024-03-06 12:49:43 +01:00
|
|
|
./emsdk install 3.1.46
|
|
|
|
./emsdk activate 3.1.46
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
Example `bashrc` scriptlet:
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
EMSDK_ENV=$HOME/Development/libreoffice/git_emsdk/emsdk_env.sh
|
|
|
|
[ -f "$EMSDK_ENV" ] && \. "$EMSDK_ENV" 1>/dev/null 2>&1
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
### Setup Qt
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
<https://doc.qt.io/qt-5/wasm.html>
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2022-01-10 04:50:03 +01:00
|
|
|
Most of the information from <https://doc.qt.io/qt-6/wasm.html> is still valid for Qt5;
|
|
|
|
generally the Qt6 WASM documentation is much better, because it incorporated many
|
|
|
|
information from the Qt Wiki.
|
|
|
|
|
2022-01-21 12:30:42 +01:00
|
|
|
FWIW: Qt 5.15 LTS is not maintained publicly and Qt WASM has quite a few bugs. Most
|
2022-12-14 21:38:08 +02:00
|
|
|
WASM fixes from Qt 6 are needed for Qt 5.15 too. Allotropia offers a Qt repository
|
|
|
|
with the necessary patches cherry-picked.
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2024-04-03 12:04:04 +02:00
|
|
|
With "-opensource -confirm-license" you agree to the open source license.
|
|
|
|
|
2022-12-14 21:38:08 +02:00
|
|
|
git clone https://github.com/allotropia/qt5.git
|
2021-10-10 19:59:20 +02:00
|
|
|
cd qt5
|
2024-08-12 13:04:48 +02:00
|
|
|
git checkout 5.15.2+wasm
|
2022-01-10 04:50:03 +01:00
|
|
|
./init-repository --module-subset=qtbase
|
2024-05-28 15:16:32 +02:00
|
|
|
./configure -opensource -confirm-license -xplatform wasm-emscripten -feature-thread -prefix <whatever> QMAKE_CFLAGS+=-sSUPPORT_LONGJMP=wasm QMAKE_CXXFLAGS+=-sSUPPORT_LONGJMP=wasm
|
2022-01-10 04:50:03 +01:00
|
|
|
make -j<CORES> module-qtbase
|
|
|
|
|
2024-08-12 13:04:48 +02:00
|
|
|
Note that `5.15.2+wasm` is a branch that is expected to contain further fixes as they become
|
|
|
|
necessary.
|
|
|
|
|
2024-08-07 15:01:25 +02:00
|
|
|
Do not include `-fwasm-exceptions` in the above `QMAKE_CXXFLAGS`, see
|
|
|
|
<https://emscripten.org/docs/api_reference/emscripten.h.html#c.emscripten_set_main_loop> "Note:
|
|
|
|
Currently, using the new Wasm exception handling and simulate_infinite_loop == true at the same time
|
|
|
|
does not work yet in C++ projects that have objects with destructors on the stack at the time of the
|
|
|
|
call." (Also see the EMSCRIPTEN-specific HACK in soffice_main, desktop/source/app/sofficemain.cxx,
|
|
|
|
for what we need to do to work around that.)
|
|
|
|
|
2022-01-10 04:50:03 +01:00
|
|
|
Optionally you can add the configure flag "-compile-examples". But then you also have to
|
|
|
|
patch at least mkspecs/wasm-emscripten/qmake.conf with EXIT_RUNTIME=0, otherwise they will
|
|
|
|
fail to run. In addition, building with examples will break with some of them, but at that
|
2024-04-03 12:04:04 +02:00
|
|
|
point Qt already works and also most examples. Or just skip them. Other interesting flags
|
|
|
|
might be "-nomake tests -no-pch -ccache".
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2024-04-03 12:04:04 +02:00
|
|
|
Linking takes quite a long time, because emscripten-finalize rewrites the whole WASM files with
|
|
|
|
some options. This way the LO WASM possibly needs 64GB RAM. For faster link times add
|
|
|
|
"-s WASM_BIGINT=1", change to ASSERTIONS=1 and use -g3 to prevent rewriting the WASM file and
|
|
|
|
generating source maps (see emscripten.py, finalize_wasm, and avoid modify_wasm = True). This is
|
|
|
|
just needed for Qt examples, as LO already uses the correct flags!
|
2021-10-10 19:59:20 +02:00
|
|
|
|
2024-04-03 12:04:04 +02:00
|
|
|
It's needed to install Qt5 to the chosen prefix. Else LO won't find all needed files in the
|
|
|
|
right place. For installation you can do
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2022-01-10 04:50:03 +01:00
|
|
|
make -j<CORES> install
|
|
|
|
or
|
|
|
|
make -j8 -C qtbase/src install_subtargets
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
Current Qt fails to start the demo webserver: <https://bugreports.qt.io/browse/QTCREATORBUG-24072>
|
|
|
|
|
2022-01-10 04:50:03 +01:00
|
|
|
Use `emrun --serve_after_close` to run Qt WASM demos.
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2024-08-18 15:45:26 +02:00
|
|
|
Qt builds some 3rd-party libraries that it brings along (e.g., qt5/qtbase/src/3rdparty/freetype) and
|
|
|
|
compiles its own code against the C/C++ include files of those 3rd-party libraries. But when we
|
|
|
|
link LO, we link against our own versions of those libraries' archives (e.g.,
|
|
|
|
workdir/UnpackedTarball/freetype/instdir/lib/libfreetype.a), not against the Qt ones (e.g.,
|
|
|
|
$QT5DIR/lib/libqtfreetype.a). This mismatch between the include files that Qt is compiled against,
|
|
|
|
vs. the archive actually linked in, seems to not cause issues in practice. (If it did, we could
|
|
|
|
either try to make both Qt and LO link against e.g. -sUSE_FREETYPE from emscripten-ports, or we
|
2024-08-19 07:53:53 +02:00
|
|
|
could move Qt from a prerequisite to a proper external/qt5 LO module built during the LO build, and
|
2024-08-19 07:53:12 +02:00
|
|
|
hack its configuration to build against LO's external/freetype etc. The former approach, building Qt
|
2024-08-18 15:45:26 +02:00
|
|
|
with -sUSE_FREETYPE, is even tried in qtbase/src/gui/configure.json, but apparently fails for
|
|
|
|
reasons not studied further yet, cf. Qt's config.log.)
|
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
### Setup LO
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2022-01-10 04:50:03 +01:00
|
|
|
`autogen.sh` is patched to use emconfigure. That basically sets various
|
|
|
|
environment vars, especially `EMMAKEN_JUST_CONFIGURE`, which will create the
|
|
|
|
correct output file names, checked by `configure` (`a.out`).
|
2021-04-23 14:04:06 +02:00
|
|
|
|
WASM fix native EH build since Emscripten 3.1.6
Building LO with WASM native EH and Emscripten 3.1.6+, the link
fails with a missing "emscripten_longjmp" symbol. Actually the
old build was simply wrong, because the emscripten_longjmp
function is just used for the Emscripten SjLj and not the native
WASM SjLj implementation. Linking just worked, because the used
libcompiler_rt-wasm-sjlj-mt.a lib was incorrectly providing that
symbol, which 3.1.6 removed.
But since running the NEH build still fails early in the same way
then before in FF nightly 100 and Chrome (= failure in the LO job
scheduler calling Task::UpdateMinPeriod for the "desktop::Desktop
m_firstRunTimer" timer, resulting in a WASM VM "RuntimeError:
indirect call signature mismatch"), there is still no way to know,
if nothing else it missing. The Emscripten / JS EH build still
works without any code changes. And it's not the first call on
a job object that fails...
And unfortunatly, because Qt itself also uses libpng, it also uses
SjLj for error handling, like LO in the image import, so the Qt
build must also be patched to build with "-s SUPPORT_LONGJMP=wasm".
Simply enough to do (see README.wasm.md). LO's configure now
detects that symbol and will bail out on incompatible Qt and LO
build setup.
See https://github.com/emscripten-core/emscripten/issues/16572
Change-Id: I3a1877f3aeb77873906176b9d3cd1ea92973f1f6
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132139
Tested-by: Jenkins
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
2022-03-26 20:46:56 +01:00
|
|
|
There's a distro config for WASM, but it just provides --host=wasm32-local-emscripten, which
|
|
|
|
should be enough setup. The build itself is a cross build and the cross-toolset just depends
|
|
|
|
on a minimal toolset (gcc, libc-dev, flex, bison); all else is build from source, because the
|
|
|
|
final result is not depending on the build system at all.
|
2021-04-25 16:02:10 +02:00
|
|
|
|
|
|
|
Recommended configure setup is thusly:
|
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
* grab defaults
|
|
|
|
`--with-distro=LibreOfficeWASM32`
|
2021-04-25 16:02:10 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
* local config
|
2024-04-03 12:04:04 +02:00
|
|
|
`QT5DIR=/dir/of/qt5/install/prefix`
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
* if you want to use ccache on both sides of the build
|
2025-03-27 20:04:54 +02:00
|
|
|
```
|
|
|
|
--with-build-platform-configure-options=--enable-ccache
|
|
|
|
--enable-ccache
|
|
|
|
```
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2022-01-10 04:50:03 +01:00
|
|
|
FWIW: it's also possible to build an almost static Linux LibreOffice by just using
|
|
|
|
--disable-dynloading --enable-customtarget-components. System externals are still
|
|
|
|
linked dynamically, but everything else is static.
|
|
|
|
|
|
|
|
### "Deploying" soffice.wasm
|
|
|
|
|
2025-03-27 20:04:54 +02:00
|
|
|
```
|
|
|
|
tar -chf wasm.tar --xform 's/.*program/lo-wasm/' instdir/program/soffice.* \
|
|
|
|
instdir/program/qt*
|
|
|
|
```
|
2022-01-10 04:50:03 +01:00
|
|
|
|
2022-01-21 12:30:42 +01:00
|
|
|
Your HTTP server needs to provide additional headers:
|
2022-01-10 04:50:03 +01:00
|
|
|
* add_header Cross-Origin-Opener-Policy same-origin
|
|
|
|
* add_header Cross-Origin-Embedder-Policy require-corp
|
|
|
|
|
|
|
|
The default html to use should be qt_soffice.html
|
|
|
|
|
|
|
|
### Debugging setup
|
|
|
|
|
|
|
|
Since a few months you can use DWARF information embedded by LLVM into the WASM
|
|
|
|
to debug WASM in Chrome. You need to enable an experimental feature and install
|
|
|
|
an additional extension. The whole setup is described in:
|
|
|
|
|
|
|
|
https://developer.chrome.com/blog/wasm-debugging-2020/
|
|
|
|
|
|
|
|
This way you don't need source maps (much faster linking!) and can resolve local
|
|
|
|
WASM variables to C++ names!
|
|
|
|
|
|
|
|
Per default, the WASM debug build splits the DWARF information into an additional
|
|
|
|
WASM file, postfixed '.debug.wasm'.
|
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
### Using Docker to cross-build with emscripten
|
2021-04-23 14:04:06 +02:00
|
|
|
|
|
|
|
If you prefer a controlled environment (sadly emsdk install/activate
|
|
|
|
is _not_ stable over time, as e.g. nodejs versions evolve), that is
|
|
|
|
easy to replicate across different machines - consider the docker
|
|
|
|
images we're providing.
|
|
|
|
|
|
|
|
Config/setup file see
|
2021-10-10 19:59:20 +02:00
|
|
|
<https://git.libreoffice.org/lode/+/ccb36979563635b51215477455953252c99ec013>
|
2021-04-23 14:04:06 +02:00
|
|
|
|
|
|
|
Run
|
|
|
|
|
2025-03-27 20:04:54 +02:00
|
|
|
```
|
|
|
|
docker-compose build
|
|
|
|
```
|
2021-04-23 14:04:06 +02:00
|
|
|
|
|
|
|
in the lode/docker dir to get the container prepared. Run
|
|
|
|
|
2025-03-27 20:04:54 +02:00
|
|
|
```
|
|
|
|
PARALLELISM=4 BUILD_OPTIONS= BUILD_TARGET=build docker-compose run --rm \
|
|
|
|
-e PARALLELISM -e BUILD_TARGET -e BUILD_OPTIONS builder
|
|
|
|
```
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
to perform an actual `srcdir != builddir` build; the container mounts
|
|
|
|
checked-out git repo and output dir via `docker-compose.yml` (so make
|
2021-04-23 14:04:06 +02:00
|
|
|
sure the path names there match your setup):
|
|
|
|
|
|
|
|
The lode setup expects, inside the lode/docker subdir, the following directories:
|
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
- core (`git checkout`)
|
2021-04-23 14:04:06 +02:00
|
|
|
- workdir (the output dir - gets written into)
|
2021-10-10 19:59:20 +02:00
|
|
|
- cache (`ccache tree`)
|
2021-04-23 14:04:06 +02:00
|
|
|
- tarballs (external project tarballs gets written and cached there)
|
|
|
|
|
2023-08-07 14:41:32 +03:00
|
|
|
### UNO bindings with Embind
|
|
|
|
|
|
|
|
Right now there's a very rough implementation in place. With lots of different
|
|
|
|
bits unimplemented. And it _might_ be leaking memory. i.e. Lots of room for
|
|
|
|
improvement! ;)
|
|
|
|
|
|
|
|
Some usage examples through javascript of the current implementation:
|
|
|
|
```js
|
|
|
|
// inserts a string at the start of the Writer document.
|
2024-07-18 11:44:45 +02:00
|
|
|
Module.uno_init.then(function() {
|
|
|
|
const css = Module.uno.com.sun.star;
|
2024-07-25 16:59:33 +02:00
|
|
|
let xModel = Module.getCurrentModelFromViewSh();
|
|
|
|
if (xModel === null || !css.text.XTextDocument.query(xModel)) {
|
|
|
|
const desktop = css.frame.Desktop.create(Module.getUnoComponentContext());
|
|
|
|
const args = new Module.uno_Sequence_com$sun$star$beans$PropertyValue(
|
|
|
|
0, Module.uno_Sequence.FromSize);
|
|
|
|
xModel = css.frame.XComponentLoader.query(desktop).loadComponentFromURL(
|
|
|
|
'file:///android/default-document/example.odt', '_default', 0, args);
|
|
|
|
args.delete();
|
|
|
|
}
|
2024-07-18 11:44:45 +02:00
|
|
|
const xTextDocument = css.text.XTextDocument.query(xModel);
|
|
|
|
const xText = xTextDocument.getText();
|
|
|
|
const xTextCursor = xText.createTextCursor();
|
|
|
|
xTextCursor.setString("string here!");
|
|
|
|
});
|
2023-08-07 14:41:32 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
```js
|
|
|
|
// changes each paragraph of the Writer document to a random color.
|
2024-07-18 11:44:45 +02:00
|
|
|
Module.uno_init.then(function() {
|
|
|
|
const css = Module.uno.com.sun.star;
|
2024-07-25 16:59:33 +02:00
|
|
|
let xModel = Module.getCurrentModelFromViewSh();
|
|
|
|
if (xModel === null || !css.text.XTextDocument.query(xModel)) {
|
|
|
|
const desktop = css.frame.Desktop.create(Module.getUnoComponentContext());
|
|
|
|
const args = new Module.uno_Sequence_com$sun$star$beans$PropertyValue(
|
|
|
|
0, Module.uno_Sequence.FromSize);
|
|
|
|
xModel = css.frame.XComponentLoader.query(desktop).loadComponentFromURL(
|
|
|
|
'file:///android/default-document/example.odt', '_default', 0, args);
|
|
|
|
args.delete();
|
|
|
|
}
|
2024-07-18 11:44:45 +02:00
|
|
|
const xTextDocument = css.text.XTextDocument.query(xModel);
|
|
|
|
const xText = xTextDocument.getText();
|
|
|
|
const xEnumAccess = css.container.XEnumerationAccess.query(xText);
|
|
|
|
const xParaEnumeration = xEnumAccess.createEnumeration();
|
|
|
|
while (xParaEnumeration.hasMoreElements()) {
|
|
|
|
const next = xParaEnumeration.nextElement();
|
|
|
|
const xParagraph = css.text.XTextRange.query(next.get());
|
|
|
|
const xParaProps = css.beans.XPropertySet.query(xParagraph);
|
|
|
|
const color = new Module.uno_Any(
|
|
|
|
Module.uno_Type.Long(), Math.floor(Math.random() * 0xFFFFFF));
|
|
|
|
xParaProps.setPropertyValue("CharColor", color);
|
|
|
|
next.delete();
|
|
|
|
color.delete();
|
|
|
|
}
|
|
|
|
});
|
2023-08-07 14:41:32 +03:00
|
|
|
```
|
|
|
|
|
Emscripten: Run external code on LO's main thread
...and not the browser's main thread. Those are different threads now since
6e6451ce96f47e0ef5e8ecf1750f394ff3fb48e4 "Emscripten: Move the Qt event loop off
the JS main thread". Running `Module.uno_init.then` on the browser's main
thread would never trigger, as that promise is only resolved on LO's main
thread.
When external code was included in soffice.js via
EMSCRIPTEN_EXTRA_SOFFICE_POST_JS, that didn't make a difference: Emscripten
effectively replicates that code to all the worker threads, so whatever worker
thread resolved the Module.uno_init promise (i.e., the LO main thread) had the
external code available and ran it. But when external code was included
directly in some HTML file (which "manually" provides a canvas and loads
soffice.js, not relying on the qt_soffice.html convenience functionality), it
was available in the browser's main thread but not in LO's main thread. For
that use case, introduce a new Module.uno_scripts array that can be set up in
the HTML file to contain an array of URLs (represented as strings) of scripts to
load into LO's main thread.
(Alternatively, running external code on the browser's main thread rather than
on LO's main thread could be more ideal in the sense that the external code
would then have access to the browser's document object. But, for one, it would
be less ideal in the sense that we would then potentially again execute LO code
(which we call into from the external code via UNO) on the browser's main
thread, which would bring back the issues that
6e6451ce96f47e0ef5e8ecf1750f394ff3fb48e4 "Emscripten: Move the Qt event loop off
the JS main thread" solved. And, for another, when I experimentally tried it
out, it caused massive Qt threading issues, so I quickly gave up again.)
Change-Id: If01a7859751706f168e93ccac75758ae5ce17cd2
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171870
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <stephan.bergmann@allotropia.de>
2024-08-14 16:54:49 +02:00
|
|
|
If you enter the above examples into the browser console, you need to enter them into the console of
|
2024-10-30 11:00:58 +01:00
|
|
|
the first web worker thread, which is the LO main thread since we use -sPROXY_TO_PTHREAD, not
|
Emscripten: Run external code on LO's main thread
...and not the browser's main thread. Those are different threads now since
6e6451ce96f47e0ef5e8ecf1750f394ff3fb48e4 "Emscripten: Move the Qt event loop off
the JS main thread". Running `Module.uno_init.then` on the browser's main
thread would never trigger, as that promise is only resolved on LO's main
thread.
When external code was included in soffice.js via
EMSCRIPTEN_EXTRA_SOFFICE_POST_JS, that didn't make a difference: Emscripten
effectively replicates that code to all the worker threads, so whatever worker
thread resolved the Module.uno_init promise (i.e., the LO main thread) had the
external code available and ran it. But when external code was included
directly in some HTML file (which "manually" provides a canvas and loads
soffice.js, not relying on the qt_soffice.html convenience functionality), it
was available in the browser's main thread but not in LO's main thread. For
that use case, introduce a new Module.uno_scripts array that can be set up in
the HTML file to contain an array of URLs (represented as strings) of scripts to
load into LO's main thread.
(Alternatively, running external code on the browser's main thread rather than
on LO's main thread could be more ideal in the sense that the external code
would then have access to the browser's document object. But, for one, it would
be less ideal in the sense that we would then potentially again execute LO code
(which we call into from the external code via UNO) on the browser's main
thread, which would bring back the issues that
6e6451ce96f47e0ef5e8ecf1750f394ff3fb48e4 "Emscripten: Move the Qt event loop off
the JS main thread" solved. And, for another, when I experimentally tried it
out, it caused massive Qt threading issues, so I quickly gave up again.)
Change-Id: If01a7859751706f168e93ccac75758ae5ce17cd2
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171870
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <stephan.bergmann@allotropia.de>
2024-08-14 16:54:49 +02:00
|
|
|
into the console of the browser's main thread.
|
|
|
|
|
|
|
|
Alternatively, you can do the following: Put an example into some file like `example.js` that you
|
|
|
|
put next to the `qt_soffice.html` that you serve to the browser (i.e., in
|
|
|
|
`workdir/installation/LibreOffice/emscripten/`). Create another small JS snippet file like
|
|
|
|
`include.js` (which is only needed during the build) containing
|
|
|
|
```
|
|
|
|
Module.uno_scripts = ['./example.js'];
|
|
|
|
```
|
|
|
|
And rebuild LO configured with an additional
|
2024-08-20 17:52:49 +02:00
|
|
|
`EMSCRIPTEN_EXTRA_SOFFICE_PRE_JS=/...path-to.../include.js`.
|
2023-08-07 14:41:32 +03:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
## Tools for problem diagnosis
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
* `nm -s` should list the symbols in the archive, based on the index generated by ranlib.
|
2021-04-23 14:04:06 +02:00
|
|
|
If you get linking errors that archive has no index.
|
|
|
|
|
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
## Emscripten filesystem access with threads
|
|
|
|
|
|
|
|
This is closed, but not really fixed IMHO:
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
- <https://github.com/emscripten-core/emscripten/issues/3922>
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
## Dynamic libraries `/` modules in emscripten
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
There is a good summary in:
|
|
|
|
|
|
|
|
- <https://bugreports.qt.io/browse/QTBUG-63925>
|
2021-04-23 14:04:06 +02:00
|
|
|
|
|
|
|
Summary: you can't use modules and threads.
|
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
This is mentioned at the end of:
|
|
|
|
|
|
|
|
- <https://github.com/emscripten-core/emscripten/wiki/Linking>
|
|
|
|
|
|
|
|
The usage of `MAIN_MODULE` and `SIDE_MODULE` has other problems, a major one IMHO is symbol resolution at runtime only.
|
|
|
|
So this works really more like plugins in the sense of symbol resolution without dependencies `/` rpath.
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-05-05 23:38:21 +02:00
|
|
|
There is some clang-level dynamic-linking in progress (WASM dlload). The following link is already a bit old,
|
2021-04-23 14:04:06 +02:00
|
|
|
but I found it a god summary of problems to expect:
|
2021-10-10 19:59:20 +02:00
|
|
|
|
|
|
|
- <https://iandouglasscott.com/2019/07/18/experimenting-with-webassembly-dynamic-linking-with-clang/>
|
2021-04-23 14:04:06 +02:00
|
|
|
|
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
## Mixed information, links, problems, TODO
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
More info on Qt WASM emscripten pthreads:
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
- <https://wiki.qt.io/Qt_for_WebAssembly#Multithreading_Support>
|
|
|
|
|
|
|
|
WASM needs `-pthread` at compile, not just link time for atomics support. Alternatively you can provide
|
|
|
|
`-s USE_PTHREADS=1`, but both don't seem to work reliable, so best provide both.
|
|
|
|
<https://github.com/emscripten-core/emscripten/issues/10370>
|
2021-04-23 14:04:06 +02:00
|
|
|
|
|
|
|
The output file must have the prefix .o, otherwise the WASM files will get a
|
2021-10-10 19:59:20 +02:00
|
|
|
`node.js` shebang (!) and ranlib won't be able to index the library (link errors).
|
2021-04-23 14:04:06 +02:00
|
|
|
|
|
|
|
Qt with threads has further memory limit. From Qt configure:
|
2025-03-27 20:04:54 +02:00
|
|
|
```
|
2021-04-23 14:04:06 +02:00
|
|
|
Project MESSAGE: Setting PTHREAD_POOL_SIZE to 4
|
|
|
|
Project MESSAGE: Setting TOTAL_MEMORY to 1GB
|
2025-03-27 20:04:54 +02:00
|
|
|
```
|
2021-10-10 19:59:20 +02:00
|
|
|
|
|
|
|
You can actually allocate 4GB:
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
- <https://bugzilla.mozilla.org/show_bug.cgi?id=1392234>
|
2021-04-23 14:04:06 +02:00
|
|
|
|
|
|
|
LO uses a nested event loop to run dialogs in general, but that won't work, because you can't drive
|
|
|
|
the browser event loop. like VCL does with the system event loop in the various VCL backends.
|
|
|
|
Changing this will need some major work (basically dropping Application::Execute).
|
|
|
|
|
|
|
|
But with the know problems with exceptions and threads, this might change:
|
2021-10-10 19:59:20 +02:00
|
|
|
|
|
|
|
- <https://github.com/emscripten-core/emscripten/pull/11518>
|
|
|
|
- <https://github.com/emscripten-core/emscripten/issues/11503>
|
|
|
|
- <https://github.com/emscripten-core/emscripten/issues/11233>
|
|
|
|
- <https://github.com/emscripten-core/emscripten/issues/12035>
|
2021-04-23 14:04:06 +02:00
|
|
|
|
|
|
|
We're also using emconfigure at the moment. Originally I patched emscripten, because it
|
2021-05-05 23:38:21 +02:00
|
|
|
wouldn't create the correct a.out file for C++ configure tests. Later I found that
|
2021-10-10 19:59:20 +02:00
|
|
|
the `emconfigure` sets `EMMAKEN_JUST_CONFIGURE` to work around the problem.
|
|
|
|
|
|
|
|
ICU bug:
|
|
|
|
|
|
|
|
- <https://github.com/emscripten-core/emscripten/issues/10129>
|
|
|
|
|
|
|
|
Alternative, probably:
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
- <https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Intl>
|
2021-04-23 14:04:06 +02:00
|
|
|
|
|
|
|
There is a wasm64, but that still uses 32bit pointers!
|
|
|
|
|
2021-10-10 19:59:20 +02:00
|
|
|
Old outdated docs:
|
|
|
|
|
|
|
|
- <https://wiki.documentfoundation.org/Development/Emscripten>
|
|
|
|
|
|
|
|
Reverted patch:
|
|
|
|
|
|
|
|
- <https://cgit.freedesktop.org/libreoffice/core/commit/?id=0e21f6619c72f1e17a7b0a52b6317810973d8a3e>
|
|
|
|
|
|
|
|
Generally <https://emscripten.org/docs/porting>:
|
|
|
|
|
|
|
|
- <https://emscripten.org/docs/porting/guidelines/api_limitations.html#api-limitations>
|
|
|
|
- <https://emscripten.org/docs/porting/files/file_systems_overview.html#file-system-overview>
|
|
|
|
- <https://emscripten.org/docs/porting/pthreads.html>
|
|
|
|
- <https://emscripten.org/docs/porting/emscripten-runtime-environment.html>
|
|
|
|
|
|
|
|
This will be interesting:
|
|
|
|
|
|
|
|
- <https://emscripten.org/docs/getting_started/FAQ.html#how-do-i-run-an-event-loop>
|
|
|
|
|
|
|
|
This didn't help much yet:
|
|
|
|
|
|
|
|
- <https://github.com/emscripten-ports>
|
|
|
|
|
|
|
|
Emscripten supports standalone WASI binaries:
|
|
|
|
|
|
|
|
- <https://github.com/emscripten-core/emscripten/wiki/WebAssembly-Standalone>
|
|
|
|
- <https://www.qt.io/qt-examples-for-webassembly>
|
|
|
|
- <http://qtandeverything.blogspot.com/2017/06/qt-for-web-assembly.html>
|
|
|
|
- <http://qtandeverything.blogspot.com/2020/>
|
|
|
|
- <https://emscripten.org/docs/api_reference/Filesystem-API.html>
|
|
|
|
- <https://discuss.python.org/t/add-a-webassembly-wasm-runtime/3957/12>
|
|
|
|
- <http://git.savannah.gnu.org/cgit/config.git>
|
|
|
|
- <https://webassembly.org/specs/>
|
|
|
|
- <https://developer.chrome.com/docs/native-client/>
|
|
|
|
- <https://emscripten.org/docs/getting_started/downloads.html>
|
|
|
|
- <https://github.com/openpgpjs/openpgpjs/blob/master/README.md#getting-started>
|
|
|
|
- <https://developer.mozilla.org/en-US/docs/WebAssembly/Using_the_JavaScript_API>
|
|
|
|
- <https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-intro.md>
|
|
|
|
- <https://www.ip6.li/de/security/x.509_kochbuch/openssl-fuer-webassembly-compilieren>
|
|
|
|
- <https://emscripten.org/docs/introducing_emscripten/about_emscripten.html#about-emscripten-porting-code>
|
|
|
|
- <https://emscripten.org/docs/compiling/Building-Projects.html>
|
2021-04-23 14:04:06 +02:00
|
|
|
|
2024-07-24 17:26:46 +02:00
|
|
|
### Threads and the event loop
|
|
|
|
|
|
|
|
The Emscripten emulation of pthreads requires the JS main thread event loop to be able to promptly
|
|
|
|
respond both when spawning and when exiting a pthread. But the Qt5 event loop runs on the JS main
|
|
|
|
thread, so the JS main thread event loop is blocked while a LO VCL Task is executed. And our
|
|
|
|
pthreads are typically spawned and joined from within such Task executions, which means that the JS
|
|
|
|
main thread event loop is not available to reliably perform those Emscripten pthread operations.
|
|
|
|
|
|
|
|
For pthread spawning, the solution is to set -sPTHREAD_POOL_SIZE to a sufficiently large value, so
|
|
|
|
that each of our pthread spawning requests during an inappropriate time finds a pre-spawned JS
|
|
|
|
Worker available.
|
|
|
|
|
|
|
|
There are patterns (like, at the time of writing this, the configmgr::Components::WriteThread) where
|
|
|
|
a pthread can get spawned and joined and then re-spawned (and re-joined) multiple times during a
|
|
|
|
single VCL Task execution (i.e., without the JS main thread event loop having a chance to get in
|
2024-07-25 09:03:27 +02:00
|
|
|
between any of those operations). But as the underlying Emscripten pthread exiting operations will
|
2024-07-24 17:26:46 +02:00
|
|
|
therefore queue up, the pthread spawning operations will eventually run out of -sPTHREAD_POOL_SIZE
|
|
|
|
pre-spawned JS Workers. The solution here is to change our pthread usage patterns accordingly, so
|
|
|
|
that such pthreads are rather kept running than being joined and re-spawned.
|
|
|
|
|
|
|
|
(-sPROXY_TO_PTHREAD would move the Qt5 event loop off the JS main thread, which should elegantly
|
|
|
|
solve all of the above issues. But Qt5 just doesn't appear to be prepared to run on anything but
|
|
|
|
the JS main thread; e.g., it tries to access the global JS `window` object in various places, which
|
|
|
|
is available on the JS main thread but not in a JS Worker.)
|
|
|
|
|
2023-02-02 13:38:02 +02:00
|
|
|
## Building headless LibreOffice as WASM for use in another product
|
|
|
|
|
|
|
|
### Set up Emscripten
|
|
|
|
|
|
|
|
Follow the instructions in the first part of this document.
|
|
|
|
|
|
|
|
### No Qt needed.
|
|
|
|
|
2023-02-06 11:57:15 +02:00
|
|
|
You don't need any dependencies other than those that normally are
|
|
|
|
downloaded and compiled when building LibreOffice.
|
2023-02-02 13:38:02 +02:00
|
|
|
|
|
|
|
### Set up LO
|
|
|
|
|
|
|
|
For instance, this autogen.input works for me:
|
|
|
|
|
2025-03-27 20:04:54 +02:00
|
|
|
```
|
|
|
|
--disable-debug
|
|
|
|
--enable-sal-log
|
|
|
|
--disable-crashdump
|
|
|
|
--host=wasm32-local-emscripten
|
|
|
|
--disable-gui
|
|
|
|
--with-wasm-module=writer
|
|
|
|
--with-package-format=emscripten
|
|
|
|
```
|
2023-02-02 13:38:02 +02:00
|
|
|
|
2023-02-13 11:04:30 +02:00
|
|
|
For building LO core for use in COWASM, it is known to work to use
|
|
|
|
Emscripten 3.1.30 (and not just 2.0.31 which is what the LO+Qt5 work
|
2024-03-06 12:49:43 +01:00
|
|
|
has been using in the past).
|
2023-02-13 11:04:30 +02:00
|
|
|
|
2023-02-02 13:38:02 +02:00
|
|
|
### That's all
|
|
|
|
|
|
|
|
After all, in this case you are building LO core headless for it to be used by other software.
|
2023-02-06 11:57:15 +02:00
|
|
|
|
|
|
|
Note that a soffice.wasm will be built, but that is just because of
|
|
|
|
how the makefilery has been set up. We do need the soffice.data file
|
|
|
|
that contains the in-memory file system needed by the LibreOffice
|
|
|
|
Technology core code during run-time, though. That is at the moment
|
|
|
|
built as a side-effect when building soffice.wasm.
|