diff --git a/cmake/QtWasmHelpers.cmake b/cmake/QtWasmHelpers.cmake index 3eb97fa3cb..2b3a97987f 100644 --- a/cmake/QtWasmHelpers.cmake +++ b/cmake/QtWasmHelpers.cmake @@ -18,14 +18,9 @@ function (qt_internal_setup_wasm_target_properties wasmTarget) target_compile_options("${wasmTarget}" INTERFACE "SHELL:-s MEMORY64=1" ) target_link_options("${wasmTarget}" INTERFACE "SHELL:-s MEMORY64=1" -mwasm64) endif() - # Enable MODULARIZE and set EXPORT_NAME, which makes it possible to - # create application instances using a global constructor function, - # e.g. let app_instance = await createQtAppInstance(). - # (as opposed to MODULARIZE=0, where Emscripten creates a global app - # instance object at Javascript eval time) - target_link_options("${wasmTarget}" INTERFACE - "SHELL:-s MODULARIZE=1" - "SHELL:-s EXPORT_NAME=createQtAppInstance") + # Enable MODULARIZE so that we are able to set EXPORT_NAME later and instantiate on demand (with + # MODULARIZE=0, emscripten creates a global app instance object at Javascript eval time) + target_link_options("${wasmTarget}" INTERFACE "SHELL:-s MODULARIZE=1") #simd if (QT_FEATURE_wasm_simd128) @@ -126,6 +121,7 @@ function (qt_internal_setup_wasm_target_properties wasmTarget) endfunction() function(qt_internal_wasm_add_finalizers target) + qt_add_list_file_finalizer(_qt_internal_set_wasm_export_name ${target}) qt_add_list_file_finalizer(_qt_internal_add_wasm_extra_exported_methods ${target}) qt_add_list_file_finalizer(_qt_internal_wasm_add_target_helpers ${target}) endfunction() diff --git a/mkspecs/common/wasm/wasm.conf b/mkspecs/common/wasm/wasm.conf index b67d721352..adad999200 100644 --- a/mkspecs/common/wasm/wasm.conf +++ b/mkspecs/common/wasm/wasm.conf @@ -35,7 +35,6 @@ EMCC_COMMON_LFLAGS += \ --bind \ -s FETCH=1 \ -s MODULARIZE=1 \ - -s EXPORT_NAME=createQtAppInstance \ -s WASM_BIGINT=1 \ -s STACK_SIZE=5MB diff --git a/mkspecs/features/wasm/wasm.prf b/mkspecs/features/wasm/wasm.prf index 0256fc2e49..72007fe863 100644 --- a/mkspecs/features/wasm/wasm.prf +++ b/mkspecs/features/wasm/wasm.prf @@ -15,6 +15,13 @@ exists($$QMAKE_QT_CONFIG) { } EMCC_LFLAGS += -s EXPORTED_RUNTIME_METHODS=$$EXPORTED_METHODS + !isEmpty(QT_WASM_EXPORT_NAME): { + EXPORT_NAME = $$QT_WASM_EXPORT_NAME + } else { + EXPORT_NAME = $${TARGET}_entry + } + EMCC_LFLAGS += -s EXPORT_NAME=$$EXPORT_NAME + qtConfig(thread) { EMCC_LFLAGS += -pthread diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake index abffd2026e..3347f39829 100644 --- a/src/corelib/Qt6CoreMacros.cmake +++ b/src/corelib/Qt6CoreMacros.cmake @@ -656,6 +656,7 @@ function(_qt_internal_finalize_executable target) if(EMSCRIPTEN) _qt_internal_wasm_add_target_helpers("${target}") _qt_internal_add_wasm_extra_exported_methods("${target}") + _qt_internal_set_wasm_export_name("${target}") endif() if(IOS) _qt_internal_finalize_ios_app("${target}") diff --git a/src/corelib/Qt6WasmMacros.cmake b/src/corelib/Qt6WasmMacros.cmake index 6e004dc862..188ed28fe4 100644 --- a/src/corelib/Qt6WasmMacros.cmake +++ b/src/corelib/Qt6WasmMacros.cmake @@ -104,3 +104,12 @@ function(_qt_internal_add_wasm_extra_exported_methods target) ) endif() endfunction() + +function(_qt_internal_set_wasm_export_name target) + get_target_property(export_name "${target}" QT_WASM_EXPORT_NAME) + if(export_name) + target_link_options("${target}" PRIVATE "SHELL:-s EXPORT_NAME=${export_name}") + else() + target_link_options("${target}" PRIVATE "SHELL:-s EXPORT_NAME=${target}_entry") + endif() +endfunction() diff --git a/src/plugins/platforms/wasm/qtloader.js b/src/plugins/platforms/wasm/qtloader.js index c62b3f8823..1d1e7fb6d1 100644 --- a/src/plugins/platforms/wasm/qtloader.js +++ b/src/plugins/platforms/wasm/qtloader.js @@ -49,7 +49,7 @@ async function qtLoad(config) if (typeof config.qt !== 'object') throw new Error('config.qt is required, expected an object'); if (typeof config.qt.entryFunction !== 'function') - config.qt.entryFunction = window.createQtAppInstance; + throw new Error('config.qt.entryFunction is required, expected a function'); config.qtContainerElements = config.qt.containerElements; delete config.qt.containerElements; diff --git a/src/plugins/platforms/wasm/wasm_shell.html b/src/plugins/platforms/wasm/wasm_shell.html index 9924890122..2287e3fad2 100644 --- a/src/plugins/platforms/wasm/wasm_shell.html +++ b/src/plugins/platforms/wasm/wasm_shell.html @@ -57,7 +57,7 @@ exitData.text !== undefined ? ` (${exitData.text})` : ''; showUi(spinner); }, - entryFunction: window.createQtAppInstance, + entryFunction: window.@APPNAME@_entry, containerElements: [screen], } }); diff --git a/tests/manual/wasm/a11y/basic_widgets/basic_widgets.html b/tests/manual/wasm/a11y/basic_widgets/basic_widgets.html index 899e72bb00..091809be5c 100644 --- a/tests/manual/wasm/a11y/basic_widgets/basic_widgets.html +++ b/tests/manual/wasm/a11y/basic_widgets/basic_widgets.html @@ -7,7 +7,7 @@

Running event dispatcher auto test.

diff --git a/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto_asyncify.html b/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto_asyncify.html index a277c9b9e0..f09b29d85b 100644 --- a/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto_asyncify.html +++ b/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto_asyncify.html @@ -3,7 +3,7 @@

Running event dispatcher auto test.

diff --git a/tests/manual/wasm/network/sockify_sockets_auto/sockify_sockets_auto.html b/tests/manual/wasm/network/sockify_sockets_auto/sockify_sockets_auto.html index 5376249a71..080ada94e7 100644 --- a/tests/manual/wasm/network/sockify_sockets_auto/sockify_sockets_auto.html +++ b/tests/manual/wasm/network/sockify_sockets_auto/sockify_sockets_auto.html @@ -3,7 +3,7 @@

Sockify tunneled sockets auto test. diff --git a/tests/manual/wasm/qstdweb/files_auto.html b/tests/manual/wasm/qstdweb/files_auto.html index 71e8088dfb..9027fdc660 100644 --- a/tests/manual/wasm/qstdweb/files_auto.html +++ b/tests/manual/wasm/qstdweb/files_auto.html @@ -6,7 +6,7 @@

Running files auto test.

diff --git a/tests/manual/wasm/qstdweb/promise_auto.html b/tests/manual/wasm/qstdweb/promise_auto.html index 786145419f..94a8dbb88a 100644 --- a/tests/manual/wasm/qstdweb/promise_auto.html +++ b/tests/manual/wasm/qstdweb/promise_auto.html @@ -3,7 +3,7 @@

Running promise auto test.

diff --git a/tests/manual/wasm/qstdweb/qwasmcompositor_auto.html b/tests/manual/wasm/qstdweb/qwasmcompositor_auto.html index 26daecdf41..f33aab0b9c 100644 --- a/tests/manual/wasm/qstdweb/qwasmcompositor_auto.html +++ b/tests/manual/wasm/qstdweb/qwasmcompositor_auto.html @@ -3,7 +3,7 @@

Running files auto test.

diff --git a/tests/manual/wasm/qtloader_integration/test_body.js b/tests/manual/wasm/qtloader_integration/test_body.js index d68c6ba8cc..385a3816d3 100644 --- a/tests/manual/wasm/qtloader_integration/test_body.js +++ b/tests/manual/wasm/qtloader_integration/test_body.js @@ -48,10 +48,32 @@ export class QtLoaderIntegrationTests 'config.qt is required, expected an object', caughtException.message); } - async useDefaultOnMissingEntryFunction() + async missingEntryFunction() { - const instance = await qtLoad({ arguments: ['--no-gui'], qt: {}}); - assert.isNotUndefined(instance); + let caughtException; + try { + await qtLoad({ qt: {}}); + } catch (e) { + caughtException = e; + } + + assert.isNotUndefined(caughtException); + assert.equal( + 'config.qt.entryFunction is required, expected a function', caughtException.message); + } + + async badEntryFunction() + { + let caughtException; + try { + await qtLoad({ qt: { entryFunction: 'invalid' }}); + } catch (e) { + caughtException = e; + } + + assert.isNotUndefined(caughtException); + assert.equal( + 'config.qt.entryFunction is required, expected a function', caughtException.message); } async environmentVariables() @@ -62,7 +84,7 @@ export class QtLoaderIntegrationTests variable1: 'value1', variable2: 'value2' }, - entryFunction: createQtAppInstance, + entryFunction: tst_qtloader_integration_entry, containerElements: [this.#testScreenContainers[0]] } }); @@ -79,7 +101,7 @@ export class QtLoaderIntegrationTests const instance = await qtLoad({ qt: { - entryFunction: createQtAppInstance, + entryFunction: tst_qtloader_integration_entry, containerElements: this.#testScreenContainers } }); @@ -125,7 +147,7 @@ export class QtLoaderIntegrationTests { const instance = await qtLoad({ qt: { - entryFunction: createQtAppInstance, + entryFunction: tst_qtloader_integration_entry, containerElements: this.#testScreenContainers, } }); @@ -181,7 +203,7 @@ export class QtLoaderIntegrationTests const instances = await Promise.all([1, 2, 3].map(i => qtLoad({ qt: { - entryFunction: createQtAppInstance, + entryFunction: tst_qtloader_integration_entry, containerElements: [this.#addScreenContainer(`screen-container-${i}`, { width: `${i * 10}px`, height: `${i * 10}px`, @@ -222,7 +244,7 @@ export class QtLoaderIntegrationTests accumulatedStdout += output; }, qt: { - entryFunction: createQtAppInstance, + entryFunction: tst_qtloader_integration_entry, } }); @@ -246,7 +268,7 @@ export class QtLoaderIntegrationTests { await qtLoad({ qt: { - entryFunction: createQtAppInstance, + entryFunction: tst_qtloader_integration_entry, containerElements: [this.#testScreenContainers[0]], module: await WebAssembly.compileStreaming( fetch('tst_qtloader_integration.wasm')) @@ -259,7 +281,7 @@ export class QtLoaderIntegrationTests const instance = await qtLoad({ arguments: ['--no-gui', 'arg1', 'other', 'yetanotherarg'], qt: { - entryFunction: createQtAppInstance, + entryFunction: tst_qtloader_integration_entry, } }); const args = this.#callTestInstanceApi(instance, 'retrieveArguments'); @@ -275,7 +297,7 @@ export class QtLoaderIntegrationTests try { await qtLoad({ qt: { - entryFunction: createQtAppInstance, + entryFunction: tst_qtloader_integration_entry, containerElements: [this.#testScreenContainers[0]], module: Promise.reject(new Error('Failed to load')), } @@ -294,7 +316,7 @@ export class QtLoaderIntegrationTests arguments: ['--no-gui'], qt: { onExit: onExitMock, - entryFunction: createQtAppInstance, + entryFunction: tst_qtloader_integration_entry, } }); try { @@ -316,7 +338,7 @@ export class QtLoaderIntegrationTests arguments: ['--no-gui', '--crash-immediately'], qt: { onExit: onExitMock, - entryFunction: createQtAppInstance, + entryFunction: tst_qtloader_integration_entry, } }); } catch (e) { @@ -340,7 +362,7 @@ export class QtLoaderIntegrationTests arguments: ['--no-gui'], onAbort: onAbortMock, qt: { - entryFunction: createQtAppInstance, + entryFunction: tst_qtloader_integration_entry, } }); try { @@ -358,7 +380,7 @@ export class QtLoaderIntegrationTests arguments: ['--no-gui'], qt: { onExit: onExitMock, - entryFunction: createQtAppInstance, + entryFunction: tst_qtloader_integration_entry, } }); // The module is running. onExit should not have been called. @@ -383,7 +405,7 @@ export class QtLoaderIntegrationTests arguments: ['--no-gui', '--exit-immediately'], qt: { onExit: onExitMock, - entryFunction: createQtAppInstance, + entryFunction: tst_qtloader_integration_entry, } }); assert.equal(1, onExitMock.calls.length); @@ -402,7 +424,7 @@ export class QtLoaderIntegrationTests arguments: ['--no-gui'], quit: quitMock, qt: { - entryFunction: createQtAppInstance, + entryFunction: tst_qtloader_integration_entry, } }); try { diff --git a/tests/manual/wasm/qtwasmtestlib/README.md b/tests/manual/wasm/qtwasmtestlib/README.md index 515c33ae6a..6de81fe8b4 100644 --- a/tests/manual/wasm/qtwasmtestlib/README.md +++ b/tests/manual/wasm/qtwasmtestlib/README.md @@ -48,7 +48,7 @@ Finally provide an html file which hosts the test runner and calls runTestCase()

Running Foo auto test.

@@ -67,7 +67,7 @@ html file provides container elements which becomes QScreens for the test code. window.onload = async () => { let log = document.getElementById("log") let containers = [document.getElementById("container")]; - runTestCase(log, containers); + runTestCase(entryFunction, log, containers); };

Running Foo auto test.

diff --git a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.js b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.js index eaf518d1ef..01003f30f9 100644 --- a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.js +++ b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.js @@ -77,12 +77,13 @@ async function runTestFunction(instance, name) { } } -async function runTestCaseImpl(testFunctionStarted, testFunctionCompleted, qtContainers) { +async function runTestCaseImpl(entryFunction, testFunctionStarted, testFunctionCompleted, qtContainers) +{ // Create test case instance const config = { qtContainerElements: qtContainers || [] } - const instance = await createQtAppInstance(config); + const instance = await entryFunction(config); // Run all test functions const functionsString = instance.getTestFunctions(); @@ -124,10 +125,11 @@ function testFunctionCompleted(status) { g_htmlLogElement.innerHTML += line; } -async function runTestCase(htmlLogElement, qtContainers) { +async function runTestCase(entryFunction, htmlLogElement, qtContainers) +{ g_htmlLogElement = htmlLogElement; try { - await runTestCaseImpl(testFunctionStarted, testFunctionCompleted, qtContainers); + await runTestCaseImpl(entryFunction, testFunctionStarted, testFunctionCompleted, qtContainers); g_htmlLogElement.innerHTML += "
DONE" } catch (err) { g_htmlLogElement.innerHTML += err diff --git a/util/wasm/batchedtestrunner/qwasmjsruntime.js b/util/wasm/batchedtestrunner/qwasmjsruntime.js index b8603f2618..d973914cd3 100644 --- a/util/wasm/batchedtestrunner/qwasmjsruntime.js +++ b/util/wasm/batchedtestrunner/qwasmjsruntime.js @@ -109,18 +109,18 @@ export class CompiledModule { this.#resourceLocator = resourceLocator; } - static make(js, wasm, resourceLocator + static make(js, wasm, entryFunctionName, resourceLocator ) { const exports = {}; eval(js); - if (!exports.createQtAppInstance) { + if (!exports[entryFunctionName]) { throw new Error( - 'createQtAppInstance has not been exported by the main script' + '${entryFunctionName} has not been exported by the main script' ); } return new CompiledModule( - exports.createQtAppInstance, js, wasm, resourceLocator + exports[entryFunctionName], js, wasm, resourceLocator ); } @@ -218,6 +218,6 @@ export class ModuleLoader { ); const [js, wasm] = await Promise.all([jsLoadPromise, wasmLoadPromise]); - return CompiledModule.make(js, wasm, this.#resourceLocator); + return CompiledModule.make(js, wasm, `${moduleName}_entry`, this.#resourceLocator); } }