From 4972fdb350fe79e18b0413e74028cd9b9803f96b Mon Sep 17 00:00:00 2001 From: Lorn Potter Date: Thu, 3 Sep 2020 18:29:41 +1000 Subject: [PATCH] wasm: add cmake build support A few configure defines get changed: QMAKE_WASM_PTHREAD_POOL_SIZE is now QT_WASM_PTHREAD_POOL_SIZE QMAKE_WASM_TOTAL_MEMORY is now QT_WASM_INITIAL_MEMORY QMAKE_WASM_SOURCE_MAP_BASE is now QT_WASM_SOURCE_MAP_BASE device-option EMSCRIPTEN_ASYNCIFY=1 is QT_EMSCRIPTEN_ASYNCIFY=1 To create source maps for debugging. use device-option QT_WASM_SOURCE_MAP=1 Task-number: QTBUG-78647 Change-Id: If9f30cd7fb408c386d6d69b5f7b1beecf1ab44b5 Reviewed-by: Alexandru Croitor --- .prev_configure.cmake | 12 ++- CMakeLists.txt | 6 ++ cmake/QtAutoDetect.cmake | 59 ++++++++++++ cmake/QtBaseConfigureTests.cmake | 2 +- cmake/QtBaseGlobalTargets.cmake | 1 + cmake/QtBuild.cmake | 2 +- .../QtBuildInternalsConfig.cmake | 2 +- cmake/QtCompilerOptimization.cmake | 9 +- cmake/QtExecutableHelpers.cmake | 3 + cmake/QtFlagHandlingHelpers.cmake | 4 +- cmake/QtModuleHelpers.cmake | 4 +- cmake/QtPlatformSupport.cmake | 2 +- cmake/QtPluginHelpers.cmake | 4 +- cmake/QtPriHelpers.cmake | 5 +- cmake/QtWasmHelpers.cmake | 92 +++++++++++++++++++ configure.cmake | 23 ++++- configure.json | 15 +-- mkspecs/features/wasm/wasm.prf | 24 ++--- mkspecs/wasm-emscripten/qmake.conf | 12 +-- src/corelib/CMakeLists.txt | 4 + src/corelib/Qt6CoreConfigExtras.cmake.in | 4 + src/corelib/Qt6CoreMacros.cmake | 3 + src/corelib/Qt6WasmMacros.cmake | 31 +++++++ src/gui/configure.cmake | 9 ++ src/plugins/platforms/CMakeLists.txt | 2 +- src/plugins/platforms/wasm/CMakeLists.txt | 78 ++++++++++++++++ 26 files changed, 370 insertions(+), 42 deletions(-) create mode 100644 cmake/QtWasmHelpers.cmake create mode 100644 src/corelib/Qt6WasmMacros.cmake create mode 100644 src/plugins/platforms/wasm/CMakeLists.txt diff --git a/.prev_configure.cmake b/.prev_configure.cmake index 3d76317909d..f796eddc8a7 100644 --- a/.prev_configure.cmake +++ b/.prev_configure.cmake @@ -341,7 +341,7 @@ qt_feature("android-style-assets" PRIVATE ) qt_feature("shared" PUBLIC LABEL "Building shared libraries" - AUTODETECT NOT UIKIT + AUTODETECT NOT UIKIT AND NOT WASM CONDITION BUILD_SHARED_LIBS ) qt_feature_definition("shared" "QT_STATIC" NEGATE PREREQUISITE "!defined(QT_SHARED) && !defined(QT_STATIC)") @@ -837,7 +837,7 @@ qt_feature_definition("concurrent" "QT_NO_CONCURRENT" NEGATE VALUE "1") qt_feature("dbus" PUBLIC PRIVATE LABEL "Qt D-Bus" AUTODETECT NOT UIKIT AND NOT ANDROID - CONDITION QT_FEATURE_thread + CONDITION QT_FEATURE_thread AND NOT WASM ) qt_feature_definition("dbus" "QT_NO_DBUS" NEGATE VALUE "1") qt_feature("dbus-linked" PRIVATE @@ -866,7 +866,7 @@ qt_feature("printsupport" PRIVATE ) qt_feature("sql" PRIVATE LABEL "Qt Sql" - CONDITION QT_FEATURE_thread + CONDITION QT_FEATURE_thread AND NOT WASM ) qt_feature("testlib" PRIVATE LABEL "Qt Testlib" @@ -1034,12 +1034,18 @@ qt_configure_add_summary_entry(ARGS "pkg-config") qt_configure_add_summary_entry(ARGS "libudev") qt_configure_add_summary_entry(ARGS "system-zlib") qt_configure_add_summary_entry(ARGS "zstd") +qt_configure_add_summary_entry(ARGS "thread") qt_configure_end_summary_section() # end of "Support enabled for" section qt_configure_add_report_entry( TYPE NOTE MESSAGE "Using static linking will disable the use of dynamically loaded plugins. Make sure to import all needed static plugins, or compile needed modules into the library." CONDITION NOT QT_FEATURE_shared ) +qt_configure_add_report_entry( + TYPE NOTE + MESSAGE "Using pthreads" + CONDITION QT_FEATURE_thread +) qt_configure_add_report_entry( TYPE ERROR MESSAGE "Debug build wihtout Release build is not currently supported on ios see QTBUG-71990. Use -debug-and-release." diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e05aa52e80..18eaf95c509 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,12 @@ if(NOT QT_BUILD_STANDALONE_TESTS) # Needed when building qtbase for android. include(src/corelib/Qt6AndroidMacros.cmake) + if(WASM) + # Needed when building for WebAssembly. + include(cmake/QtWasmHelpers.cmake) + qt_internal_setup_wasm_target_properties(Platform) + endif() + # Set up optimization flags like in qmake. # This function must be called after the global QT_FEATURE_xxx variables have been set up, # aka after QtBaseGlobalTargets is processed. diff --git a/cmake/QtAutoDetect.cmake b/cmake/QtAutoDetect.cmake index d3173dfad06..b32258e929e 100644 --- a/cmake/QtAutoDetect.cmake +++ b/cmake/QtAutoDetect.cmake @@ -5,6 +5,64 @@ # Make sure to not run detection when building standalone tests, because the detection was already # done when initially configuring qtbase. + +function(qt_auto_detect_wasm) + if("${QT_QMAKE_TARGET_MKSPEC}" STREQUAL "wasm-emscripten" AND DEFINED ENV{EMSDK}) + if(NOT DEFINED QT_AUTODETECT_WASM) + set(QT_AUTODETECT_WASM TRUE CACHE BOOL "") + # detect EMSCRIPTEN_ROOT path + file(READ "$ENV{EMSDK}/.emscripten" ver) + string(REGEX MATCH "EMSCRIPTEN_ROOT.*$" EMROOT "${ver}") + string(REGEX MATCH "'([^' ]*)'" EMROOT2 "${EMROOT}") + string(REPLACE "'" "" EMROOT_PATH "${EMROOT2}") + + # get emscripten version + if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + set (EXECUTE_COMMANDPATH "$ENV{EMSDK}/${EMROOT_PATH}/emcc.bat") + else() + set (EXECUTE_COMMANDPATH "$ENV{EMSDK}/${EMROOT_PATH}/emcc") + endif() + + file(TO_NATIVE_PATH "${EXECUTE_COMMANDPATH}" EXECUTE_COMMAND) + execute_process(COMMAND ${EXECUTE_COMMAND} --version + OUTPUT_VARIABLE emOutput + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_VARIABLE emrun_error + RESULT_VARIABLE result) + if(NOT emOutput) + message(FATAL_ERROR + "Can't determine Emscripten version! Error: ${emrun_error}") + endif() + string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" CMAKE_EMSDK_REGEX_VERSION "${emOutput}") + set(EMCC_VERSION "${CMAKE_EMSDK_REGEX_VERSION}" CACHE STRING INTERNAL FORCE) + + # find toolchain file + if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) + set(wasm_toolchain_file "$ENV{EMSDK}/${EMROOT_PATH}/cmake/Modules/Platform/Emscripten.cmake") + set(CMAKE_TOOLCHAIN_FILE "${wasm_toolchain_file}" CACHE STRING "" FORCE) + endif() + + if(EXISTS "${CMAKE_TOOLCHAIN_FILE}") + message(STATUS "Emscripten ${CMAKE_EMSDK_REGEX_VERSION} toolchain file detected at ${CMAKE_TOOLCHAIN_FILE}") + else() + message(FATAL_ERROR "Cannot find the toolchain file Emscripten.cmake. " + "Please specify the toolchain file with -DCMAKE_TOOLCHAIN_FILE=.") + endif() + + if(NOT DEFINED BUILD_SHARED_LIBS) + set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build Qt statically or dynamically" FORCE) + endif() + + if(BUILD_SHARED_LIBS) + message(FATAL_ERROR + "Building Qt for ${CMAKE_SYSTEM_NAME} as shared libraries is not supported.") + endif() + # this version of Qt needs this version of emscripten + set(QT_EMCC_RECOMMENDED_VERSION 2.0.14 CACHE STRING INTERNAL FORCE) + endif() + endif() +endfunction() + function(qt_auto_detect_cmake_generator) if(NOT CMAKE_GENERATOR MATCHES "Ninja" AND NOT QT_SILENCE_CMAKE_GENERATOR_WARNING) message(WARNING @@ -340,3 +398,4 @@ qt_auto_detect_ios() qt_auto_detect_android() qt_auto_detect_vpckg() qt_auto_detect_pch() +qt_auto_detect_wasm() diff --git a/cmake/QtBaseConfigureTests.cmake b/cmake/QtBaseConfigureTests.cmake index cbd482bd59e..67cee65d4a3 100644 --- a/cmake/QtBaseConfigureTests.cmake +++ b/cmake/QtBaseConfigureTests.cmake @@ -32,7 +32,7 @@ function(qt_run_config_test_architecture) # With emscripten the application entry point is a .js file (to be run with node for example), # but the real "data" is in the .wasm file, so that's where we need to look for the ABI, etc. # information. - if (EMSCRIPTEN) + if (WASM) set(_arch_file_suffix ".wasm") endif() diff --git a/cmake/QtBaseGlobalTargets.cmake b/cmake/QtBaseGlobalTargets.cmake index 03f4b4d76a2..580e84f80a9 100644 --- a/cmake/QtBaseGlobalTargets.cmake +++ b/cmake/QtBaseGlobalTargets.cmake @@ -214,6 +214,7 @@ qt_copy_or_install(FILES cmake/QtTestHelpers.cmake cmake/QtToolchainHelpers.cmake cmake/QtToolHelpers.cmake + cmake/QtWasmHelpers.cmake cmake/QtWrapperScriptHelpers.cmake cmake/QtWriteArgsFile.cmake DESTINATION "${__GlobalConfig_install_dir}" diff --git a/cmake/QtBuild.cmake b/cmake/QtBuild.cmake index 9766e7f683f..f31a215a4af 100644 --- a/cmake/QtBuild.cmake +++ b/cmake/QtBuild.cmake @@ -302,7 +302,7 @@ elseif(IOS) set(QT_DEFAULT_MKSPEC macx-ios-clang) elseif(APPLE) set(QT_DEFAULT_MKSPEC macx-clang) -elseif(EMSCRIPTEN) +elseif(WASM) set(QT_DEFAULT_MKSPEC wasm-emscripten) elseif(QNX) # Certain POSIX defines are not set if we don't compile with -std=gnuXX diff --git a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake index 9cd5c6ecfbb..6192d809a36 100644 --- a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake +++ b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake @@ -120,7 +120,7 @@ function(qt_build_internals_disable_pkg_config_if_needed) set(pkg_config_enabled ON) qt_build_internals_find_pkg_config_executable() - if(APPLE OR WIN32 OR QNX OR ANDROID OR (NOT PKG_CONFIG_EXECUTABLE)) + if(APPLE OR WIN32 OR QNX OR ANDROID OR WASM OR (NOT PKG_CONFIG_EXECUTABLE)) set(pkg_config_enabled OFF) endif() diff --git a/cmake/QtCompilerOptimization.cmake b/cmake/QtCompilerOptimization.cmake index ed6921da532..c8c3da78d56 100644 --- a/cmake/QtCompilerOptimization.cmake +++ b/cmake/QtCompilerOptimization.cmake @@ -136,7 +136,7 @@ endif() # TODO: Missing mkspecs flags we don't handle below: win32-clang-g++, win32-clang-msvc, rtems-base # # gcc and clang base -if(GCC OR CLANG) +if(GCC OR CLANG AND NOT WASM) set(QT_CFLAGS_OPTIMIZE "-O2") set(QT_CFLAGS_OPTIMIZE_FULL "-O3") set(QT_CFLAGS_OPTIMIZE_DEBUG "-Og") @@ -187,3 +187,10 @@ if(ICC) set(QT_CFLAGS_OPTIMIZE_SIZE "-Os") endif() endif() + +if(WASM) + set(QT_CFLAGS_OPTIMIZE "-O2") + set(QT_CFLAGS_OPTIMIZE_FULL "-O3") + set(QT_CFLAGS_OPTIMIZE_SIZE "-Os") + set(QT_CFLAGS_OPTIMIZE_DEBUG "-g2") +endif() diff --git a/cmake/QtExecutableHelpers.cmake b/cmake/QtExecutableHelpers.cmake index d96f2edaa12..c25a9ae8a36 100644 --- a/cmake/QtExecutableHelpers.cmake +++ b/cmake/QtExecutableHelpers.cmake @@ -33,6 +33,9 @@ function(qt_internal_add_executable name) PROPERTY EXCLUDE_FROM_ALL "$>") endif() + if(WASM) + qt6_wasm_add_target_helpers("${name}") + endif() if (arg_VERSION) if(arg_VERSION MATCHES "[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+") # nothing to do diff --git a/cmake/QtFlagHandlingHelpers.cmake b/cmake/QtFlagHandlingHelpers.cmake index 003e1a45ecd..cbc39cbf1a9 100644 --- a/cmake/QtFlagHandlingHelpers.cmake +++ b/cmake/QtFlagHandlingHelpers.cmake @@ -88,7 +88,7 @@ function(qt_internal_apply_gc_binaries target visibility) message(FATAL_ERROR "Visibitily setting must be one of PRIVATE, INTERFACE or PUBLIC.") endif() - if ((GCC OR CLANG) AND NOT EMSCRIPTEN AND NOT UIKIT AND NOT MSVC) + if ((GCC OR CLANG) AND NOT WASM AND NOT UIKIT AND NOT MSVC) if(APPLE) set(gc_sections_flag "-Wl,-dead_strip") elseif(SOLARIS) @@ -101,7 +101,7 @@ function(qt_internal_apply_gc_binaries target visibility) target_link_options("${target}" ${visibility} "${gc_sections_flag}") endif() - if((GCC OR CLANG OR ICC) AND NOT EMSCRIPTEN AND NOT UIKIT AND NOT MSVC) + if((GCC OR CLANG OR ICC) AND NOT WASM AND NOT UIKIT AND NOT MSVC) set(split_sections_flags "-ffunction-sections" "-fdata-sections") endif() if(split_sections_flags) diff --git a/cmake/QtModuleHelpers.cmake b/cmake/QtModuleHelpers.cmake index 4a3237d4eaa..95272d3932b 100644 --- a/cmake/QtModuleHelpers.cmake +++ b/cmake/QtModuleHelpers.cmake @@ -552,7 +552,9 @@ set(QT_CMAKE_EXPORT_NAMESPACE ${QT_CMAKE_EXPORT_NAMESPACE})") PRIVATE_HEADER DESTINATION ${INSTALL_INCLUDEDIR}/${module_include_name}/${PROJECT_VERSION}/${module}/private ) - qt_apply_rpaths(TARGET "${target}" INSTALL_PATH "${INSTALL_LIBDIR}" RELATIVE_RPATH) + if(BUILD_SHARED_LIBS) + qt_apply_rpaths(TARGET "${target}" INSTALL_PATH "${INSTALL_LIBDIR}" RELATIVE_RPATH) + endif() if (ANDROID AND NOT arg_HEADER_MODULE) # Record install library location so it can be accessed by diff --git a/cmake/QtPlatformSupport.cmake b/cmake/QtPlatformSupport.cmake index 02c0c0fb632..94ff238418b 100644 --- a/cmake/QtPlatformSupport.cmake +++ b/cmake/QtPlatformSupport.cmake @@ -16,7 +16,7 @@ qt_set01(QNX CMAKE_SYSTEM_NAME STREQUAL "QNX") # FIXME: How to identify this? qt_set01(OPENBSD CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") # FIXME: How to identify this? qt_set01(FREEBSD CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") # FIXME: How to identify this? qt_set01(NETBSD CMAKE_SYSTEM_NAME STREQUAL "NetBSD") # FIXME: How to identify this? -qt_set01(WASM CMAKE_SYSTEM_NAME STREQUAL "Emscripten") +qt_set01(WASM CMAKE_SYSTEM_NAME STREQUAL "Emscripten" OR EMSCRIPTEN) qt_set01(BSD APPLE OR OPENBSD OR FREEBSD OR NETBSD) diff --git a/cmake/QtPluginHelpers.cmake b/cmake/QtPluginHelpers.cmake index e2289a0561a..86d59cb8ab9 100644 --- a/cmake/QtPluginHelpers.cmake +++ b/cmake/QtPluginHelpers.cmake @@ -338,7 +338,9 @@ function(qt_internal_add_plugin target) NAMESPACE ${QT_CMAKE_EXPORT_NAMESPACE}:: DESTINATION "${config_install_dir}" ) - qt_apply_rpaths(TARGET "${target}" INSTALL_PATH "${install_directory}" RELATIVE_RPATH) + if(BUILD_SHARED_LIBS) + qt_apply_rpaths(TARGET "${target}" INSTALL_PATH "${install_directory}" RELATIVE_RPATH) + endif() endif() if (NOT arg_ALLOW_UNDEFINED_SYMBOLS) diff --git a/cmake/QtPriHelpers.cmake b/cmake/QtPriHelpers.cmake index 47be8a6194f..e425a4ec2b9 100644 --- a/cmake/QtPriHelpers.cmake +++ b/cmake/QtPriHelpers.cmake @@ -563,7 +563,6 @@ QT_PATCH_VERSION = ${PROJECT_VERSION_PATCH} list(APPEND extra_statements "QT_LIBINFIX = ${QT_LIBINFIX}") endif() - # TODO: Add QT_EMCC_VERSION when WASM is ported over. if(APPLECLANG) set(compiler_version_major_var_name "QT_APPLE_CLANG_MAJOR_VERSION") set(compiler_version_minor_var_name "QT_APPLE_CLANG_MINOR_VERSION") @@ -603,6 +602,10 @@ QT_PATCH_VERSION = ${PROJECT_VERSION_PATCH} list(APPEND extra_statements "QT_EDITION = Open Source") + if(WASM) + list(APPEND extra_statements + "QT_EMCC_VERSION = ${EMCC_VERSION}") + endif() if(extra_statements) string(REPLACE ";" "\n" extra_statements "${extra_statements}") string(APPEND content "\n${extra_statements}\n") diff --git a/cmake/QtWasmHelpers.cmake b/cmake/QtWasmHelpers.cmake new file mode 100644 index 00000000000..1bbe5c928f9 --- /dev/null +++ b/cmake/QtWasmHelpers.cmake @@ -0,0 +1,92 @@ + +function (qt_internal_setup_wasm_target_properties wasmTarget) + + target_link_options("${wasmTarget}" INTERFACE "SHELL:-s EXIT_RUNTIME=1" + "SHELL:-s ERROR_ON_UNDEFINED_SYMBOLS=1" + "SHELL:-s EXTRA_EXPORTED_RUNTIME_METHODS=[UTF16ToString,stringToUTF16]" + "SHELL:-s USE_WEBGL2=1" + "--bind" + "SHELL:-s FETCH=1") + + # Hardcode wasm memory size. Emscripten does not currently support memory growth + # (ALLOW_MEMORY_GROWTH) in pthreads mode, and requires specifying the memory size + # at build time. Further, browsers limit the maximum initial memory size to 1GB. + # QT_WASM_INITIAL_MEMORY must be a multiple of 64KB (i.e. 65536) + if(NOT DEFINED QT_WASM_INITIAL_MEMORY AND QT_FEATURE_thread) + set(QT_WASM_INITIAL_MEMORY "1GB") + endif() + + if(DEFINED QT_WASM_INITIAL_MEMORY) + target_link_options("${wasmTarget}" INTERFACE "SHELL:-s INITIAL_MEMORY=${QT_WASM_INITIAL_MEMORY}") + message("Setting INITIAL_MEMORY to ${QT_WASM_INITIAL_MEMORY}") + endif() + + if (QT_FEATURE_opengles3) + target_link_options("${wasmTarget}" INTERFACE "SHELL:-s FULL_ES3=1") + + target_link_options("${wasmTarget}" INTERFACE "SHELL:-s FULL_ES3=1" + "SHELL:-s MAX_WEBGL_VERSION=2" + "SHELL:-s WEBGL2_BACKWARDS_COMPATIBILITY_EMULATION=1") + else() + target_link_options("${wasmTarget}" INTERFACE "SHELL:-s FULL_ES2=1") + endif() + + set(disable_exceptions_catching 1) + if (QT_FEATURE_exceptions) + set(disable_exceptions_catching 0) + endif() + target_link_options("${wasmTarget}" INTERFACE "SHELL:-s DISABLE_EXCEPTION_CATCHING=${disable_exceptions_catching}") + + if (QT_FEATURE_thread) + target_compile_options("${wasmTarget}" INTERFACE "SHELL:-s USE_PTHREADS=1") + target_link_options("${wasmTarget}" INTERFACE "SHELL:-s USE_PTHREADS=1") + + if(DEFINED QT_WASM_PTHREAD_POOL_SIZE) + target_link_options("${wasmTarget}" INTERFACE "SHELL:-s PTHREAD_POOL_SIZE=${QT_WASM_PTHREAD_POOL_SIZE}") + message("Setting PTHREAD_POOL_SIZE to ${QT_WASM_PTHREAD_POOL_SIZE}") + endif() + + else() + target_link_options("${wasmTarget}" INTERFACE "SHELL:-s ALLOW_MEMORY_GROWTH=1") + endif() + + # debug add_compile_options + if ("QT_WASM_SOURCE_MAP=1" IN_LIST QT_QMAKE_DEVICE_OPTIONS) + set(WASM_SOURCE_MAP_BASE "http://localhost:8000/") + + if(DEFINED QT_WASM_SOURCE_MAP_BASE) + set(WASM_SOURCE_MAP_BASE "${QT_WASM_SOURCE_MAP_BASE}") + endif() + + # Pass --source-map-base on the linker line. This informs the + # browser where to find the source files when debugging. + # -g4 to make source maps for debugging + target_link_options("${wasmTarget}" INTERFACE "-g4" "--source-map-base" "${WASM_SOURCE_MAP_BASE}") + + endif() + + # a few good defaults to make console more verbose while debugging + target_link_options("${wasmTarget}" INTERFACE $<$: + SHELL:"-s DEMANGLE_SUPPORT=1" + SHELL:"-s GL_DEBUG=1" + SHELL:"-s ASSERTIONS=2" + --profiling-funcs>) + + # target_link_options("${wasmTarget}" INTERFACE "SHELL:-s LIBRARY_DEBUG=1") # print out library calls, verbose + # target_link_options("${wasmTarget}" INTERFACE "SHELL:-s SYSCALL_DEBUG=1") # print out sys calls, verbose + # target_link_options("${wasmTarget}" INTERFACE "SHELL:-s FS_LOG=1") # print out filesystem ops, verbose + # target_link_options("${wasmTarget}" INTERFACE "SHELL:-s SOCKET_DEBUG") # print out socket,network data transfer + + if ("QT_EMSCRIPTEN_ASYNCIFY=1" IN_LIST QT_QMAKE_DEVICE_OPTIONS) + + # Emscripten recommends building with optimizations when using asyncify + # in order to reduce wasm file size, and may also generate broken wasm + # (with "wasm validation error: too many locals" type errors) if optimizations + # are omitted. Enable optimizations also for debug builds. + set(QT_CFLAGS_OPTIMIZE_DEBUG "-Os" CACHE STRING INTERNAL FORCE) + set(QT_FEATURE_optimize_debug ON CACHE BOOL INTERNAL FORCE) + + target_link_options("${wasmTarget}" INTERFACE "SHELL:-s ASYNCIFY" "-Os") + target_compile_definitions("${wasmTarget}" INTERFACE QT_HAVE_EMSCRIPTEN_ASYNCIFY) + endif() +endfunction() diff --git a/configure.cmake b/configure.cmake index 7d015717ed5..b4774999156 100644 --- a/configure.cmake +++ b/configure.cmake @@ -339,7 +339,7 @@ qt_feature("android-style-assets" PRIVATE ) qt_feature("shared" PUBLIC LABEL "Building shared libraries" - AUTODETECT NOT UIKIT + AUTODETECT NOT UIKIT AND NOT WASM CONDITION BUILD_SHARED_LIBS ) qt_feature_definition("shared" "QT_STATIC" NEGATE PREREQUISITE "!defined(QT_SHARED) && !defined(QT_STATIC)") @@ -600,6 +600,7 @@ qt_feature("c11" PUBLIC qt_feature("precompile_header" LABEL "Using precompiled headers" CONDITION BUILD_WITH_PCH + AUTODETECT NOT WASM ) qt_feature_config("precompile_header" QMAKE_PRIVATE_CONFIG) set(__qt_ltcg_detected FALSE) @@ -848,7 +849,7 @@ qt_feature_definition("concurrent" "QT_NO_CONCURRENT" NEGATE VALUE "1") qt_feature("dbus" PUBLIC PRIVATE LABEL "Qt D-Bus" AUTODETECT NOT UIKIT AND NOT ANDROID - CONDITION QT_FEATURE_thread + CONDITION QT_FEATURE_thread AND NOT WASM ) qt_feature_definition("dbus" "QT_NO_DBUS" NEGATE VALUE "1") qt_feature("dbus-linked" PRIVATE @@ -877,7 +878,7 @@ qt_feature("printsupport" PRIVATE ) qt_feature("sql" PRIVATE LABEL "Qt Sql" - CONDITION QT_FEATURE_thread + CONDITION QT_FEATURE_thread AND NOT WASM ) qt_feature("testlib" PRIVATE LABEL "Qt Testlib" @@ -1050,6 +1051,7 @@ qt_configure_add_summary_entry(ARGS "pkg-config") qt_configure_add_summary_entry(ARGS "libudev") qt_configure_add_summary_entry(ARGS "system-zlib") qt_configure_add_summary_entry(ARGS "zstd") +qt_configure_add_summary_entry(ARGS "thread") qt_configure_end_summary_section() # end of "Support enabled for" section qt_configure_add_report_entry( TYPE NOTE @@ -1094,9 +1096,22 @@ qt_configure_add_report_entry( MESSAGE "Setting a library infix is not supported for framework builds." CONDITION QT_FEATURE_framework AND DEFINED QT_LIBINFIX ) +qt_configure_add_report_entry( + TYPE NOTE + MESSAGE "Using pthreads" + CONDITION QT_FEATURE_thread +) +qt_configure_add_report_entry( + TYPE WARNING + MESSAGE "You should use the recommended Wasm version ${QT_EMCC_RECOMMENDED_VERSION} with this Qt. You have ${EMCC_VERSION}." + CONDITION WASM AND NOT ${EMCC_VERSION} MATCHES ${QT_EMCC_RECOMMENDED_VERSION} +) +if(WASM) + qt_extra_definition("QT_EMCC_VERSION" "\"${EMCC_VERSION}\"" PUBLIC) +endif() # special case end - qt_extra_definition("QT_VERSION_STR" "\"${PROJECT_VERSION}\"" PUBLIC) qt_extra_definition("QT_VERSION_MAJOR" ${PROJECT_VERSION_MAJOR} PUBLIC) qt_extra_definition("QT_VERSION_MINOR" ${PROJECT_VERSION_MINOR} PUBLIC) qt_extra_definition("QT_VERSION_PATCH" ${PROJECT_VERSION_PATCH} PUBLIC) + diff --git a/configure.json b/configure.json index c6790eab780..b656ad1bdaf 100644 --- a/configure.json +++ b/configure.json @@ -677,7 +677,7 @@ }, "shared": { "label": "Building shared libraries", - "autoDetect": "!config.uikit", + "autoDetect": "!config.uikit && !config.wasm", "condition": "!config.integrity && !config.wasm && !config.rtems", "output": [ "shared", @@ -1010,6 +1010,7 @@ "precompile_header": { "label": "Using precompiled headers", "condition": "tests.precompile_header", + "autodetect": "!config.wasm", "output": [ "privateConfig", { "type": "varRemove", "negative": true, "name": "CONFIG", "value": "'precompile_header'" } @@ -1337,7 +1338,7 @@ "dbus": { "label": "Qt D-Bus", "autoDetect": "!config.uikit && !config.android", - "condition": "features.thread", + "condition": "features.thread && !config.wasm", "output": [ "privateFeature", "feature" ] }, "dbus-linked": { @@ -1378,7 +1379,7 @@ }, "sql": { "label": "Qt Sql", - "condition": "features.thread", + "condition": "features.thread && !config.wasm", "output": [ "privateFeature" ] }, "testlib": { @@ -1512,8 +1513,9 @@ "message": "Qt requires a compliant STL library." }, { - "type": "emccVersion", - "condition": "config.wasm" + "type": "note", + "condition": "features.thread", + "message": "Using pthreads" }, { "type": "error", @@ -1673,7 +1675,8 @@ "pkg-config", "libudev", "system-zlib", - "zstd" + "zstd", + "thread" ] } ] diff --git a/mkspecs/features/wasm/wasm.prf b/mkspecs/features/wasm/wasm.prf index 2e886fc4a5f..882d2e49c82 100644 --- a/mkspecs/features/wasm/wasm.prf +++ b/mkspecs/features/wasm/wasm.prf @@ -13,8 +13,8 @@ exists($$QMAKE_QT_CONFIG) { # Create worker threads at startup. This is supposed to be an optimization, # however exceeding the pool size has been obesverved to hang the application. POOL_SIZE = 4 - !isEmpty(QMAKE_WASM_PTHREAD_POOL_SIZE) { - POOL_SIZE = $$QMAKE_WASM_PTHREAD_POOL_SIZE + !isEmpty(QT_WASM_PTHREAD_POOL_SIZE) { + POOL_SIZE = $$QT_WASM_PTHREAD_POOL_SIZE } message("Setting PTHREAD_POOL_SIZE to" $$POOL_SIZE) @@ -23,18 +23,18 @@ exists($$QMAKE_QT_CONFIG) { EMCC_THREAD_LFLAGS += -s ALLOW_MEMORY_GROWTH=1 } - qtConfig(thread) | !isEmpty(QMAKE_WASM_TOTAL_MEMORY) { + qtConfig(thread) | !isEmpty(QT_WASM_INITIAL_MEMORY) { # Hardcode wasm memory size. Emscripten does not currently support memory growth # (ALLOW_MEMORY_GROWTH) in pthreads mode, and requires specifying the memory size # at build time. Further, browsers limit the maximum initial memory size to 1GB. - # QMAKE_WASM_TOTAL_MEMORY must be a multiple of 64KB - TOTAL_MEMORY = 1GB - !isEmpty(QMAKE_WASM_TOTAL_MEMORY) { - TOTAL_MEMORY = $$QMAKE_WASM_TOTAL_MEMORY + # QT_WASM_INITIAL_MEMORY must be a multiple of 64KB + INITIAL_MEMORY = 1GB + !isEmpty(QT_WASM_INITIAL_MEMORY) { + INITIAL_MEMORY = $$QT_WASM_INITIAL_MEMORY } - message("Setting TOTAL_MEMORY to" $$TOTAL_MEMORY) - EMCC_THREAD_LFLAGS += -s TOTAL_MEMORY=$$TOTAL_MEMORY + message("Setting INITIAL_MEMORY to" $$INITIAL_MEMORY) + EMCC_THREAD_LFLAGS += -s INITIAL_MEMORY=$$INITIAL_MEMORY } QMAKE_LFLAGS += $$EMCC_THREAD_LFLAGS QMAKE_LFLAGS_DEBUG += $$EMCC_THREAD_LFLAGS @@ -117,9 +117,9 @@ qtConfTest_emccVersion() # Pass --source-map-base on the linker line. This informs the # browser where to find the source files when debugging. WASM_SOURCE_MAP_BASE = http://localhost:8000/ -!isEmpty(QMAKE_WASM_SOURCE_MAP_BASE):\ - WASM_SOURCE_MAP_BASE = $$QMAKE_WASM_SOURCE_MAP_BASE -CONFIG(debug): QMAKE_LFLAGS += --source-map-base $$WASM_SOURCE_MAP_BASE +!isEmpty(QT_WASM_SOURCE_MAP_BASE):\ + WASM_SOURCE_MAP_BASE = $$QT_WASM_SOURCE_MAP_BASE +CONFIG(debug): QMAKE_LFLAGS += -g4 --source-map-base $$WASM_SOURCE_MAP_BASE # Creates the stand-alone version of the library from bitcode !static:contains(TEMPLATE, .*lib): { diff --git a/mkspecs/wasm-emscripten/qmake.conf b/mkspecs/wasm-emscripten/qmake.conf index 2b108a5e900..2788d925c40 100644 --- a/mkspecs/wasm-emscripten/qmake.conf +++ b/mkspecs/wasm-emscripten/qmake.conf @@ -8,11 +8,11 @@ include(../common/clang.conf) load(device_config) load(emcc_ver) -# Support enabling asyncify by configuring with "-device-option EMSCRIPTEN_ASYNCIFY=1" -!isEmpty(EMSCRIPTEN_ASYNCIFY): { - !equals(EMSCRIPTEN_ASYNCIFY, 1):!equals(EMSCRIPTEN_ASYNCIFY, 0): \ - message(Error: The value for EMSCRIPTEN_ASYNCIFY must be 0 or 1) - equals(EMSCRIPTEN_ASYNCIFY, 1): { +# Support enabling asyncify by configuring with "-device-option QT_EMSCRIPTEN_ASYNCIFY=1" +!isEmpty(QT_EMSCRIPTEN_ASYNCIFY): { + !equals(QT_EMSCRIPTEN_ASYNCIFY, 1):!equals(QT_EMSCRIPTEN_ASYNCIFY, 0): \ + message(Error: The value for QT_EMSCRIPTEN_ASYNCIFY must be 0 or 1) + equals(QT_EMSCRIPTEN_ASYNCIFY, 1): { QMAKE_CFLAGS += -DQT_HAVE_EMSCRIPTEN_ASYNCIFY QMAKE_CXXFLAGS += -DQT_HAVE_EMSCRIPTEN_ASYNCIFY QMAKE_LFLAGS += -s ASYNCIFY @@ -56,7 +56,7 @@ EMCC_COMMON_LFLAGS_DEBUG = \ -s GL_DEBUG=1 \ --profiling-funcs -QMAKE_LFLAGS_DEBUG += -g4 +QMAKE_LFLAGS_DEBUG += -g2 QMAKE_LFLAGS_RELEASE += -O2 QMAKE_COMPILER += emscripten diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 54b756d2a60..af6bba2dbe0 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -20,6 +20,10 @@ if(ANDROID) set(corelib_extra_cmake_files "${CMAKE_CURRENT_SOURCE_DIR}/${QT_CMAKE_EXPORT_NAMESPACE}AndroidMacros.cmake") endif() +if(WASM) + set(corelib_extra_cmake_files + "${CMAKE_CURRENT_SOURCE_DIR}/${QT_CMAKE_EXPORT_NAMESPACE}WasmMacros.cmake") +endif() # special case end ##################################################################### diff --git a/src/corelib/Qt6CoreConfigExtras.cmake.in b/src/corelib/Qt6CoreConfigExtras.cmake.in index e7892bbcc3f..25b3f5acd51 100644 --- a/src/corelib/Qt6CoreConfigExtras.cmake.in +++ b/src/corelib/Qt6CoreConfigExtras.cmake.in @@ -47,3 +47,7 @@ set(_Qt6CTestMacros "${_Qt6CoreConfigDir}/Qt6CTestMacros.cmake") if(ANDROID_PLATFORM) include("${CMAKE_CURRENT_LIST_DIR}/@QT_CMAKE_EXPORT_NAMESPACE@AndroidMacros.cmake") endif() + +if(EMSCRIPTEN) + include("${CMAKE_CURRENT_LIST_DIR}/@QT_CMAKE_EXPORT_NAMESPACE@WasmMacros.cmake") +endif() diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake index b4ffa545789..54edcd102f6 100644 --- a/src/corelib/Qt6CoreMacros.cmake +++ b/src/corelib/Qt6CoreMacros.cmake @@ -559,6 +559,9 @@ function(qt6_finalize_executable target) qt6_android_generate_deployment_settings("${target}") qt6_android_add_apk_target("${target}") endif() + if(EMSCRIPTEN) + qt_wasm_add_target_helpers("${target}") + endif() endfunction() if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) diff --git a/src/corelib/Qt6WasmMacros.cmake b/src/corelib/Qt6WasmMacros.cmake new file mode 100644 index 00000000000..e9c19160a37 --- /dev/null +++ b/src/corelib/Qt6WasmMacros.cmake @@ -0,0 +1,31 @@ + +function(qt6_wasm_add_target_helpers target) + # copy in Qt HTML/JS launch files for apps + get_target_property(targetType "${target}" TYPE) + if("${targetType}" STREQUAL "EXECUTABLE") + + set(APPNAME ${target}) + + if(QT6_INSTALL_PREFIX) + set(WASM_BUILD_DIR "${QT6_INSTALL_PREFIX}") + elseif(QT_BUILD_DIR) + set(WASM_BUILD_DIR "${QT_BUILD_DIR}") + endif() + + configure_file("${WASM_BUILD_DIR}/plugins/platforms/wasm_shell.html" + "${target}.html") + configure_file("${WASM_BUILD_DIR}/plugins/platforms/qtloader.js" + qtloader.js COPYONLY) + configure_file("${WASM_BUILD_DIR}/plugins/platforms/qtlogo.svg" + qtlogo.svg COPYONLY) + + endif() +endfunction() + +if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) + function(qt_wasm_add_target_helpers) + if(QT_DEFAULT_MAJOR_VERSION EQUAL 6) + qt6_wasm_add_target_helpers(${ARGV}) + endif() + endfunction() +endif() diff --git a/src/gui/configure.cmake b/src/gui/configure.cmake index 0e2482e3809..449bd838b7a 100644 --- a/src/gui/configure.cmake +++ b/src/gui/configure.cmake @@ -389,10 +389,19 @@ ioctl(fd, FBIOGET_VSCREENINFO, &vinfo); ") # opengles3 +# special case begin +if(WASM) + set(extra_compiler_options "-s FULL_ES3=1") +endif() +# special case end + qt_config_compile_test(opengles3 LABEL "OpenGL ES 3.0" LIBRARIES GLESv2::GLESv2 +# special case begin + COMPILE_OPTIONS ${extra_compiler_options} +# special case end CODE "#ifdef __APPLE__ # include diff --git a/src/plugins/platforms/CMakeLists.txt b/src/plugins/platforms/CMakeLists.txt index f6206942eaa..8ef533ad43e 100644 --- a/src/plugins/platforms/CMakeLists.txt +++ b/src/plugins/platforms/CMakeLists.txt @@ -47,7 +47,7 @@ if(HAIKU) # add_subdirectory(haiku) # special case TODO endif() if(WASM) - # add_subdirectory(wasm) # special case TODO + add_subdirectory(wasm) endif() if(QT_FEATURE_integrityfb) # add_subdirectory(integrity) # special case TODO diff --git a/src/plugins/platforms/wasm/CMakeLists.txt b/src/plugins/platforms/wasm/CMakeLists.txt new file mode 100644 index 00000000000..10e247b7ddc --- /dev/null +++ b/src/plugins/platforms/wasm/CMakeLists.txt @@ -0,0 +1,78 @@ +# Generated from wasm.pro. + +##################################################################### +## QWasmIntegrationPlugin Plugin: +##################################################################### + +qt_internal_add_plugin(QWasmIntegrationPlugin + OUTPUT_NAME qwasm + DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES wasm # special case + TYPE platforms + STATIC + SOURCES + main.cpp + qwasmclipboard.cpp qwasmclipboard.h + qwasmcompositor.cpp qwasmcompositor.h + qwasmcursor.cpp qwasmcursor.h + qwasmeventdispatcher.cpp qwasmeventdispatcher.h + qwasmeventtranslator.cpp qwasmeventtranslator.h + qwasmfontdatabase.cpp qwasmfontdatabase.h + qwasmintegration.cpp qwasmintegration.h + qwasmoffscreensurface.cpp qwasmoffscreensurface.h + qwasmopenglcontext.cpp qwasmopenglcontext.h + qwasmscreen.cpp qwasmscreen.h + qwasmservices.cpp qwasmservices.h + qwasmstring.cpp qwasmstring.h + qwasmstylepixmaps_p.h + qwasmtheme.cpp qwasmtheme.h + qwasmwindow.cpp qwasmwindow.h + DEFINES + QT_EGL_NO_X11 + QT_NO_FOREACH + PUBLIC_LIBRARIES + Qt::Core + Qt::CorePrivate + Qt::Gui + Qt::GuiPrivate +) + +# Resources: +set_source_files_properties("${QT_SOURCE_TREE}/src/3rdparty/wasm/Vera.ttf" PROPERTIES QT_RESOURCE_ALIAS "Vera.ttf") +set_source_files_properties("${QT_SOURCE_TREE}/src/3rdparty/wasm/DejaVuSans.ttf" PROPERTIES QT_RESOURCE_ALIAS "DejaVuSans.ttf") +set_source_files_properties("${QT_SOURCE_TREE}/src/3rdparty/wasm/DejaVuSansMono.ttf" PROPERTIES QT_RESOURCE_ALIAS "DejaVuSansMono.ttf") + +set(wasmfonts_resource_files + "${QT_SOURCE_TREE}/src/3rdparty/wasm/Vera.ttf" + "${QT_SOURCE_TREE}/src/3rdparty/wasm/DejaVuSans.ttf" + "${QT_SOURCE_TREE}/src/3rdparty/wasm/DejaVuSansMono.ttf" +) + +qt_internal_add_resource(QWasmIntegrationPlugin "wasmfonts" + PREFIX + "/fonts" + FILES + ${wasmfonts_resource_files} +) +qt_internal_extend_target(QWasmIntegrationPlugin CONDITION QT_FEATURE_opengl + SOURCES + qwasmbackingstore.cpp qwasmbackingstore.h + PUBLIC_LIBRARIES + Qt::OpenGL + Qt::OpenGLPrivate +) + +#### Keys ignored in scope 4:.:.:wasm.pro:NOT TARGET___equals____ss_QT_DEFAULT_QPA_PLUGIN: +# PLUGIN_EXTENDS = "-" + +qt_copy_or_install(FILES + wasm_shell.html + DESTINATION "${CMAKE_INSTALL_PREFIX}/plugins/platforms/" +) +qt_copy_or_install(FILES + qtloader.js + DESTINATION "${CMAKE_INSTALL_PREFIX}/plugins/platforms/" +) +qt_copy_or_install(FILES + qtlogo.svg + DESTINATION "${CMAKE_INSTALL_PREFIX}/plugins/platforms/" +)