A
Air官方
【Air 系列 · 鸿蒙 PC 适配全记录】25 / 56
■ 应用:轻矩阵(from GNU Octave)(AirOctave)
■ 定位:数值计算与编程环境(GNU Octave,MATLAB 同类工具)
■ 上游:GNU Octave | 路线:Qt6 6.11.1+QPA | 版本:1.0.0
■ 市场状态:开发中,即将上架
■ 官网(免费下载): https://www.shunidc.cn/
————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/
PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:(AtomGit 仓库建立后回填)
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
▍一、为什么要适配 轻矩阵(GNU Octave)
GNU Octave 是开源数值计算环境,语言与 MATLAB 高度兼容,自带基于 Qt Widgets 的交互式图形界面,覆盖命令行、脚本编辑、工作区变量与绘图。矩阵运算、数值求解与算法原型验证是高校与工程场景的长期需求,把 Octave 带到鸿蒙 PC,意味着 2in1 设备可以获得一套不依赖云端的本地科学计算环境。
本工程(产品名轻矩阵,仓库 AirOctave)于 2026-09-25 立项,上游为 GNU Octave 11.3.0。包名 com.airoctave.app,版本 1.0.0,deviceTypes 覆盖 phone、tablet 与 2in1,目标设备为 MatePad 与 MateBook Pro,SDK 为 6.1.0(23),NDK 采用 AirChat-SDK24(LLVM15/musl),目标 ABI 为 arm64-v8a。与纯 ArkTS 应用不同,这是典型的 NDK 交叉编译移植工程:上层的 liboctave、libinterp 与 Qt GUI 全部要从源码构建。
需要说明的是,截至工程 PORTING.md 与 ai_log 记录的时间点,项目处于 M1 依赖链与 M2 CLI 内核编译的推进阶段,GUI 首窗属于 M3 里程碑。本文如实按当前阶段撰写,已完成的依赖与已验证的转译路线均有 ai_log 记录支撑,未达成的部分明确列为后续目标。
▍二、先划清边界:不把编译通过当成适配完成
桌面版 Octave 依赖发行版的包管理与完整的 Unix 进程模型:gfortran 编译数学外围层、独立 octave-gui 进程、桌面 OpenGL、texinfo 文档链。鸿蒙沙箱对这几条都有实质约束,工程在 PORTING.md 的路线定案与风险登记册中把边界逐条写死,避免用"configure 通过"冒充移植完成。
层次 传统桌面 Octave 方案 鸿蒙侧当前方案
发行形态 桌面安装包,octave-gui 独立进程 HAP 双层架构,GUI 库化进宿主线程(沙箱无 execve)
GUI 框架 系统 Qt + X11/Wayland AirGis Qt6.11.1 预编译栈 + 自研 ohos QPA(AirCast 血统)
绘图 GL 桌面 OpenGL 2.1 系统 libGLv4 直连优先,gl4es 备胎,最终可关绘图兜底
Fortran 数学层 gfortran 编译 .f 源 f2c 全树转译为 C + libf2c 运行时 + OpenBLAS C_LAPACK
依赖来源 发行版包管理器 native/install 前缀自建:PCRE2、ncurses、readline、GMP、MPFR、Qhull、GLPK、OpenBLAS、FFTW、SuiteSparse
数值扩展 ARPACK/qrupdate 提供 eigs 等 NDK 无 Fortran,首版 --without-arpack、--without-qrupdate 降级
帮助文档 texinfo 生成 qch 首版帮助浏览器空壳,M5 后宿主工具链补生成
这条边界的核心判断是:Octave 主体是 C++,真正卡脖子的是 liboctave/external 的 Fortran 数学外围层,把它换成 f2c 转译物,其余部分才有讨论价值。
▍三、鸿蒙版的整体架构
工程采用 Air 系双层架构。ArkTS 薄壳只承载 XComponent 与系统能力接线;NAPI 桥 libairoctavebridge.so 零预编译链接,运行时按拓扑序 dlopen 全部依赖;宿主核心 libairoctave_core.so 在独立线程上完成环境变量前置、日志与信号处理,再调起 GUI 库化的 Octave 入口;Qt 绘图经由自研 QPA 接到系统 NativeWindow。
text
EntryAbility / Index.ets(ArkTS 薄壳)
├── XComponent(SURFACE)承载 Qt 画面
├── 菜单栏:bindMenu 弹系统 Menu,QMenuBar 快照轮询刷新
├── IME:离屏 1×1 TextInput 通道
└── 选择器:QPA pick 请求 → TSFN → DocumentViewPicker 回流
└── libairoctavebridge.so(napi_init.cpp,NAPI 薄桥,零预编译链接)
└── dlopen 拓扑序全栈(RTLD_GLOBAL)
├── Qt6.11 栈(Core/Gui/Widgets/Help/OpenGL/...)
├── libqohos.so(ohos QPA)
├── liboctave.so(liboctave/libinterp)
└── libairoctave_host.so(airoctave_octave_main)
└── octave::qt_application(GUI 库化,宿主线程)
主要目录如下:
text
AirOctave/
├── PORTING.md # 路线定案、依赖矩阵、补丁清单、风险登记册
├── ai_log/ # 立项、M1 依赖链、M2 f2c 转译三轮记录
├── AppScope/app.json5 # 包名 com.airoctave.app
├── entry/src/main/
│ ├── cpp/
│ │ ├── napi_init.cpp # NAPI 薄桥:dlsym core API、输入注入、TSFN
│ │ ├── airoctave_core.cpp # 宿主核心:环境前置、dlopen 拓扑、菜单快照
│ │ ├── octave_host.cpp # GUI 库化入口(复刻 src/main-gui.cc)
│ │ ├── qt_platform/ohos_qt6/ # 自研 ohos QPA(inputcontext/filedialoghelper)
│ │ ├── patch_libs.py # NEEDED 去版本号等打包链
│ │ └── fix_elf_align.py # ELF 对齐修正
│ └── ets/ # Index、FileManager、SubWindowManager、DialogHost
└── native/
├── cross/ # aarch64 toolchain/cross 文件、fake F77、musl 垫片
├── sources/ # PCRE2、ncurses、readline、GMP、MPFR、Qhull、GLPK、
│ # OpenBLAS、FFTW、SuiteSparse、HDF5 等源码档
├── patches/ # openblas-ohos-affinity.diff
├── scripts/ # env.sh、build_deps.sh、build_octave.sh、f2c_convert.sh
└── install/ # 本工程自有产物前缀(含 octave 子前缀)
▍四、核心适配过程
### 1. 零链接 NAPI 桥与拓扑序 dlopen
entry/src/main/cpp/napi_init.cpp 只链接 ace_napi、hilog、native_window 等系统库,绝不链接任何预编译 .so。它在启动时 dlopen libairoctave_core.so 并以 dlsym 取出 start、菜单快照、输入注入等接口;XComponent 的触摸与按键事件经 QOhos_Inject 系列符号转投 Qt QPA;系统文件选择器通过 TSFN 回调到 ArkTS 的 DocumentViewPicker,结果再回流 QPA。预编译依赖的加载顺序全部由 airoctave_core.cpp 中的静态数组按拓扑序管理:Qt6 各模块先于 libqohos(QPA),再是 liboctave 与 libairoctave_host,顺序错误即加载失败,因此清单化是唯一可靠做法。
### 2. 宿主核心:环境前置与 Qt 主线程
airoctave_core.cpp 在宿主线程上完成四件事:把 HOME 与 XDG 系列环境变量前置到沙箱可写路径,使 QSettings 与 Qt 缓存不落非法位置;按拓扑序 dlopen 全部依赖(RTLD_GLOBAL);在该线程上调起 airoctave_octave_main,保证 QApplication 的构造线程即 Qt 主线程;注册 SIGSEGV 处理与日志镜像。此外预留了绑定 127.0.0.1 的 8098 端口 HTTP 通道,作为自动化回归的触发入口,局域网不可达,仅可经 hdc fport 转发使用。
### 3. f2c 全树转译:把 Fortran 数学外围层变成 C
这是 M2 的核心突破。Octave 并非全 C++:liboctave/external 下有 247 个 .f 源(amos、dassl、odepack、quadpack、lapack-xtra 等),liboctave/util 另有 3 个。native/scripts/f2c_convert.sh 用宿主自建的 netlib f2c(20240504)把全部 .f 转为 ANSI C,同步改写 module.mk 与顶层 Makefile.in 的源列表,为 mk-f77-def.in.sh 增加 .c 分支,再以宿主 autoconf 2.73 与 automake 1.18.1 执行 autoreconf -fi 全量重生成。libf2c 运行时交叉编译踩了四个坑并全部记录:makefile 硬编码 ld -r 换成 ld.lld;arith.h 由 arithchk 探测程序生成而交叉环境不可运行,改在宿主跑(LP64 与目标一致);uninit.c 依赖 glibc 头文件被剔除;打包环节宿主 ar 打 ELF 目标会静默产出空档,必须换 llvm-ar。转译物的 COMMON 块需要全局 -fcommon,configure 注入 LIBS="-lz -lf2c" 提供运行时符号。
### 4. 依赖链交叉编译与共享栈复用
native/scripts/build_deps.sh 按依赖序构建数学与文本栈,全部产物为静态库进 native/install,避免扩大 entry/libs 部署面,stamp 文件支持增量跳过。各库的要点:PCRE2 10.45 静态构建并 --disable-jit 规避 RWX 内存段;ncurses 6.5 只出 widec 变体并以 libncurses.a 软名满足 gnulib 探测;OpenBLAS 0.3.29 以 C_LAPACK=1 NOFORTRAN=1 提供 f2c 修饰的 BLAS 与 LAPACK,与 Octave 的 f2c 调用约定精确对齐;SuiteSparse 7.10.3 取 amd、klu、umfpack、cholmod、cxsparse 等子集;FFTW 3.3.10 出 double 与 float 一对。Qt 栈与 QScintilla 直接复用 AirGis 预编译前缀,缺失的 QtHelp 由 native/scripts/build_qthelp.sh 从 qttools 6.11.1 源码只建 Help 目标后增量装配,不动共享栈既有库。
### 5. GUI 库化与 QPA 系统接线
上游 octave-gui 是独立进程,而沙箱禁 execve。native/scripts/build_octave.sh 在 M3 将走完整 Qt configure,entry/src/main/cpp/octave_host.cpp 复刻上游 src/main-gui.cc 的 182 行薄壳,导出 airoctave_octave_main 供宿主线程调起 octave::qt_application,把整个 GUI 当库驱动。ArkTS 侧 Index.ets 承担 QPA 覆盖不到的系统能力:顶栏菜单以 bindMenu 弹系统 Menu 组件,经 QMenuBar 快照轮询刷新与触发回注;IME 用离屏 1×1 TextInput 通道;QPA 光标协议码映射为系统 PointerStyle 枚举;差分回注按码点而非 UTF-16 单元计数,保证 emoji 退格正确。
▍五、适配中遇到的几个难点
### 难点一:NDK 没有 Fortran 编译器,而 configure 硬性要求一个
Octave 的 configure 检查 F77,NDK 工具链不存在 Fortran 前端,ARPACK 与 qrupdate 也因此首版禁用。工程的两级对策:先用 native/cross/make_fake_f77.sh 生成恒返回 0 的假 F77 让 configure 过检,同时以 cache 预置目标修饰与字长(mangling、sizeof=4);真正的 Fortran 代码则由 f2c 全树转译承担,并配 --enable-fortran-calling-convention=f2c,使 Octave 的调用约定与 OpenBLAS C_LAPACK 的 f2c 修饰完全一致。假编译器只能骗过探测,不能产出目标码,这两级缺一不可。
### 难点二:半替换的构建系统比全手动更危险
f2c 转译后依赖文件已变成 .c,但 automake 预生成的规则仍携带 --tag=F77 指向假编译器,make 在中途以 fake-f77 处理 .c 目标,报错形态极具迷惑性。工程的做法是不做半自动修补,直接 autoreconf -fi 全量重生成构建系统,让规则与源文件语言重新一致,并在 f2c_convert.sh 中固化为幂等步骤。
### 难点三:可见性标志悄悄吞掉转译符号
Octave 默认以 -fvisibility=hidden 编译,f2c 产物没有可见性属性标注,全部符号被隐藏,表现为 liboctave.so 符号变 local、liboctinterp 解析失败。这个失败不指向根因,容易在链接脚本上浪费时间。对策是 configure 加 --disable-lib-visibility-flags 放弃导出控制,工程在 PORTING.md 补丁清单中把它列为 P1 的组成部分。
### 难点四:musl 与沙箱差异族
风险登记册 R4 记录了这一族:musl 无 pthread_cancel(垫片已备)、gnulib 交叉探测特殊、ELF 无版本化 NEEDED(patch_libs.py 处理)、fork-exec 受限(system() 与 pkg 命令降级)。其中影响最大的是解释器初始化重——扫 load-path 与历史记录会拖住 ArkTS 主线程触发 appfreeze,工程因此把解释器全量放进 core 后台线程,配启动遮罩与 TSFN 回调;R5 则针对约 2000 个 .m 文件加 share 数据的部署,采用首启版本号门控一次性解包加分块流拷贝。
### 难点五:GL 栈的三级降级预案
Octave 绘图工具包用 QOpenGLWidget 依赖桌面 GL 2.1 语义,而 QPA 的 GL 上下文路径尚未实证。工程预案分三级:优先系统 libGLv4 直连(AirCast libobs 已实证该路线),不行换 AirSCAD 的 gl4es libGL,仍不行才临时关闭 OpenGL 绘图。构建期已经为前两级铺路:sysroot 老 gl.h 缺 glBlendFuncSeparate,用 gl4es 头文件入前缀解决;AirSCAD 的最小 GLU 缺 tessellator API 导致 gl-render.cc 报 20 错,改为交叉编译 Mesa GLU 9.0.2 替换并处理 libnurbs 老代码的 register 关键字问题。
▍六、编译、安装与启动
交叉编译以前缀脚本为入口,顺序不可颠倒。macOS 上的步骤如下(路径来自 native/scripts/env.sh,按实际安装位置调整):
bash
source native/scripts/env.sh && bash native/scripts/build_deps.sh
bash native/scripts/build_octave.sh --cli
bash native/scripts/build_octave_host.sh
HAP 构建使用标准 hvigor 流程:
bash
export JAVA_HOME=/Applications/DevEco-Studio.app/Contents/jbr/Contents/Home
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk
hvigorw --mode module -p module=entry assembleHap --no-daemon
签名产物位于 entry/build/default/outputs/default/entry-default-signed.hap。安装启动:
bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airoctave.app
工程同时把打包五件套写入 PORTING.md:nativeLib 不 strip 调试符号、关闭 compressNativeLibs、NEEDED 去版本号、dlopen 模块进 entry/libs/arm64-v8a/(含 .oct 子目录)、拓扑序清单静态写在 napi_init.cpp。ai_log v3 记录的当前状态是:M2 make 零错误推进中,external 全部走 CC,f2c 路线验证通过,CLI 产物可经 hilog 冒烟验证 1+1。
▍七、当前功能边界
按工程记录,当前阶段已覆盖的部分:
- M0 工程骨架:native/cross 模板、env.sh、scripts、ohos QPA 副本(与 AirSCAD 副本 diff 为空)、壳层命名与 PORTING.md;
- 依赖链主体:PCRE2、ncurses、readline、GMP、MPFR、Qhull、GLPK 完成,QtHelp 6.11.1 装配完成,OpenBLAS、FFTW、SuiteSparse、HDF5 按 ai_log 处于构建推进中;
- f2c 全树转译路线:247+3 个 Fortran 源转译、libf2c 运行时入前缀、fake F77 configure 过检、autoreconf 重生成与可见性修正;
- 壳层与桥:XComponent 宿主、零链接 NAPI 桥、宿主核心骨架、菜单快照/IME/选择器/光标映射的接线代码。
尚未达成的部分(均有里程碑与风险登记依据):octave CLI 与 GUI 产物尚在编译推进中,Qt 首窗上机属 M3;约 2000 个 .m 文件的 rawfile 部署与 .oct 模块进 libs 属 M4;菜单、IME、触摸双模、文件选择器与暗色主题的逐项交互验收属 M5;eigs 等依赖 ARPACK 的函数首版降级禁用;帮助浏览器 qch 文档与界面 .qm 汉化缺口按 R8、R9 排期。当前阶段更准确的定位是"轻矩阵移植基础骨架与数学依赖链成果":架构定案、转译路线与依赖栈已经实证,距离可交互的科学计算环境还隔着 M2 到 M5 的既定里程碑。
▍八、总结
轻矩阵工程的价值目前主要体现在路线层面:它验证了在无 Fortran 编译器的 NDK 上,以 f2c 全树转译加 C_LAPACK 的组合重建 Octave 数学内核是可行的;也验证了共享 Qt 栈、自研 QPA 与双层 dlopen 架构可以复用到 Qt Widgets 的重型科学应用。这些结论写在 ai_log 的症状、根因、对策条目里,对后续同类移植是直接的参考资料。
后续工作的重心严格跟随里程碑:先让 M2 的 CLI 产物稳定通过 hilog 冒烟,再以 QtHelp 就位后的完整 configure 推进 M3 首窗,随后是 .m 树部署、打包五件套与交互标准验收。每一步都以可构建的产物与上机行为为验收点,风险登记册中的沙箱拒载 .oct、GL 路径与 appfreeze 三项则作为 M3、M4 的重点观察对象。
■ 应用:轻矩阵(from GNU Octave)(AirOctave)
■ 定位:数值计算与编程环境(GNU Octave,MATLAB 同类工具)
■ 上游:GNU Octave | 路线:Qt6 6.11.1+QPA | 版本:1.0.0
■ 市场状态:开发中,即将上架
■ 官网(免费下载): https://www.shunidc.cn/
————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/
PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:(AtomGit 仓库建立后回填)
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
▍一、为什么要适配 轻矩阵(GNU Octave)
GNU Octave 是开源数值计算环境,语言与 MATLAB 高度兼容,自带基于 Qt Widgets 的交互式图形界面,覆盖命令行、脚本编辑、工作区变量与绘图。矩阵运算、数值求解与算法原型验证是高校与工程场景的长期需求,把 Octave 带到鸿蒙 PC,意味着 2in1 设备可以获得一套不依赖云端的本地科学计算环境。
本工程(产品名轻矩阵,仓库 AirOctave)于 2026-09-25 立项,上游为 GNU Octave 11.3.0。包名 com.airoctave.app,版本 1.0.0,deviceTypes 覆盖 phone、tablet 与 2in1,目标设备为 MatePad 与 MateBook Pro,SDK 为 6.1.0(23),NDK 采用 AirChat-SDK24(LLVM15/musl),目标 ABI 为 arm64-v8a。与纯 ArkTS 应用不同,这是典型的 NDK 交叉编译移植工程:上层的 liboctave、libinterp 与 Qt GUI 全部要从源码构建。
需要说明的是,截至工程 PORTING.md 与 ai_log 记录的时间点,项目处于 M1 依赖链与 M2 CLI 内核编译的推进阶段,GUI 首窗属于 M3 里程碑。本文如实按当前阶段撰写,已完成的依赖与已验证的转译路线均有 ai_log 记录支撑,未达成的部分明确列为后续目标。
▍二、先划清边界:不把编译通过当成适配完成
桌面版 Octave 依赖发行版的包管理与完整的 Unix 进程模型:gfortran 编译数学外围层、独立 octave-gui 进程、桌面 OpenGL、texinfo 文档链。鸿蒙沙箱对这几条都有实质约束,工程在 PORTING.md 的路线定案与风险登记册中把边界逐条写死,避免用"configure 通过"冒充移植完成。
层次 传统桌面 Octave 方案 鸿蒙侧当前方案
发行形态 桌面安装包,octave-gui 独立进程 HAP 双层架构,GUI 库化进宿主线程(沙箱无 execve)
GUI 框架 系统 Qt + X11/Wayland AirGis Qt6.11.1 预编译栈 + 自研 ohos QPA(AirCast 血统)
绘图 GL 桌面 OpenGL 2.1 系统 libGLv4 直连优先,gl4es 备胎,最终可关绘图兜底
Fortran 数学层 gfortran 编译 .f 源 f2c 全树转译为 C + libf2c 运行时 + OpenBLAS C_LAPACK
依赖来源 发行版包管理器 native/install 前缀自建:PCRE2、ncurses、readline、GMP、MPFR、Qhull、GLPK、OpenBLAS、FFTW、SuiteSparse
数值扩展 ARPACK/qrupdate 提供 eigs 等 NDK 无 Fortran,首版 --without-arpack、--without-qrupdate 降级
帮助文档 texinfo 生成 qch 首版帮助浏览器空壳,M5 后宿主工具链补生成
这条边界的核心判断是:Octave 主体是 C++,真正卡脖子的是 liboctave/external 的 Fortran 数学外围层,把它换成 f2c 转译物,其余部分才有讨论价值。
▍三、鸿蒙版的整体架构
工程采用 Air 系双层架构。ArkTS 薄壳只承载 XComponent 与系统能力接线;NAPI 桥 libairoctavebridge.so 零预编译链接,运行时按拓扑序 dlopen 全部依赖;宿主核心 libairoctave_core.so 在独立线程上完成环境变量前置、日志与信号处理,再调起 GUI 库化的 Octave 入口;Qt 绘图经由自研 QPA 接到系统 NativeWindow。
text
EntryAbility / Index.ets(ArkTS 薄壳)
├── XComponent(SURFACE)承载 Qt 画面
├── 菜单栏:bindMenu 弹系统 Menu,QMenuBar 快照轮询刷新
├── IME:离屏 1×1 TextInput 通道
└── 选择器:QPA pick 请求 → TSFN → DocumentViewPicker 回流
└── libairoctavebridge.so(napi_init.cpp,NAPI 薄桥,零预编译链接)
└── dlopen 拓扑序全栈(RTLD_GLOBAL)
├── Qt6.11 栈(Core/Gui/Widgets/Help/OpenGL/...)
├── libqohos.so(ohos QPA)
├── liboctave.so(liboctave/libinterp)
└── libairoctave_host.so(airoctave_octave_main)
└── octave::qt_application(GUI 库化,宿主线程)
主要目录如下:
text
AirOctave/
├── PORTING.md # 路线定案、依赖矩阵、补丁清单、风险登记册
├── ai_log/ # 立项、M1 依赖链、M2 f2c 转译三轮记录
├── AppScope/app.json5 # 包名 com.airoctave.app
├── entry/src/main/
│ ├── cpp/
│ │ ├── napi_init.cpp # NAPI 薄桥:dlsym core API、输入注入、TSFN
│ │ ├── airoctave_core.cpp # 宿主核心:环境前置、dlopen 拓扑、菜单快照
│ │ ├── octave_host.cpp # GUI 库化入口(复刻 src/main-gui.cc)
│ │ ├── qt_platform/ohos_qt6/ # 自研 ohos QPA(inputcontext/filedialoghelper)
│ │ ├── patch_libs.py # NEEDED 去版本号等打包链
│ │ └── fix_elf_align.py # ELF 对齐修正
│ └── ets/ # Index、FileManager、SubWindowManager、DialogHost
└── native/
├── cross/ # aarch64 toolchain/cross 文件、fake F77、musl 垫片
├── sources/ # PCRE2、ncurses、readline、GMP、MPFR、Qhull、GLPK、
│ # OpenBLAS、FFTW、SuiteSparse、HDF5 等源码档
├── patches/ # openblas-ohos-affinity.diff
├── scripts/ # env.sh、build_deps.sh、build_octave.sh、f2c_convert.sh
└── install/ # 本工程自有产物前缀(含 octave 子前缀)
▍四、核心适配过程
### 1. 零链接 NAPI 桥与拓扑序 dlopen
entry/src/main/cpp/napi_init.cpp 只链接 ace_napi、hilog、native_window 等系统库,绝不链接任何预编译 .so。它在启动时 dlopen libairoctave_core.so 并以 dlsym 取出 start、菜单快照、输入注入等接口;XComponent 的触摸与按键事件经 QOhos_Inject 系列符号转投 Qt QPA;系统文件选择器通过 TSFN 回调到 ArkTS 的 DocumentViewPicker,结果再回流 QPA。预编译依赖的加载顺序全部由 airoctave_core.cpp 中的静态数组按拓扑序管理:Qt6 各模块先于 libqohos(QPA),再是 liboctave 与 libairoctave_host,顺序错误即加载失败,因此清单化是唯一可靠做法。
### 2. 宿主核心:环境前置与 Qt 主线程
airoctave_core.cpp 在宿主线程上完成四件事:把 HOME 与 XDG 系列环境变量前置到沙箱可写路径,使 QSettings 与 Qt 缓存不落非法位置;按拓扑序 dlopen 全部依赖(RTLD_GLOBAL);在该线程上调起 airoctave_octave_main,保证 QApplication 的构造线程即 Qt 主线程;注册 SIGSEGV 处理与日志镜像。此外预留了绑定 127.0.0.1 的 8098 端口 HTTP 通道,作为自动化回归的触发入口,局域网不可达,仅可经 hdc fport 转发使用。
### 3. f2c 全树转译:把 Fortran 数学外围层变成 C
这是 M2 的核心突破。Octave 并非全 C++:liboctave/external 下有 247 个 .f 源(amos、dassl、odepack、quadpack、lapack-xtra 等),liboctave/util 另有 3 个。native/scripts/f2c_convert.sh 用宿主自建的 netlib f2c(20240504)把全部 .f 转为 ANSI C,同步改写 module.mk 与顶层 Makefile.in 的源列表,为 mk-f77-def.in.sh 增加 .c 分支,再以宿主 autoconf 2.73 与 automake 1.18.1 执行 autoreconf -fi 全量重生成。libf2c 运行时交叉编译踩了四个坑并全部记录:makefile 硬编码 ld -r 换成 ld.lld;arith.h 由 arithchk 探测程序生成而交叉环境不可运行,改在宿主跑(LP64 与目标一致);uninit.c 依赖 glibc 头文件被剔除;打包环节宿主 ar 打 ELF 目标会静默产出空档,必须换 llvm-ar。转译物的 COMMON 块需要全局 -fcommon,configure 注入 LIBS="-lz -lf2c" 提供运行时符号。
### 4. 依赖链交叉编译与共享栈复用
native/scripts/build_deps.sh 按依赖序构建数学与文本栈,全部产物为静态库进 native/install,避免扩大 entry/libs 部署面,stamp 文件支持增量跳过。各库的要点:PCRE2 10.45 静态构建并 --disable-jit 规避 RWX 内存段;ncurses 6.5 只出 widec 变体并以 libncurses.a 软名满足 gnulib 探测;OpenBLAS 0.3.29 以 C_LAPACK=1 NOFORTRAN=1 提供 f2c 修饰的 BLAS 与 LAPACK,与 Octave 的 f2c 调用约定精确对齐;SuiteSparse 7.10.3 取 amd、klu、umfpack、cholmod、cxsparse 等子集;FFTW 3.3.10 出 double 与 float 一对。Qt 栈与 QScintilla 直接复用 AirGis 预编译前缀,缺失的 QtHelp 由 native/scripts/build_qthelp.sh 从 qttools 6.11.1 源码只建 Help 目标后增量装配,不动共享栈既有库。
### 5. GUI 库化与 QPA 系统接线
上游 octave-gui 是独立进程,而沙箱禁 execve。native/scripts/build_octave.sh 在 M3 将走完整 Qt configure,entry/src/main/cpp/octave_host.cpp 复刻上游 src/main-gui.cc 的 182 行薄壳,导出 airoctave_octave_main 供宿主线程调起 octave::qt_application,把整个 GUI 当库驱动。ArkTS 侧 Index.ets 承担 QPA 覆盖不到的系统能力:顶栏菜单以 bindMenu 弹系统 Menu 组件,经 QMenuBar 快照轮询刷新与触发回注;IME 用离屏 1×1 TextInput 通道;QPA 光标协议码映射为系统 PointerStyle 枚举;差分回注按码点而非 UTF-16 单元计数,保证 emoji 退格正确。
▍五、适配中遇到的几个难点
### 难点一:NDK 没有 Fortran 编译器,而 configure 硬性要求一个
Octave 的 configure 检查 F77,NDK 工具链不存在 Fortran 前端,ARPACK 与 qrupdate 也因此首版禁用。工程的两级对策:先用 native/cross/make_fake_f77.sh 生成恒返回 0 的假 F77 让 configure 过检,同时以 cache 预置目标修饰与字长(mangling、sizeof=4);真正的 Fortran 代码则由 f2c 全树转译承担,并配 --enable-fortran-calling-convention=f2c,使 Octave 的调用约定与 OpenBLAS C_LAPACK 的 f2c 修饰完全一致。假编译器只能骗过探测,不能产出目标码,这两级缺一不可。
### 难点二:半替换的构建系统比全手动更危险
f2c 转译后依赖文件已变成 .c,但 automake 预生成的规则仍携带 --tag=F77 指向假编译器,make 在中途以 fake-f77 处理 .c 目标,报错形态极具迷惑性。工程的做法是不做半自动修补,直接 autoreconf -fi 全量重生成构建系统,让规则与源文件语言重新一致,并在 f2c_convert.sh 中固化为幂等步骤。
### 难点三:可见性标志悄悄吞掉转译符号
Octave 默认以 -fvisibility=hidden 编译,f2c 产物没有可见性属性标注,全部符号被隐藏,表现为 liboctave.so 符号变 local、liboctinterp 解析失败。这个失败不指向根因,容易在链接脚本上浪费时间。对策是 configure 加 --disable-lib-visibility-flags 放弃导出控制,工程在 PORTING.md 补丁清单中把它列为 P1 的组成部分。
### 难点四:musl 与沙箱差异族
风险登记册 R4 记录了这一族:musl 无 pthread_cancel(垫片已备)、gnulib 交叉探测特殊、ELF 无版本化 NEEDED(patch_libs.py 处理)、fork-exec 受限(system() 与 pkg 命令降级)。其中影响最大的是解释器初始化重——扫 load-path 与历史记录会拖住 ArkTS 主线程触发 appfreeze,工程因此把解释器全量放进 core 后台线程,配启动遮罩与 TSFN 回调;R5 则针对约 2000 个 .m 文件加 share 数据的部署,采用首启版本号门控一次性解包加分块流拷贝。
### 难点五:GL 栈的三级降级预案
Octave 绘图工具包用 QOpenGLWidget 依赖桌面 GL 2.1 语义,而 QPA 的 GL 上下文路径尚未实证。工程预案分三级:优先系统 libGLv4 直连(AirCast libobs 已实证该路线),不行换 AirSCAD 的 gl4es libGL,仍不行才临时关闭 OpenGL 绘图。构建期已经为前两级铺路:sysroot 老 gl.h 缺 glBlendFuncSeparate,用 gl4es 头文件入前缀解决;AirSCAD 的最小 GLU 缺 tessellator API 导致 gl-render.cc 报 20 错,改为交叉编译 Mesa GLU 9.0.2 替换并处理 libnurbs 老代码的 register 关键字问题。
▍六、编译、安装与启动
交叉编译以前缀脚本为入口,顺序不可颠倒。macOS 上的步骤如下(路径来自 native/scripts/env.sh,按实际安装位置调整):
bash
source native/scripts/env.sh && bash native/scripts/build_deps.sh
bash native/scripts/build_octave.sh --cli
bash native/scripts/build_octave_host.sh
HAP 构建使用标准 hvigor 流程:
bash
export JAVA_HOME=/Applications/DevEco-Studio.app/Contents/jbr/Contents/Home
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk
hvigorw --mode module -p module=entry assembleHap --no-daemon
签名产物位于 entry/build/default/outputs/default/entry-default-signed.hap。安装启动:
bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airoctave.app
工程同时把打包五件套写入 PORTING.md:nativeLib 不 strip 调试符号、关闭 compressNativeLibs、NEEDED 去版本号、dlopen 模块进 entry/libs/arm64-v8a/(含 .oct 子目录)、拓扑序清单静态写在 napi_init.cpp。ai_log v3 记录的当前状态是:M2 make 零错误推进中,external 全部走 CC,f2c 路线验证通过,CLI 产物可经 hilog 冒烟验证 1+1。
▍七、当前功能边界
按工程记录,当前阶段已覆盖的部分:
- M0 工程骨架:native/cross 模板、env.sh、scripts、ohos QPA 副本(与 AirSCAD 副本 diff 为空)、壳层命名与 PORTING.md;
- 依赖链主体:PCRE2、ncurses、readline、GMP、MPFR、Qhull、GLPK 完成,QtHelp 6.11.1 装配完成,OpenBLAS、FFTW、SuiteSparse、HDF5 按 ai_log 处于构建推进中;
- f2c 全树转译路线:247+3 个 Fortran 源转译、libf2c 运行时入前缀、fake F77 configure 过检、autoreconf 重生成与可见性修正;
- 壳层与桥:XComponent 宿主、零链接 NAPI 桥、宿主核心骨架、菜单快照/IME/选择器/光标映射的接线代码。
尚未达成的部分(均有里程碑与风险登记依据):octave CLI 与 GUI 产物尚在编译推进中,Qt 首窗上机属 M3;约 2000 个 .m 文件的 rawfile 部署与 .oct 模块进 libs 属 M4;菜单、IME、触摸双模、文件选择器与暗色主题的逐项交互验收属 M5;eigs 等依赖 ARPACK 的函数首版降级禁用;帮助浏览器 qch 文档与界面 .qm 汉化缺口按 R8、R9 排期。当前阶段更准确的定位是"轻矩阵移植基础骨架与数学依赖链成果":架构定案、转译路线与依赖栈已经实证,距离可交互的科学计算环境还隔着 M2 到 M5 的既定里程碑。
▍八、总结
轻矩阵工程的价值目前主要体现在路线层面:它验证了在无 Fortran 编译器的 NDK 上,以 f2c 全树转译加 C_LAPACK 的组合重建 Octave 数学内核是可行的;也验证了共享 Qt 栈、自研 QPA 与双层 dlopen 架构可以复用到 Qt Widgets 的重型科学应用。这些结论写在 ai_log 的症状、根因、对策条目里,对后续同类移植是直接的参考资料。
后续工作的重心严格跟随里程碑:先让 M2 的 CLI 产物稳定通过 hilog 冒烟,再以 QtHelp 就位后的完整 configure 推进 M3 首窗,随后是 .m 树部署、打包五件套与交互标准验收。每一步都以可构建的产物与上机行为为验收点,风险登记册中的沙箱拒载 .oct、GL 路径与 appfreeze 三项则作为 M3、M4 的重点观察对象。
楼主
8 天前