Sviluppare applicazioni Qt per il Web con WebAssembly

Uno dei punti di forza del framework Qt è sicuramente lo sviluppo multi piattaforma. La stessa applicazione può essere rilasciata sotto Windows, Linux, MacOs, su sistemi operativi mobile Android e iOS e dispositivi embedded.
A questo ventaglio di possibilità si aggiunge quella di potere rilasciare la propria applicazione come Web-App potendole eseguire su qualsiasi browser tramite WebAssembly.
Questo articolo coprirà l’installazione e la compilazione di Qt WebAssembly su ambiente Linux. Come verrà spiegato più avanti è conveniente appoggiarsi a una Virtual Machine o un Docker per la compilazione quindi la guida può andare bene anche per utenti Windows.
WebAssembly
WebAssembly (Wasm, WA) è uno standard web che definisce un formato binario e un corrispondente formato testuale per la scrittura di codice eseguibile nelle pagine web. Ha lo scopo di abilitare l’esecuzione del codice quasi alla stessa velocità con cui esegue il codice macchina nativo. È stato progettato come integrazione di JavaScript per accelerare le prestazioni delle parti critiche delle applicazioni Web e in seguito per consentire lo sviluppo web in altri linguaggi oltre a JavaScript. È sviluppato dal World Wide Web Consortium (W3C) con ingegneri provenienti da Mozilla, Microsoft, Google e Apple.
Wikipedia – WebAssembly
La necessità di potere integrare altri linguaggi oltre a JavaScript per lo sviluppo web per quei contesti dove le proprietà di JavaScript possono rappresentare un collo di bottiglia (performance, assenza di multithreading, linguaggio interpretato) ha spinto verso la creazione di questo nuovo standard.
Ci troviamo quindi di fronte ad uno strumento omnicomprensivo per lo sviluppo nativo, web e mobile? No, o comunque non ancora. La tecnologia è molto promettente, tuttavia spesso è comunque preferibile una soluzione nativa per target specifici come il web o il mobile. Il contesto per cui ho trovato molto interessante questa tecnologia è quando si ha un’applicazione nativa da fare girare su hardware dedicati come pannelli o dispositivi embedded e si ha la necessità di esporli o remotarli senza l’utilizzo di strumenti come VNC.
Qt WebAssembly
Il modulo Qt for WebAssembly è disponibile dalla versione di Qt 5.13 (5.12 come technical preview). Il modulo è ancora in via di sviluppo ed anche se tranquillamente utilizzabile, non sfrutta ancora tutte le potenzialità offerte da WebAssembly.
È possibile trovare sulla pagina dedicata del sito Qt alcuni esempi compilati e disponibili online.
I moduli supportati da Qt per WebAssembly sono i seguenti:
- QtBase
- QtDeclarative
- QtQuickcontrols2
- QtWebsockets
- QtSvg
- QtCharts
- QtMqtt
Mentre quelli non supportati sono QtMultimedia e QtWebView. Una delle maggiori limitazioni è quindi quella di non potere riprodurre file audio e video, anche se per i video è bypassabile trasmettendolo come stream di immagini. Infine, i moduli non elencati potrebbero funzionare, ma non sono supportati. Qt 3D non è strettamente indicato come non funzionante, ma di fatto non lo è. Sono però disponibili i controlli di QQuick3D e alcune funzionaligà di OpenGL.
Una limitazione attuale del modulo è quella che tutte le eventuali librerie devono essere linkate staticamente. Per cui è più semplice utilizzare Wasm per applicazioni con poche dipendenze o che siano suddivise in applicazioni back-end e front-end, con solo quest’ultima compilata in WebAssembly.
E riguardo Qt 6? Il reintegro ed alcuni miglioramenti erano previsti per Qt 6.2, la prima LTS della serie 6, tra cui la possibilità di includere libereie dinamiche. Tuttavia come dichiarato su QTBUG-75469 i lavori dipendono molto dalla migrazione verso CMake e il feature freeze della 6.2 è previsto pe Maggio 2021, pertanto probabilmente per quella versione sarà semplicemente reintegrato con le funzionalità utilizzabili all’interno di Qt 5.15. Per chi volesse seguire l’avanzamento dei lavoril il ticket dedicato è il QTBUG-78647.
Emscripten

La prima cosa da fare per potere compilare in WebAssembly è quella di scaricare ed installare emscripten, il suo compilatore dedicato. Attenzione: nel Mantainance Tool di Qt emscripten non è incluso! Quindi installando il pacchetto Qt WebAssembly da lì non si sarà in grado di compilare nulla se prima non si sarà installato il compilatore.
Il metodo più semplice è quello di clonare il repository da GitHub ed attivare la versione consigliata per la versione di Qt che si vuole installare. Le versioni consigliate dalla documentazione Qt sono le seguenti:
- Qt 5.12: 1.38.16
- Qt 5.13: 1.38.27 (multithreading: 1.38.30)
- Qt 5.14: 1.38.27 (multithreading: 1.38.30)
- Qt 5.15: 1.39.8
Una volta attivata la versione desiderata, sarà necessario settare le variabili d’ambiente contenute nello script emsdk_env.sh, che rimarranno attive per la sessione corrente. Altrimenti è sempre possibile inserire su .bash_profile per averle sempre attive.
La sequenza completa di operazioni è la seguente:
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
emsdk install sdk-upstream-1.39.8-64bit
emsdk activate sdk-upstream-1.39.8-64bit
source emsdk_env.sh
echo 'source "/opt/repositories/emsdk/emsdk_env.sh"' >> $HOME/.bash_profile
Installazione
Ci sono due metodi per installare Qt for WebAssembly,
- Tramite Mantainance Tool
- Ricompilando i sorgenti
Utilizzando il Mantainance Tool avremo disponibili di default solo i moduli supportati. Per utilizzare gli altri appartenenti ai moduli extras o qquick3d sarà necessario ricompilare i sorgenti. In questa guida verrà utilizzato il primo metodo, in quanto la ricompilazione si presta di più nel caso di un setup di un ambiente tramite Docker. Selezionando il pacchetto WebAssembly dalla versione Qt scelta installerà i componenti.

Compilare un progetto
Per compilare ho scelto di utilizzare l’esempio calculator-qml disponibile tra gli esempi di Qt Creator. L’esempio in questione si presenta così:

Anche avendo installato il pacchetto dal Mantainance Tool, non sarà disponibile un kit adeguato per la compilazione in Wasm. Senza la ricompilazione delle Qt dai sorgenti, il setup del kit e la compilazione tramite esso non è un’operazione immediata e non è detto che nonostante tutto vada a buon fine. La soluzione più semplice e immediata a cui sono arrivato è di lanciare la compilazione manualmente da shell.
Aggiornamento: a quanto pare c’erano diversi ticket aperti riguardante che impedivano il corretto rilevamento e configurazione del kit WebAssembly (QTCREATORBUG-23561, QTCREATORBUG-24822, QTCREATORBUG-23126, QTCREATORBUG-23741). Questi dovrebbero essere risolti con il rilascio di Qt Creator 5.15.
Come detto precedentemente, la compilazione avviene interamente in statico e la prima in particolare risulterà estremamente lunga rispetto a una compilazione nativa. La produzione degli header precompilati per i moduli Qt abbasserà il tempo di compilazione delle volte successive, anche se queste saranno comunque un ordine di grandezza più grande del normale.
Dalla cartella del progetto creiamo una cartella di build, che chiamerò buildwasm per non mischiare i target delle due compilazioni, prestando attenzione a richiamare il qmake che fa riferimento all’installazione wasm_32 delle Qt (di default è quella di gcc o MinGW).
mkdir buildwasm && cd builwasm
/opt/Qt/5.15.2/wasm_32/bin/qmake .. && make -j8
Una volta compilato, vedremo la pagina HTML e i file JavaScript generati dalla compilazione. Se si proverà ad aprire direttamente la pagina HTML generata, si osserverà il seguente errore:
TypeError: NetworkError when attempting to fetch resource.
Questo perché bisognerà esporre la pagina tramite il server HTTP di emscripten anche in locale. Per lanciarlo sarà sufficiente digitare sulla shell dalla cartella di build la seguente riga
emrun --no_browser --port 8080 .
che mapperà il contenuto della cartella corrente sulla porta 8080. Adesso si potrà aprire il browser sull’indirizzo locale sulla porta 8080 e vedremo apparire il contenuto della cartella.

Adesso entranto all’interno della pagina calculator-qml.html sarà possibile vedere l’applicazione compilata. Osservando lo screenshot a seguire si può vedere come l’applicazione appaia identica in tutto e per tutto a quella nativa.

Conclusioni
WebAssembly è di per sé uno strumento molto promettente e le cui capacità devono ancora essere esplorate a fondo, anche se esitono progetti di prova interessanti come quello del porting di Doom 3 in Wasm. Qt WebAssembly è allo stato attuale da considerarsi ancora in stato embrionale, data la mancanza di supporto completo verso i suoi moduli, la sua controparte in C#, Blazor, è in uno stadio più avanzanto e stabile e con una migliore integrazione in Visual Studio Code. Tuttavia, calato nel contesto di Qt, più orientato verso l’embedded, può rivelarsi uno strumento utile.
Attualmente un progetto molto utilizzato con Qt WebAssembly è quello di QmlOnline, versione online di QHot, un editor QML live utilizzato come base da sempre più progetti. Un altro esempio degno di nota è il suo fork per la libreria grafica Qaterial, QaterialOnline.
Riferimenti: