- What is "emscripten-qt"?
- What browsers are supported?
- What kind of Qt apps will run?
- How can I get it?
- How do I use it?
- Are you using emscripten-qt?
What is "emscripten-qt"?¶
What browsers are supported?¶
In theory, it should work with any browser that supports the HTML5 canvas element and which supports typed arrays. So far, emscripten-qt has been tested and found to work in Firefox and Chrome. Konqueror and Rekonq have been tested and, alas, do not run programs compiled with emscripten-qt, seemingly due to missing typed array support (an app compiled with emscripten's TYPED_ARRAY=0 will at least start running, but Qt really needs full typed array support in order to work correctly)
What kind of Qt apps will run?¶
Many Qt demos, and even some full-fledged KDE productivity applications such as Kate have been ported, but there are some kinds of apps that will definitely need to be re-worked in order to run in emscripten-qt. A non-exhaustive list:
- Apps that use drag and drop. Related to the above point, but with the added annoyance that QDrag appears not to have an asynchronous version. This is probably fixable with a small, emscripten-specific API-addition, plus patching of any Qt internals that use the original QDrag API (the Qt ItemViews, for example).
- Apps that use any Qt modules except for, at the time of writing, QtCore, QtGui, QtXml and QtScript. Apps that use QtNetwork will compile, but the networking part currently won't work.
- Apps that use any parts of QtCore or QtGui that have been (temporarily?) compiled out: In the beginning, I was concentrating on just getting emscripten-qt up and running at all, and so many features of Qt got compiled out if they looked like they would be even slightly problematic - for example, JPEG support and QFileDialog were ditched immediately and only added back in after several weeks!
- ... and of course, apps that have additional dependencies that have not yet been converted to emscripten!
How can I get it?¶
How do I use it?¶
The instructions below are for Unix-y operation systems, such as Linux.
First, we need to check out Emscripten itself. Decide where you want to place it - somewhere in your home directory is fine - call that /home/path/to/put/emscripten/in. Change to /home/path/to/put/emscripten/in, and do
git clone https://github.com/kripken/emscripten.git emscripten
Then, from the same directory, do
git clone git://gitorious.org/qt/emscripten-qt.git
and then, still within the same directory, create a directory for compiling emscripten-qt:
Your /home/path/to/put/emscripten/in should now contain the directories "emscripten", "emscripten-qt", and "build-emscripten-qt". cd into your build directory:
Emscripten needs, at the time of writing, version 3.2 of clang and llvm to be installed. Your distro might have these packaged for you, or you may need to compile from source. Install them now.
Before we build emscripten-qt, we need to tell it where emscripten's root directory is (the directory containing "emcc", "em++", etc) - apps compiled with emscripten should use the header files from the "system" subdirectory there instead of the system-wide ones. We do this via an environment variable:
Now we can configure emscripten-qt:
../emscripten-qt/configure -xplatform qws/emscripten-clang -embedded emscripten -static -opensource -debug -no-qt3support -no-opengl -no-openssl -system-zlib -no-gif -qt-zlib -qt-libpng -no-libmng -no-libtiff -qt-libjpeg -no-accessibility -dbus -script -no-fpu -no-mmx -no-3dnow -no-sse -no-sse2 -no-sse3 -no-ssse3 -no-sse4.1 -no-sse4.2 -no-icu -no-rpath -confirm-license -no-webkit -no-phonon -no-freetype -nomake demos -nomake examples -little-endian -no-feature-socket -no-feature-codecs -no-feature-textcodecplugin -no-feature-systemlocale -no-feature-qws_multiprocess -no-feature-sound -no-feature-printpreviewwidget -no-feature-printpreviewdialog -no-feature-systemsemaphore -no-feature-sharedmemory -no-feature-localeventloop -feature-qws_clientblit -feature-qws_cursor -depths 32 -make tools --prefix=$(pwd)/install
This should succeed: note that it shouldn't matter too much what you have installed on your system (besides clang & llvm 3.2, that is) as it only relies on the headers that emscripten provides.
When the "configure" step has succeeded, do
make sub-tools-bootstrap && make install_qmake sub-moc-install_subtargets sub-uic-install_subtargets sub-rcc-install_subtargets && make sub-corelib-install_subtargets sub-gui-install_subtargets install_mkspecs
(you can add "-j <number of cores>" after each occurrence of "make" if you have a multi-core computer, to speed things up).
This should also succeed, and install Qt headers, libraries etc inside /home/path/to/put/emscripten/build-emscripten-qt/install.
There are a handful of source files that profiling has shown will benefit hugely from being compiled with a higher level of optimisation; if you want to re-compile these source files, do
for i in qcosmeticstroker qdrawhelper qgrayraster; do opt -O3 src/gui/.obj/debug-static-emb-emscripten/$i.o > tmp-llvm-$i.o; mv tmp-llvm-$i.o src/gui/.obj/debug-static-emb-emscripten/$i.o; done && make sub-corelib-install_subtargets sub-gui-install_subtargets
You are now ready to try and compile a sample app!
Currently, only QMake-based projects will work, though I hope to support CMake-based ones eventually.
Let's try one of Qt's own examples - the chips demo. The source is included with emscripten-qt.
Stage 1: Compiling Qt app to llvm¶
For reasons that are quite impenetrable to me, we don't seem to be able to build apps with emscripten-qt from anywhere within our build-emscripten-qt, so we'll have to move up a directory first, and then create and cd into a new subdirectory for building the chips demo:
cd .. mkdir qtdemos cd qtdemos
We'll have to make sure we our freshly compiled QMake, and the correct compiler settings:
export PATH=/home/path/to/put/emscripten/build-emscripten-qt/install/bin/:$PATH export QMAKESPEC=/home/path/to/put/emscripten/build-emscripten-qt/install/mkspecs/qws/emscripten-clang
As mentioned, the "QApplication::exec()" procedure for stopping main() from exiting straight away will not work with emscripten-qt, so we must make some small changes to the chips demo. The changes basically ensure that any objects local to main that must be kept alive while the app is running are allocated on the heap instead of the heap. We'll copy the chips source into our build directory since we need to patch it:
cp -R ../emscripten-qt/demos/chip .
The patch is included with emscripten-qt (you can view it here: source:emscripten-stuff/chip-emscripten-qt-patch.patch). Let's apply it:
cd chip patch main.cpp < ../../emscripten-qt/emscripten-stuff/chip-emscripten-qt-patch.patch cd ..
I like out-of-source builds, so let's have another build directory :)
mkdir build cd build
Then we should be able to QMake the chips project file as usual:
Then make - again, you can use the "-j" flag to speed things up if you have multiple cores:
Emscripten's "emcc" does all the hard work for us, here, but first we need a few of Qt's fonts - these will be embedded in the resulting webpage. I generally pick a very small sampling of them - the line below will copy about 156k worth of fonts:
mkdir -p qt-fonts && cp ../../build-emscripten-qt/install/lib/fonts/helvetica_*.qpf ../../build-emscripten-qt/install/lib/fonts/fixed_*.qpf qt-fonts/
This is pretty clunky, and I hope to get it a bit more automated at some point, but we also need to copy the QtCore.a and QtGui.a to the current directory and rename to .so:
cp ../../build-emscripten-qt/install/lib/QtGui.a QtGui.so cp ../../build-emscripten-qt/install/lib/QtCore.a QtCore.so
Now we let emcc work its magic:
../../emscripten/emcc \ chip.bc \ QtGui.so QtCore.so \ -O2 \ --closure 0 \ --jcache \ --pre-js ../../emscripten-qt/emscripten-stuff/pre-qt.js \ --js-library ../../emscripten-qt/emscripten-stuff/pre-qt-library.js \ --embed-file qt-fonts \ -s EXPORTED_FUNCTIONS="['_main', '_EMSCRIPTENQT_resetTimerCallback', '_EMSCRIPTENQT_timerCallback', '_EMSCRIPTENQT_timerCallback_springboard', '_EMSCRIPTEN_canvas_width_pixels', '_EMSCRIPTEN_canvas_height_pixels', '_EMSCRIPTENQT_mouseCanvasPosChanged', '_EMSCRIPTENQT_mouseCanvasButtonChanged']" \ -s TOTAL_MEMORY=67108864 \ -s INLINING_LIMIT=50 \ -o chip.html
A brief explanation of the arguments:
- then we provide the paths to QtCore.a and QtGui.a, which are the bitcode libraries we created when we compiled emscripten-qt;
- -O2 is the optimisation level;
- --closure 0 disables Google's "Closure" compiler as it currently seems to generate incorrect code with emscripten-qt;
- --jcache just tells emcc to cache the results so hopefully things will go faster should we want to compile this again;
- --embed-file qt-fonts adds the fonts that we just copied over - note that the directory name "qt-fonts" is important here, as emscripten-qt is hardcoded to look for fonts stored in a directory called "qt-fonts";
- -s EXPORTED_FUNCTIONS is something that will hopefully be useful to us when/ if I figure out how to get Closure working with emscripten-qt;
- -s TOTAL_MEMORY sets the amount of "simulated" memory that will be allotted to the app when it runs; the "chip" demo takes up a fair bit of RAM, and Emscripten in asm.js mode requires RAM to be a power of 2, so we allocate ~64MB to be on the safe side; and
Are you using emscripten-qt?¶
If so, please drop me a line - it's always nice to hear about people who are putting it to good use! You can find a number of ways to contact me on the main page, here.