编译环境 : Windows 11 Pro x64 / MinGW gcc 8.1.0 (host) + OHOS Clang 15 (target)
源码分支 :
tqtc/harmonyos-5.12.12(40 个 git submodules)目标平台 : HarmonyOS aarch64-linux-ohos (arm64-v8a)
NDK 版本 : DevEco Studio 6.1.1 / Native SDK 6.1.1.125 / API 24
最终产物: 210 个 aarch64 .so + 20 个 Windows 主机工具

1. 前言: 为什么需要交叉编译
我们的目的是把 Qt 应用跑在 鸿蒙设备 (HarmonyOS) 上,鸿蒙设备使用 ARM 处理器, 运行的是 aarch64-linux-ohos 内核, 用的是 musl libc 而非 glibc。 所以我们需要做 交叉编译 (cross-compilation): 在 Windows PC 上, 用鸿蒙 NDK 提供的 Clang 工具链, 编译出能在鸿蒙设备上运行的 aarch64 ELF 共享库 (.so)。
这一篇记录的就是交叉编译的完整过程, 包括遇到的 8 个坑 及其解决方案。
2. 交叉编译架构解析
2.1 什么是交叉编译
交叉编译的核心概念:
| 概念 | 说明 |
|---|---|
| Host (宿主机) | 运行编译器的机器 -- 我们的 Windows PC |
| Target (目标机) | 最终运行产物的机器 -- 鸿蒙设备 (ARM aarch64) |
| Host 编译器 | MinGW gcc 8.1.0 -- 编译 qmake, moc, rcc, uic 等主机工具 |
| Target 编译器 | OHOS Clang 15 -- 编译 libQt5Core.so 等目标库 |
编译过程中, 两套编译器同时工作:
- gcc/g++ 编译
qmake.exe,moc.exe等工具 (Windows x64 可执行文件) - OHOS clang/clang++ 编译
libQt5Core.so等库 (aarch64 ELF 共享对象)
2.2 Qt 交叉编译的 -platform 与 -xplatform
Qt 的 configure 脚本通过两个参数控制交叉编译:
configure -platform <host-mkspec> -xplatform <target-mkspec>
| 参数 | 值 | 含义 |
|---|---|---|
-platform |
win32-g++ |
主机平台 mkspec, 用 MinGW gcc 编译主机工具 |
-xplatform |
ohos-clang |
目标平台 mkspec, 用 OHOS Clang 编译目标库 |
注意 : 这里 host platform 用的是
win32-g++而不是win32-msvc。原因在后面第 4、5 个坑中详细解释。
2.3 ohos-clang mkspec 解读
ohos-clang mkspec 位于 qtbase/mkspecs/ohos-clang/qmake.conf, 它的关键配置:
qmake
# 生成 Unix 风格的 Makefile (而非 nmake 格式)
MAKEFILE_GENERATOR = UNIX
# 从环境变量读取 NDK 路径
NATIVE_OHOS_SDK = $$(NATIVE_OHOS_SDK)
# 设置交叉编译工具链
QMAKE_CC = $${NDK_LLVM_PATH}/bin/clang
QMAKE_CXX = $${NDK_LLVM_PATH}/bin/clang++
# 目标三元组: aarch64-linux-ohos
OHOS_TARGET = aarch64-linux-ohos
# 编译 flags: --sysroot + --target 指定交叉编译目标
OHOS_TOOLCHAIN_COMMON_FLAGS = \
--sysroot=$${OHOS_SDK_SYSROOT} \
--target=$${OHOS_TARGET}
这个 MAKEFILE_GENERATOR = UNIX 是一切坑的根源 -- 它让 qmake 生成 Unix 风格的 Makefile, 里面包含 test -e, mkdir -p, $(SHELL) 等 Unix shell 命令。而我们是在 Windows 上编译!
3. 环境准备
3.1 需要安装的工具
| 工具 | 版本 | 用途 | 安装路径 |
|---|---|---|---|
| MinGW gcc/g++ | 8.1.0 (Qt 5.15.2 附带) | 编译主机工具 (qmake, moc...) | D:\Qt\Qt5.15.2\Tools\mingw810_64 |
| mingw32-make | 4.2.1 (同上) | GNU Make, 执行 Unix Makefile | 同上 |
| Git for Windows | 2.x | 提供 sh.exe, test, mkdir, cp 等 Unix 工具 | D:\Program Files\Git\usr\bin |
| ActivePerl | 5.x (64-bit) | Qt 构建系统脚本依赖 | C:\Perl64\bin |
| Python | 3.12 | Qt 构建系统脚本依赖 | D:\Program Files\Python312 |
| DevEco Studio | 6.1.1 | 提供 OHOS NDK (Clang 15 + sysroot) | D:\Huawei\DevEcoStudio6.1.1 |
为什么不用 MSVC? 因为 ohos-clang mkspec 生成的是 Unix Makefile, MSVC 的 nmake/jom 无法解析。必须用 GNU Make (mingw32-make)。详见第 4 个坑。
为什么需要 Git Bash? mingw32-make 在 PATH 中发现 sh.exe 后, 会自动用 sh 来执行 Makefile 中的 shell 命令 (test, mkdir -p 等)。没有 sh.exe, 这些命令会被送到 cmd.exe 执行然后报错。
3.2 关键环境变量
bat
REM OHOS NDK 路径 (必须用正斜杠, 原因见第 6 个坑)
set "NATIVE_OHOS_SDK=D:/Huawei/DevEcoStudio6.1.1/sdk/default/openharmony/native"
REM 目标架构
set "OHOS_TARGET_ARCH=arm64-v8a"
REM 强制使用 mingw32-make (阻止 qmake 自动检测 jom)
set "MAKE=mingw32-make"
PATH 的设置至关重要, 必须精心排列:
bat
set "PATH=%MINGW_BIN%;%PERL_BIN%;%PYTHON_BIN%;%GIT_USR_BIN%;C:\Windows\System32;C:\Windows"
顺序说明:
- MinGW bin -- gcc, g++, mingw32-make 在最前面
- Perl, Python -- Qt 构建脚本需要
- Git usr/bin -- 提供 sh.exe, test, cp, rm, mkdir 等 Unix 工具
- System32 -- 基本 Windows 命令
绝对不能 让 jom.exe 出现在 PATH 中! qmake 会自动检测 jom 并使用它, 导致灾难性后果 (见第 4 个坑)。
4. 第一个坑: jom 无法执行 Unix Makefile
症状
最初的想法是用 MSVC + jom 来做交叉编译 (因为 Part 1 就是这么编的)。configure 成功了, 但 jom -j24 一开始就报错:
'test' 不是内部或外部命令,也不是可运行的程序或批处理文件。
'mkdir' 不是内部或外部命令...
根因分析
ohos-clang mkspec 设置了 MAKEFILE_GENERATOR = UNIX, 这导致 qmake 生成的 Makefile 里包含大量 Unix shell 命令:
makefile
# qmake 生成的 Makefile 片段
first: all
MKDIR = mkdir -p
INSTALL_FILE = install -m 644 -p
...
$(INSTALL_ROOT)/lib:
test -e $(INSTALL_ROOT)/lib || mkdir -p $(INSTALL_ROOT)/lib
而 jom 是 nmake 的并行版本, 它把 shell 命令交给 cmd.exe 执行。cmd.exe 不认识 test, mkdir -p, install 这些 Unix 命令, 所以全部报错。
解决方案
弃用 jom, 改用 mingw32-make (GNU Make)。
GNU Make 有一个关键特性: 如果它在 PATH 中发现了 sh.exe, 就会自动用 sh 来执行 Makefile 中的命令, 而不是 cmd.exe。Git for Windows 自带的 sh.exe 完美适配。
jom (nmake) --> cmd.exe --> 不认识 Unix 命令 --> 失败!
mingw32-make --> sh.exe --> 完美执行 Unix 命令 --> 成功!
同时, 还要设置环境变量阻止 qmake 自动检测 jom:
bat
set "MAKE=mingw32-make"
这是因为 qtbase/mkspecs/features/configure_base.prf 中有这样的逻辑:
qmake
QMAKE_MAKE = $$(MAKE) # 优先读取 MAKE 环境变量
!isEmpty(QMAKE_MAKE) {
# 使用环境变量指定的 make
} else: if(equals(MAKEFILE_GENERATOR, UNIX)|equals(MAKEFILE_GENERATOR, MINGW)) {
!equals(QMAKE_HOST.os, Windows): \
QMAKE_MAKE = make
else: \
QMAKE_MAKE = mingw32-make # Windows 默认用 mingw32-make
}
5. 第二个坑: win32-msvc + mingw32-make 格式不兼容
症状
改用 mingw32-make 后, 继续用 -platform win32-msvc 做 configure, 然后执行 mingw32-make:
Makefile:14: *** missing separator. Stop.
第一行就报错, Makefile 格式不对。
根因分析
-platform win32-msvc 让 qmake 用 MSVC 的 Makefile 生成器 来生成 host 工具 (qmake 自身) 的 bootstrap Makefile。这种 Makefile 用的是 nmake 语法, 比如:
makefile
# nmake 格式
!IF "$(OS)" == "Windows_NT"
GNU Make 不认识 !IF 这样的 nmake 指令, 所以报 "missing separator"。
解决方案
Host platform 改用 win32-g++:
bat
configure -platform win32-g++ -xplatform ohos-clang
这样 host 工具的 Makefile 也用 GNU Make 格式生成, mingw32-make 可以正确解析。host 工具 (qmake, moc, rcc, uic) 改用 MinGW gcc 编译, 不影响最终产物 (target 库仍然用 OHOS Clang)。
| 方案 | Host 编译器 | Host Makefile 格式 | Make 工具 | 结果 |
|---|---|---|---|---|
| win32-msvc + jom | MSVC cl.exe | nmake | jom | Target Makefile 是 Unix 格式, jom 无法执行 |
| win32-msvc + mingw32-make | MSVC cl.exe | nmake | mingw32-make | Host Makefile 是 nmake 格式, GNU Make 无法解析 |
| win32-g++ + mingw32-make | MinGW gcc | GNU Make | mingw32-make | 两边都用 GNU Make 格式, 完美兼容 |
6. 第三个坑: sh.exe 吃反斜杠
症状
PATH 中 NATIVE_OHOS_SDK 用了 Windows 反斜杠:
bat
set "NATIVE_OHOS_SDK=D:\Huawei\DevEcoStudio5.0.2\sdk\default\openharmony\native"
configure 阶段报错, clang 找不到 sysroot:
clang: error: no such file or directory: '--sysroot=D:HuaweiDevEcoStudio5.0.2...'
路径中的反斜杠全被吃掉了!
根因分析
前面说过, mingw32-make 会用 sh.exe 来执行 Makefile 命令。sh.exe 是 Unix shell, 反斜杠 \ 是转义字符:
D:\Huawei --> sh 认为 \H 是转义序列 --> 变成 DHuawei
解决方案
所有路径统一使用正斜杠:
bat
set "NATIVE_OHOS_SDK=D:/Huawei/DevEcoStudio6.1.1/sdk/default/openharmony/native"
正斜杠在 Windows cmd 中是合法的路径分隔符 (Windows API 同时接受 / 和 \), 在 sh.exe 中也不会被转义。两全其美。
经验: 在 Windows 交叉编译环境中, 凡是会被 sh.exe 解释的路径, 都必须用正斜杠。这包括环境变量中的路径和传递给 configure 的路径。
7. 第四个坑: .bat 文件换行符
症状
自动生成的 .bat 脚本在 cmd 中执行时报各种莫名其妙的错误:
'\r' 不是内部或外部命令
或者中文注释被解析成乱码命令。
根因分析
两个问题叠加:
-
LF vs CRLF : 生成的 .bat 文件使用了 Unix 换行符 (LF), 而 cmd.exe 严格要求 Windows 换行符 (CRLF)。LF 会导致 cmd 把
\r字符当成命令的一部分。 -
UTF-8 vs GBK : .bat 文件中的中文注释使用 UTF-8 编码, 但 cmd.exe 默认使用 GBK (代码页 936)。UTF-8 的中文字符被按 GBK 解码后, 可能包含
>,(,)等 cmd 特殊字符, 导致解析错乱。
解决方案
- 所有 .bat 文件生成后, 用
unix2dos转换换行符:
bash
unix2dos build_qt_ohos_arm64_mingw.bat
- .bat 文件中的注释全部使用纯 ASCII 英文, 不写中文:
bat
REM ---- OHOS NDK (use forward slash; sh.exe eats backslash as escape) ----
8. 第五个坑: -Werror 与 C++17 弃用警告
症状
使用 DevEco 5.0.2 的 NDK (版本 5.0.2.126) 编译时, qtbase 编译到 qrandom.cpp 报错:
error: 'is_literal_type_v' is deprecated in C++17
[-Werror,-Wdeprecated-declarations]
根因分析
ohos-clang mkspec 的 qmake.conf 中设置了 -Werror, 把所有警告当成错误:
qmake
OHOS_C_CXX_COMMON_FLAGS = \
...
-Werror
Qt 5.12.12 的代码使用了 std::is_literal_type, 这个 trait 在 C++17 中被标记为 deprecated。OHOS Clang 15 默认使用 C++17 标准, 检测到 deprecated 用法后产生 -Wdeprecated-declarations 警告, 再被 -Werror 提升为编译错误。
解决方案 (初始)
在 -Werror 后面添加 -Wno-error=deprecated-declarations, 只针对这个特定警告降级:
qmake
OHOS_C_CXX_COMMON_FLAGS = \
...
-Werror \
-Wno-error=deprecated-declarations
后续 : 这个方案在后面第 10 个坑中被证明不够彻底, 最终改为直接移除
-Werror。
9. 第六个坑: NDK 版本过低, API 不存在
症状
修复上一个问题后继续编译, 编译出了 34 个 aarch64 .so 文件 (qtbase, qtdeclarative 等核心模块), 但在编译 qtbase/src/plugins/platforms/ohos/ 时报错:
fatal error: 'arkui/native_interface_accessibility.h' file not found
更深入的模块还报:
error: use of undeclared identifier 'ArkUI_AccessibilityProviderCallbacksWithInstance'
根因分析
Qt 鸿蒙版源码 (tqtc/harmonyos-5.12.12 分支) 使用了 ArkUI 无障碍 API:
cpp
// Qt 源码中使用的 API
ArkUI_AccessibilityProviderCallbacksWithInstance callbacks;
这个 WithInstance 后缀的结构体是 NDK 5.1+ (API 14+) 才引入的新 API。而 DevEco Studio 5.0.2 自带的 NDK 版本是 5.0.2.126, 只有旧版 API ArkUI_AccessibilityProviderCallbacks (不含 WithInstance)。
版本对照:
| DevEco Studio 版本 | NDK 版本 | API Level | 是否有 WithInstance API |
|---|---|---|---|
| 5.0.2 | 5.0.2.126 | 12 | 没有 |
| 6.1.1 | 6.1.1.125 | 24 | 有 |
解决方案
升级 DevEco Studio 到 6.1.1, 获取更新的 OHOS NDK。
验证新 NDK 中 API 存在:
bash
$ grep -r "ArkUI_AccessibilityProviderCallbacksWithInstance" \
"D:/Huawei/DevEcoStudio6.1.1/sdk/default/openharmony/native/" \
--include="*.h" -l
# 输出:
D:/Huawei/DevEcoStudio6.1.1/sdk/default/openharmony/native/sysroot/usr/include/arkui/native_interface_accessibility.h
确认 API 存在, 更新编译脚本中的 NDK 路径:
bat
REM 旧路径 (DevEco 5.0.2)
set "NATIVE_OHOS_SDK=D:/Huawei/DevEcoStudio5.0.2/sdk/default/openharmony/native"
REM 新路径 (DevEco 6.1.1)
set "NATIVE_OHOS_SDK=D:/Huawei/DevEcoStudio6.1.1/sdk/default/openharmony/native"
新 NDK 的版本信息 (oh-uni-package.json):
json
{
"apiVersion": "24",
"displayName": "Native",
"releaseType": "Release",
"version": "6.1.1.125"
}
10. 第七个坑: 新 NDK 的 Clang 更严格
症状
升级到 NDK 6.1.1.125 后, 重新 configure + build, 编译到 qtremoteobjects 模块时报错:
qtremoteobjects/src/remoteobjects/qtremoteobjectglobal.h:56:38:
error: definition of implicit copy constructor for
'QRemoteObjectSourceLocationInfo' is deprecated because it has a
user-provided copy assignment operator
[-Werror,-Wdeprecated-copy-with-user-provided-copy]
根因分析
新 NDK 中的 Clang 虽然同为 15.0.4, 但其默认的诊断行为更严格。它新增了 -Wdeprecated-copy-with-user-provided-copy 这个警告。
之前的修复只添加了 -Wno-error=deprecated-declarations, 但这个新警告属于另一类 (-Wdeprecated-copy), 没被覆盖。
面对这种 "打地鼠" 式的问题, 继续逐个添加 -Wno-error=xxx 是不可持续的 -- Qt 5.12 的代码太老, 现代 Clang 能找出无数个 deprecated 用法。
解决方案
彻底移除 -Werror。
qmake.conf 修改前:
qmake
OHOS_C_CXX_COMMON_FLAGS = \
$$OHOS_TOOLCHAIN_COMMON_FLAGS \
-fdata-sections \
-ffunction-sections \
-funwind-tables \
-fstack-protector-strong \
-no-canonical-prefixes \
-fno-addrsig \
-Wformat \
-Werror \
-Wno-error=deprecated-declarations
修改后:
qmake
OHOS_C_CXX_COMMON_FLAGS = \
$$OHOS_TOOLCHAIN_COMMON_FLAGS \
-fdata-sections \
-ffunction-sections \
-funwind-tables \
-fstack-protector-strong \
-no-canonical-prefixes \
-fno-addrsig \
-Wformat
为什么可以安全地移除
-Werror?
- configure 命令已经传了
-no-warnings-are-errors参数-Werror是 OHOS 移植团队添加的, 适用于他们自己新写的代码确保质量- 但 Qt 5.12 的老代码 + 现代 Clang 编译器 = 大量无害的 deprecated 警告, 不应阻断编译
- 移除
-Werror不影响真正的编译错误 (语法错误、类型错误等仍会报错)
11. 第八个坑: std::auto_ptr 在 C++17 中被移除
症状
移除 -Werror 后重新编译, 大部分模块编译成功, 但 qtscript 模块报错:
qtscript/src/3rdparty/javascriptcore/JavaScriptCore/wtf/OwnPtr.h:43:21:
error: no template named 'auto_ptr' in namespace 'std'
根因分析
qtscript 模块内嵌了一个古老的 JavaScriptCore 引擎, 其代码使用了 std::auto_ptr:
cpp
// OwnPtr.h
template<typename T>
class OwnPtr : public std::auto_ptr<T> { // <-- C++17 中已删除!
std::auto_ptr 在 C++11 中被标记为 deprecated, 在 C++17 中被彻底删除。OHOS Clang 15 默认用 C++17 编译, 所以直接报 "no template named 'auto_ptr'" -- 这不是警告, 是真正的编译错误, 无法通过 flag 绕过。
解决方案
跳过 qtscript 模块 : 在 configure 命令中添加 -skip qtscript。
理由:
qtscript自 Qt 5.5 起就被标记为 deprecated (弃用)- 新的 Qt Quick/QML 应用应使用
qtdeclarative(QML JavaScript 引擎), 而非qtscript - 修复 JavaScriptCore 的
auto_ptr用法工作量大且无实际价值 - 跳过它不影响其他任何模块
最终 configure 命令的 skip 列表:
bat
-skip qtmacextras REM macOS 专用
-skip qtx11extras REM X11 专用
-skip qtwayland REM Wayland 专用
-skip qtandroidextras REM Android 专用
-skip qtwinextras REM Windows 专用 (目标是鸿蒙)
-skip qtactiveqt REM ActiveX/COM, Windows 专用
-skip qtwebengine REM Chromium, 太重, 单独处理
-skip qtdoc REM 文档生成, 非必需
-skip qtspeech REM 语音, 非核心
-skip qtscript REM deprecated, C++17 不兼容
12. 最终编译脚本
经过 8 个坑的洗礼, 最终的编译脚本 build_qt_ohos_arm64_mingw.bat:
bat
@echo off
REM ============================================================================
REM Qt 5.12.12 (HarmonyOS port) - CROSS-COMPILE to HarmonyOS arm64-v8a
REM Strategy: ALL MinGW (no MSVC, no jom)
REM Host : Windows MinGW gcc 8.1.0 (win32-g++)
REM Target: HarmonyOS Clang 15 (ohos-clang, aarch64-linux-ohos)
REM Make : mingw32-make (GNU Make 4.2.1) + Git Bash sh.exe
REM ============================================================================
setlocal EnableDelayedExpansion
REM ---- Paths ----
set "SRC_ROOT=f:\QtForHarmony\qt-harmonyos-src-5.12.12-20260330"
set "BUILD_DIR=f:\QtForHarmony\qt-build-ohos-arm64"
set "INSTALL_DIR=f:\QtForHarmony\qt-install-ohos-arm64"
set "LOG_DIR=f:\QtForHarmony\build-logs"
REM ---- Toolchain ----
set "MINGW_MAKE=D:\Qt\Qt5.15.2\Tools\mingw810_64\bin\mingw32-make.exe"
set "MINGW_BIN=D:\Qt\Qt5.15.2\Tools\mingw810_64\bin"
set "GIT_USR_BIN=D:\Program Files\Git\usr\bin"
set "PERL_BIN=C:\Perl64\bin"
set "PYTHON_BIN=D:\Program Files\Python312"
REM ---- OHOS NDK (forward slash to avoid sh.exe escape issue) ----
set "NATIVE_OHOS_SDK=D:/Huawei/DevEcoStudio6.1.1/sdk/default/openharmony/native"
set "OHOS_TARGET_ARCH=arm64-v8a"
REM ---- Make tool ----
set "MAKE=mingw32-make"
if not exist "%LOG_DIR%" mkdir "%LOG_DIR%"
REM ---- Clean minimal PATH ----
set "PATH=%MINGW_BIN%;%PERL_BIN%;%PYTHON_BIN%;%GIT_USR_BIN%;C:\Windows\System32;C:\Windows"
echo [INFO] Verifying tools ...
where gcc.exe 1>nul 2>nul || (echo [ERROR] gcc.exe not found & exit /b 1)
where g++.exe 1>nul 2>nul || (echo [ERROR] g++.exe not found & exit /b 1)
where perl.exe 1>nul 2>nul || (echo [ERROR] perl.exe not found & exit /b 1)
where python.exe 1>nul 2>nul || (echo [ERROR] python.exe not found & exit /b 1)
where mingw32-make.exe 1>nul 2>nul || (echo [ERROR] mingw32-make.exe not found & exit /b 1)
where sh.exe 1>nul 2>nul || (echo [ERROR] sh.exe not found & exit /b 1)
if not exist "%NATIVE_OHOS_SDK%" (echo [ERROR] NATIVE_OHOS_SDK missing & exit /b 1)
if not exist "%NATIVE_OHOS_SDK%\llvm\bin\clang.exe" (echo [ERROR] OHOS clang missing & exit /b 1)
REM ---- Verify jom is NOT in PATH ----
where jom.exe 1>nul 2>nul && (echo [ERROR] jom.exe found in PATH; remove it & exit /b 1)
if not exist "%BUILD_DIR%" mkdir "%BUILD_DIR%"
cd /d "%BUILD_DIR%" || (echo [ERROR] cd to build dir failed & exit /b 1)
if "%1"=="configure" goto :do_configure
if "%1"=="build" goto :do_build
if "%1"=="install" goto :do_install
if "%1"=="all" goto :do_all
if "%1"=="" goto :do_all
:do_all
call :do_configure || exit /b 1
call :do_build || exit /b 1
call :do_install || exit /b 1
goto :eof
:do_configure
echo ===== STEP 1/3 : cross-compile configure =====
call "%SRC_ROOT%\configure.bat" ^
-prefix "%INSTALL_DIR%" ^
-release -opensource -confirm-license ^
-platform win32-g++ -xplatform ohos-clang ^
-opengl es2 ^
-nomake examples -nomake tests ^
-no-warnings-are-errors -shared ^
-skip qtmacextras -skip qtx11extras -skip qtwayland ^
-skip qtandroidextras -skip qtwinextras -skip qtactiveqt ^
-skip qtwebengine -skip qtdoc -skip qtspeech -skip qtscript ^
> "%LOG_DIR%\configure-ohos.log" 2>&1
if errorlevel 1 (echo [ERROR] configure failed & exit /b 1)
echo [OK] configure done.
goto :eof
:do_build
echo ===== STEP 2/3 : cross-compile build (mingw32-make -j8) =====
"%MINGW_MAKE%" -j8 > "%LOG_DIR%\build-ohos.log" 2>&1
if errorlevel 1 (echo [ERROR] build failed & exit /b 1)
echo [OK] build done.
goto :eof
:do_install
echo ===== STEP 3/3 : install OHOS Qt =====
"%MINGW_MAKE%" install > "%LOG_DIR%\install-ohos.log" 2>&1
if errorlevel 1 (echo [ERROR] install failed & exit /b 1)
echo [OK] install done at %INSTALL_DIR%
goto :eof
源码修改清单
编译前需要对源码做两处修改:
1. qtbase/mkspecs/ohos-clang/qmake.conf -- 移除 -Werror
diff
OHOS_C_CXX_COMMON_FLAGS = \
$$OHOS_TOOLCHAIN_COMMON_FLAGS \
...
- -Wformat \
- -Werror \
- -Wno-error=deprecated-declarations
+ -Wformat
2. qtbase/src/3rdparty/zlib.pri -- 限制 Clang-only 编译 flag
原始代码无条件添加了 -Wno-deprecated-non-prototype, 这是 Clang-only 的 flag, MSVC 不认识:
diff
-QMAKE_CXXFLAGS += -Wno-deprecated-non-prototype
-QMAKE_CFLAGS += -Wno-deprecated-non-prototype
+clang|gcc {
+ QMAKE_CXXFLAGS += -Wno-deprecated-non-prototype
+ QMAKE_CFLAGS += -Wno-deprecated-non-prototype
+}
注意: zlib.pri 的修改在 Part 1 (Windows MSVC 编译) 中就已经做了, 交叉编译延续使用。
13. 编译结果验证
13.1 安装目录结构
f:\QtForHarmony\qt-install-ohos-arm64\ (213 MB)
|-- bin\ 20 个 Windows .exe (主机工具)
|-- doc\ 文档
|-- include\ C++ 头文件
|-- lib\ 58 个 aarch64 .so (核心库)
|-- mkspecs\ qmake 配置
|-- plugins\ 80 个 aarch64 .so (插件)
|-- qml\ 60 个 aarch64 .so + QML 文件
`-- translations\ 翻译文件
13.2 ELF 格式验证
bash
$ file lib/libQt5Core.so
libQt5Core.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV),
dynamically linked, for GNU/Linux 3.17.0,
BuildID[sha1]=e1bd0e36f49798d500ca6d5f64bee66891f744ee, stripped
确认是 aarch64 (ARM 64-bit) 架构的 ELF 共享库, 而非 x86_64。
13.3 主机工具清单
| 工具 | 功能 |
|---|---|
qmake.exe |
Qt 项目构建系统 |
moc.exe |
Meta-Object Compiler (信号/槽) |
rcc.exe |
Resource Compiler (qrc 资源) |
uic.exe |
UI Compiler (.ui 文件) |
lrelease.exe / lupdate.exe / lconvert.exe |
翻译工具链 |
qmlcachegen.exe / qmlimportscanner.exe |
QML 工具 |
openharmonydeployqt.exe |
鸿蒙专用部署工具 |
repc.exe |
Qt Remote Objects 编译器 |
qscxmlc.exe |
SCXML 状态机编译器 |
注意
openharmonydeployqt.exe-- 这是鸿蒙版 Qt 特有的部署工具, 类似 Android 的androiddeployqt, 用于打包 Qt 应用到鸿蒙 HAP。
13.4 核心 .so 库 (前 20 大)
| 库名 | 大小 | 说明 |
|---|---|---|
| libQt5Widgets.so | 6.4 MB | Widget 框架 |
| libQt5Core.so | 5.0 MB | 核心库 |
| libQt5Qml.so | 4.5 MB | QML 引擎 |
| libQt5Quick.so | 4.5 MB | Qt Quick 框架 |
| libQt5Gui.so | 4.4 MB | GUI 框架 |
| libQt5XmlPatterns.so | 4.0 MB | XPath/XQuery |
| libQt53DRender.so | 2.5 MB | 3D 渲染 |
| libQt5Charts.so | 1.8 MB | 图表 |
| libQt5Location.so | 1.7 MB | 定位/地图 |
| libQt5QuickTemplates2.so | 1.4 MB | Quick Controls 2 模板 |
| libQt5EglFSDeviceIntegration.so | 1.3 MB | EGLFS 设备集成 |
| libQt5DataVisualization.so | 1.3 MB | 数据可视化 |
| libQt5Network.so | 1.2 MB | 网络 |
| libQt5Multimedia.so | 869 KB | 多媒体 |
| libQt5VirtualKeyboard.so | 819 KB | 虚拟键盘 |
| libQt5Bluetooth.so | 746 KB | 蓝牙 |
| libQt53DExtras.so | 701 KB | 3D 附加组件 |
| libQt5RemoteObjects.so | 619 KB | 远程对象 |
| libQt5OhosExtras.so | 223 KB | 鸿蒙专用扩展 |
| libQt5Purchasing.so | 40 KB | 应用内购买 |
libQt5OhosExtras.so 是鸿蒙版特有的模块, 提供鸿蒙平台集成功能。
13.5 插件目录结构
plugins/
|-- audio/ 音频输出插件
|-- bearer/ 网络承载管理
|-- canbus/ CAN 总线
|-- egldeviceintegrations/ EGLFS 设备集成
|-- gamepads/ 游戏手柄
|-- generic/ 通用输入
|-- geometryloaders/ 3D 几何加载器
|-- geoservices/ 地图服务 (OHOS Map Kit, OSM, HERE, Esri, Mapbox)
|-- iconengines/ 图标引擎 (SVG)
|-- imageformats/ 图片格式 (JPEG, GIF, ICO, SVG, TIFF, ...)
|-- mediaservice/ 多媒体服务
|-- platforminputcontexts/ 输入法上下文
|-- platforms/ **平台插件 (OHOS EGLFS)**
|-- platformthemes/ 平台主题
|-- playlistformats/ 播放列表
|-- position/ 定位
|-- printsupport/ 打印
|-- qmltooling/ QML 调试工具
|-- renderplugins/ 3D 渲染插件
|-- sceneparsers/ 3D 场景解析
|-- sensorgestures/ 传感器手势
|-- sensors/ 传感器
|-- sqldrivers/ SQL 驱动
|-- styles/ 控件样式
|-- virtualkeyboard/ 虚拟键盘
`-- webview/ WebView
13.6 汇总统计
| 类别 | 数量 |
|---|---|
核心 .so 库 (lib/) |
58 |
插件 .so (plugins/) |
80 |
QML .so (qml/) |
60 |
静态库 .a (lib/) |
若干 (bootstrap, pcre2, png 等) |
| 目标 .so 总计 | 210 (全部 aarch64 ELF) |
主机工具 (bin/) |
20 (.exe, Windows x64) |
| 安装总大小 | 213 MB |
14. 经验总结
做对的几件事
| 决策 | 收益 |
|---|---|
| 弃用 jom, 全面转 mingw32-make | 从根本上解决了 Unix Makefile 兼容性问题 |
| Host 改用 win32-g++ | 统一 Makefile 格式, 两边都用 GNU Make |
| 路径全用正斜杠 | 避免 sh.exe 转义, 一劳永逸 |
| 精心控制 PATH | 只放必需工具, 确保 jom 不被发现 |
设 MAKE=mingw32-make 环境变量 |
从 qmake 内部阻止 jom 检测 |
直接移除 -Werror 而非逐个豁免 |
避免 "打地鼠", 一次性解决所有 deprecated 警告 |
| 跳过 qtscript | deprecated 模块, C++17 根本无法编译, 跳过零成本 |
| 升级 DevEco Studio | 获取新 NDK, 解决 API 缺失, 无需 patch 源码 |
避坑清单
| 坑 | 后果 | 解决方案 |
|---|---|---|
| jom + Unix Makefile | test, mkdir 等命令全部报 "不是命令" |
用 mingw32-make + sh.exe |
| win32-msvc + GNU Make | missing separator 第一行就挂 |
Host 用 win32-g++ |
| 路径反斜杠 + sh.exe | D:\Huawei 变成 DHuawei |
路径全用正斜杠 |
| .bat 用 LF 换行 | cmd 解析错乱 | unix2dos 转 CRLF |
-Werror + 老代码 + 新编译器 |
deprecated 警告变错误, 编不过 | 移除 -Werror |
| NDK 版本过低 | ArkUI API 不存在 | 升级 DevEco Studio |
std::auto_ptr + C++17 |
模板不存在, 编译错误 | -skip qtscript |
| 中文注释 + cmd GBK | 乱码变成非法命令 | 全用 ASCII 注释 |
15. FAQ
Q1: 编译出来的 .so 怎么部署到鸿蒙设备上?
A: 使用 bin/openharmonydeployqt.exe 工具。它的工作原理类似 Android 的 androiddeployqt:
- 用
qmake构建你的 Qt 应用项目 (生成 .so) openharmonydeployqt扫描依赖, 把应用 .so + Qt .so + 插件 + QML 文件打包成鸿蒙 HAP- 用
hdc命令安装到设备
Q2: 为什么不在 Linux/WSL 上编译, 而要在 Windows 上折腾?
A: 两个原因:
- 很多开发者的主力开发环境就是 Windows, 不想装 WSL 或双系统
- DevEco Studio 原生运行在 Windows 上, NDK 路径、调试流程更顺畅
当然, 如果你有 Linux 环境, 交叉编译会简单得多 -- 因为 MAKEFILE_GENERATOR = UNIX 在 Linux 上就是 native 的, 不需要 sh.exe hack。
Q3: 能否用 MSVC 做 host 编译器?
A: 理论上可以, 但需要修改 qmake 的 Makefile 生成逻辑, 让它在交叉编译时对 host 和 target 使用不同的 Makefile 格式。这个改动很复杂。用 MinGW gcc 做 host 是最简单的路径。
Q4: 编译出的 .so 支持哪些鸿蒙设备?
A: 编译目标是 aarch64-linux-ohos (arm64-v8a), 支持所有运行 64 位 ARM 处理器的鸿蒙设备, 包括:
- 华为手机 (Mate/P 系列)
- 华为平板 (MatePad 系列)
- 其他 ARM64 鸿蒙设备
如果需要 32 位 ARM (armeabi-v7a) 或 x86_64, 修改 OHOS_TARGET_ARCH 环境变量并重新编译即可。
Q5: 为什么跳过了 qtscript 但没跳 qtscxml?
A: qtscxml (SCXML 状态机) 和 qtscript (JavaScript 脚本引擎) 是完全不同的模块:
qtscript内嵌了古老的 JavaScriptCore, 用了std::auto_ptr, C++17 下无法编译qtscxml没有这个问题, 编译正常
Q6: 编译日志在哪里?
A: 所有日志在 f:\QtForHarmony\build-logs\ 目录下:
| 文件 | 内容 |
|---|---|
configure-ohos.log |
configure 输出 (功能检测结果) |
build-ohos.log |
编译输出 (15000+ 行) |
install-ohos.log |
安装输出 |
结语
在 Windows 上交叉编译 Qt 给鸿蒙, 最大的挑战不是 OHOS Clang 工具链本身, 而是 Windows 环境与 Unix Makefile 的不兼容 。MAKEFILE_GENERATOR = UNIX 这个设定, 决定了整个编译流水线必须在 Unix-like 的 shell 环境中运行, 而 Windows 天然不提供这种环境。
最终的解决方案是一条 "曲线救国" 路线: MinGW gcc (host) + mingw32-make (GNU Make) + Git Bash sh.exe (shell) + OHOS Clang (target)。四个组件各司其职, 组成了一条在 Windows 上运行但兼容 Unix Makefile 的交叉编译流水线。
8 个坑, 从 Makefile 格式到路径分隔符到换行符到编译器 flag 到 SDK 版本, 每一个都是 "Windows 上做 Unix 的事" 这个根本矛盾的具体体现。但一旦踩完这些坑, 编译过程本身是稳定可复现的。
产出: 210 个 aarch64 ELF 共享库, 涵盖 Qt 5.12.12 的绝大部分模块 (除了 deprecated 的 qtscript 和平台无关的几个模块), 可以用于在鸿蒙设备上运行 Qt 应用。
相关文件:
- 编译脚本:
build_qt_ohos_arm64_mingw.bat - 源码修改:
qtbase/mkspecs/ohos-clang/qmake.conf,qtbase/src/3rdparty/zlib.pri - 编译日志:
build-logs/configure-ohos.log,build-logs/build-ohos.log - 安装目录:
qt-install-ohos-arm64/ - Part 1 (Windows 原生编译):
Qt-5.12.12-鸿蒙版Windows-MSVC编译实录.md
作者备注 : 本文编译环境为 Windows 11 + DevEco Studio 6.1.1 + Qt 5.12.12 鸿蒙版 (tqtc/harmonyos-5.12.12 分支)。不同版本的 DevEco Studio / NDK 可能遇到不同的问题, 请以实际为准。