wasm: Generate plugin preloads for dynamic linking at install step

Dynamic linking on WebAssembly involves preloading .so libraries
during startup of application. Normally, those plugin preload
lists are generating manually by user.
Automate this process as part of installation step.

Change-Id: I364ebdb170f9fac53da241c96f601613352972d8
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
Piotr Wierciński 2024-06-26 16:32:00 +02:00
parent bc2ed77a72
commit f0f89d3c19
4 changed files with 64 additions and 13 deletions

View File

@ -378,9 +378,17 @@ if(APPLE)
elseif(WASM) elseif(WASM)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/util/wasm/wasmtestrunner/qt-wasmtestrunner.py" configure_file("${CMAKE_CURRENT_SOURCE_DIR}/util/wasm/wasmtestrunner/qt-wasmtestrunner.py"
"${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/qt-wasmtestrunner.py" @ONLY) "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/qt-wasmtestrunner.py" @ONLY)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/util/wasm/preload/preload_qml_imports.py"
"${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/preload_qml_imports.py" COPYONLY)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/util/wasm/preload/preload_qt_plugins.py"
"${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/preload_qt_plugins.py" COPYONLY)
qt_install(PROGRAMS "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/qt-wasmtestrunner.py" qt_install(PROGRAMS "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/qt-wasmtestrunner.py"
DESTINATION "${INSTALL_LIBEXECDIR}") DESTINATION "${INSTALL_LIBEXECDIR}")
qt_install(PROGRAMS "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/preload_qml_imports.py"
DESTINATION "${INSTALL_LIBEXECDIR}")
qt_install(PROGRAMS "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/preload_qt_plugins.py"
DESTINATION "${INSTALL_LIBEXECDIR}")
endif() endif()
# Install CI support files to libexec. # Install CI support files to libexec.

View File

@ -91,6 +91,49 @@ function(_qt_internal_wasm_add_target_helpers target)
${_target_directory}/qtloader.js COPYONLY) ${_target_directory}/qtloader.js COPYONLY)
configure_file("${WASM_BUILD_DIR}/plugins/platforms/qtlogo.svg" configure_file("${WASM_BUILD_DIR}/plugins/platforms/qtlogo.svg"
${_target_directory}/qtlogo.svg COPYONLY) ${_target_directory}/qtlogo.svg COPYONLY)
if(QT_FEATURE_shared)
configure_file("${WASM_BUILD_DIR}/libexec/preload_qml_imports.py"
${_target_directory}/preload_qml_imports.py COPYONLY)
configure_file("${WASM_BUILD_DIR}/libexec/preload_qt_plugins.py"
${_target_directory}/preload_qt_plugins.py COPYONLY)
endif()
if(CMAKE_STAGING_PREFIX)
install(FILES
${_target_directory}/qtloader.js
${_target_directory}/qtlogo.svg
${_target_directory}/${_target_output_name}.html
${_target_directory}/${_target_output_name}.wasm
${_target_directory}/${_target_output_name}.js
DESTINATION ${CMAKE_STAGING_PREFIX})
if(QT_FEATURE_thread)
install(FILES
${_target_directory}/${_target_output_name}.worker.js
DESTINATION ${CMAKE_STAGING_PREFIX})
endif()
if(QT_FEATURE_shared)
find_package(Python3 COMPONENTS Interpreter)
if(Python3_Interpreter_FOUND)
install(FILES
${_target_directory}/preload_qml_imports.py
${_target_directory}/preload_qt_plugins.py
DESTINATION ${CMAKE_STAGING_PREFIX})
install(CODE "execute_process(COMMAND ${Python3_EXECUTABLE} \
${_target_directory}/preload_qml_imports.py \
${CMAKE_CURRENT_SOURCE_DIR} ${QT_HOST_PATH} \
${QT6_INSTALL_PREFIX} \
${CMAKE_STAGING_PREFIX})")
install(CODE "execute_process(COMMAND ${Python3_EXECUTABLE} \
${_target_directory}/preload_qt_plugins.py \
${QT6_INSTALL_PREFIX} \
${CMAKE_STAGING_PREFIX})")
else()
message(WARNING "Python 3 not found. Generating preload list for dynamic linking is disabled.")
endif()
endif()
endif()
endif() endif()
endif() endif()
endif() endif()

View File

@ -19,10 +19,6 @@ qt_qml_path = "$QTDIR/qml"
qt_deploy_qml_path = "/qt/qml" qt_deploy_qml_path = "/qt/qml"
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def preload_file(source, destination): def preload_file(source, destination):
preload_files.append({"source": source, "destination": destination}) preload_files.append({"source": source, "destination": destination})
@ -55,24 +51,23 @@ def extract_preload_files_from_imports(imports):
) )
preload_file(qmldir_source_path, qmldir_destination_path) preload_file(qmldir_source_path, qmldir_destination_path)
except Exception as e: except Exception as e:
eprint(e)
continue continue
return libraries return libraries
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) != 4: if len(sys.argv) != 5:
print("Usage: python preload_qml_imports.py <qml-source-path> <qt-host-path> <qt-wasm-path>") print("Usage: python preload_qml_imports.py <qml-source-path> <qt-host-path> <qt-wasm-path> <output-dir>")
sys.exit(1) sys.exit(1)
qml_source_path = sys.argv[1] qml_source_path = sys.argv[1]
qt_host_path = sys.argv[2] qt_host_path = sys.argv[2]
qt_wasm_path = sys.argv[3] qt_wasm_path = sys.argv[3]
output_dir = sys.argv[4]
qml_import_path = os.path.join(qt_wasm_path, "qml") qml_import_path = os.path.join(qt_wasm_path, "qml")
qmlimportsscanner_path = os.path.join(qt_host_path, "libexec/qmlimportscanner") qmlimportsscanner_path = os.path.join(qt_host_path, "libexec/qmlimportscanner")
eprint("runing qmlimportsscanner")
command = [qmlimportsscanner_path, "-rootPath", qml_source_path, "-importPath", qml_import_path] command = [qmlimportsscanner_path, "-rootPath", qml_source_path, "-importPath", qml_import_path]
result = subprocess.run(command, stdout=subprocess.PIPE) result = subprocess.run(command, stdout=subprocess.PIPE)
imports = json.loads(result.stdout) imports = json.loads(result.stdout)
@ -98,4 +93,6 @@ if __name__ == "__main__":
destination = os.path.join("/", library) destination = os.path.join("/", library)
preload_file(source, destination) preload_file(source, destination)
print(json.dumps(preload_files, indent=2)) with open(f"{output_dir}/qt_qml_imports.json", "w") as f:
f.write(json.dumps(preload_files, indent=2))

View File

@ -27,11 +27,12 @@ def find_so_files(directory):
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) != 2: if len(sys.argv) != 3:
print("Usage: python make_qt_symlinks.py <qt-wasm-path>") print("Usage: python preload_qt_plugins.py <qt-wasm-path> <output-dir>")
sys.exit(1) sys.exit(1)
qt_wasm_path = sys.argv[1] qt_wasm_path = sys.argv[1]
output_dir = sys.argv[2]
# preload all plugins # preload all plugins
plugins = find_so_files(os.path.join(qt_wasm_path, "plugins")) plugins = find_so_files(os.path.join(qt_wasm_path, "plugins"))
@ -47,8 +48,10 @@ if __name__ == "__main__":
# and QML imports in /qt/plugins and /qt/qml. The qt.conf file is # and QML imports in /qt/plugins and /qt/qml. The qt.conf file is
# written to the current directory. # written to the current directory.
qtconf = "[Paths]\nPrefix = /qt\n" qtconf = "[Paths]\nPrefix = /qt\n"
with open("qt.conf", "w") as f: with open(f"{output_dir}/qt.conf", "w") as f:
f.write(qtconf) f.write(qtconf)
preload.append({"source": "qt.conf", "destination": "/qt.conf"}) preload.append({"source": "qt.conf", "destination": "/qt.conf"})
print(json.dumps(preload, indent=2)) with open(f"{output_dir}/qt_plugins.json", "w") as f:
f.write(json.dumps(preload, indent=2))