QT配置WebAssembly¶
效果¶
https://survivor.beyondxin.top/

安装 emsdk¶
Qt 的每个次版本都以特定的 Emscripten 版本为目标,该版本在补丁发布时保持不变。Qt 的二进制包使用目标 Emscripten 版本构建。应用程序应使用相同的版本,因为 Emscripten 并不保证不同版本之间的ABI 兼容性,版本对应查看 Qt for WebAssembly 文档。
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
emsdk install 3.1.56
emsdk activate 3.1.56
QTC 配置 WebAssembly¶
使用 qt-online-installer 安装时候勾选 WebAssembly。(多线程)


CMake 配置¶
链接QT¶
moc qt_standard_project_setup() 会处理。
rcc 如果项目中有多个自己的 静态/动态库 ,需手动set(CMAKE_AUTORCC ON)
,只有一个 qt_standard_project_setup() 会处理。(原因未知)
set(CMAKE_AUTORCC ON)
set(BINDIR bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${BINDIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${BINDIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Svg Xml Multimedia OpenGL OpenGLWidgets MultimediaWidgets)
qt_standard_project_setup()
如果使用 wasm 必须指定 install¶
function(apply_app_optimizations target_name)
apply_compiler_optimizations(${target_name})
if(MSVC)
target_link_options(${target_name} PRIVATE
$<$<CONFIG:Release>:/LTCG> # 链接时代码生成
$<$<CONFIG:Release>:/OPT:REF> # 消除未引用函数和数据
$<$<CONFIG:Release>:/OPT:ICF> # 启用相同COMDAT折叠
$<$<CONFIG:Release>:/INCREMENTAL:NO> # 禁用增量链接
$<$<CONFIG:Release>:/SUBSYSTEM:WINDOWS> # Windows子系统
)
target_compile_options(${target_name} PRIVATE
$<$<CONFIG:Release>:/favor:INTEL64> # 针对Intel64优化
$<$<CONFIG:Release>:/Qvec-report:2> # 向量化报告
)
set_target_properties(${target_name} PROPERTIES
WIN32_EXECUTABLE TRUE
LINK_FLAGS_RELEASE "/ENTRY:mainCRTStartup"
)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_link_options(${target_name} PRIVATE
$<$<CONFIG:Release>:-flto> # 链接时优化
$<$<CONFIG:Release>:-Wl,--gc-sections> # 垃圾回收未使用段
)
endif()
if(EMSCRIPTEN)
set_target_properties(${target_name} PROPERTIES
LINK_FLAGS "--preload-file ${CMAKE_SOURCE_DIR}/data_wasm/assets_wasm.pak@assets.pak --preload-file ${CMAKE_SOURCE_DIR}/data_wasm/tables_wasm.pak@tables.pak"
)
include(GNUInstallDirs)
install(TARGETS ${target_name}
BUNDLE DESTINATION .
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
qt_generate_deploy_app_script(
TARGET SurvivorApp
OUTPUT_SCRIPT deploy_script
NO_UNSUPPORTED_PLATFORM_ERROR
)
install(SCRIPT ${deploy_script})
endif()
endfunction()
本地文件访问¶
文件系统访问在网络上是沙箱式的,相关资源(包括字体)都需要打包进 qrc 。如果资源太大用 rcc 打包成单独文件通过 --preload-file 挂载
if(EMSCRIPTEN)
set_target_properties(${target_name} PROPERTIES
LINK_FLAGS "--preload-file ${CMAKE_SOURCE_DIR}/data_wasm/assets_wasm.pak@assets.pak --preload-file ${CMAKE_SOURCE_DIR}/data_wasm/tables_wasm.pak@tables.pak"
)
判断是否为 wasm¶
# 平台检测和配置
function(configure_platform_headers)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "riscv|riscv64" OR CMAKE_CXX_COMPILER_ARCHITECTURE_ID MATCHES "riscv|riscv64")
set(IS_RISCV 1)
else()
set(IS_RISCV 0)
endif()
if(WIN32)
set(IS_WINDOWS 1)
else()
set(IS_WINDOWS 0)
endif()
if(UNIX AND NOT APPLE)
set(IS_LINUX 1)
else()
set(IS_LINUX 0)
endif()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|x64|AMD64" OR CMAKE_CXX_COMPILER_ARCHITECTURE_ID MATCHES "x86_64|amd64|x64|AMD64")
set(IS_X64 1)
else()
set(IS_X64 0)
endif()
if(EMSCRIPTEN)
set(IS_WASM 1)
else()
set(IS_WASM 0)
endif()
configure_file(
${CMAKE_SOURCE_DIR}/cmake/platform.h.in
${CMAKE_BINARY_DIR}/include/platform.h
)
configure_file(
${CMAKE_SOURCE_DIR}/cmake/version.h.in
${CMAKE_BINARY_DIR}/include/version.h
)
endfunction()
部署¶
编译后文件如下,额外准备: index.html 、favicon.ico。部署时候需要设置允许跨域头(wasm 需要),app.data 和 app.wasm 文件比较大,如果想体验效果好需要用cdn。

python 本地部署
import http.server
import socketserver
PORT = 8000
class CORSRequestHandler(http.server.SimpleHTTPRequestHandler):
def end_headers(self):
self.send_header('Cross-Origin-Opener-Policy', 'same-origin')
self.send_header('Cross-Origin-Embedder-Policy', 'require-corp')
super().end_headers()
Handler = CORSRequestHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print(f"Serving at http://localhost:{PORT}")
httpd.serve_forever()
nginx 配置
add_header Cross-Origin-Opener-Policy same-origin always;
add_header Cross-Origin-Embedder-Policy require-corp always;
cdn 阿里云 边缘脚本
add_rsp_header('Cross-Origin-Opener-Policy', 'same-origin')
add_rsp_header('Cross-Origin-Embedder-Policy', 'require-corp')
add_rsp_header('Access-Control-Allow-Origin', 'https://survivor.beyondxin.top')
cdn 七牛云
默认配置只支持有限的响应头,需要提工单。