【Qt for Harmony】 Qt 5.12.12 鸿蒙版 Windows 交叉编译

编译环境 : 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"

顺序说明:

  1. MinGW bin -- gcc, g++, mingw32-make 在最前面
  2. Perl, Python -- Qt 构建脚本需要
  3. Git usr/bin -- 提供 sh.exe, test, cp, rm, mkdir 等 Unix 工具
  4. 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' 不是内部或外部命令

或者中文注释被解析成乱码命令。

根因分析

两个问题叠加:

  1. LF vs CRLF : 生成的 .bat 文件使用了 Unix 换行符 (LF), 而 cmd.exe 严格要求 Windows 换行符 (CRLF)。LF 会导致 cmd 把 \r 字符当成命令的一部分。

  2. UTF-8 vs GBK : .bat 文件中的中文注释使用 UTF-8 编码, 但 cmd.exe 默认使用 GBK (代码页 936)。UTF-8 的中文字符被按 GBK 解码后, 可能包含 >, (, ) 等 cmd 特殊字符, 导致解析错乱。

解决方案

  1. 所有 .bat 文件生成后, 用 unix2dos 转换换行符:
bash 复制代码
unix2dos build_qt_ohos_arm64_mingw.bat
  1. .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?

  1. configure 命令已经传了 -no-warnings-are-errors 参数
  2. -Werror 是 OHOS 移植团队添加的, 适用于他们自己新写的代码确保质量
  3. 但 Qt 5.12 的老代码 + 现代 Clang 编译器 = 大量无害的 deprecated 警告, 不应阻断编译
  4. 移除 -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:

  1. qmake 构建你的 Qt 应用项目 (生成 .so)
  2. openharmonydeployqt 扫描依赖, 把应用 .so + Qt .so + 插件 + QML 文件打包成鸿蒙 HAP
  3. hdc 命令安装到设备

Q2: 为什么不在 Linux/WSL 上编译, 而要在 Windows 上折腾?

A: 两个原因:

  1. 很多开发者的主力开发环境就是 Windows, 不想装 WSL 或双系统
  2. 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 可能遇到不同的问题, 请以实际为准。

相关推荐
秋田君1 小时前
Qt_常用控件使用学习
数据库·qt·学习
木木子222 小时前
# 鸿蒙 HarmonyOS 应用开发实战(第25期)|骰子(Dice Roller)— Unicode 符号与动画渲染精讲
华为·harmonyos
qizayaoshuap2 小时前
# HarmonyOS ArkTS 记忆翻牌游戏深度解析 —— Fisher-Yates 洗牌与翻牌匹配机制
游戏·华为·harmonyos
youtootech3 小时前
HarmonyOS 6.0 自定义绘图表盘与仪表
华为·harmonyos
yaoyaoxingzhe13 小时前
HarmonyOS应用《玄象》开发实战:Canvas 绘制星宿连线:moveTo / lineTo / stroke 路径绘制
harmonyos·鸿蒙
echohelloworld1117 小时前
HarmonyOS应用开发实战:猫猫大作战-springMotion 三参数、responsiveSpringMotion 响应式弹簧、与 EaseO
harmonyos·鸿蒙
达子66620 小时前
第13章_HarmonyOs开发图解 视频
华为·音视频·harmonyos