尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

把 cc-switch 搬上鸿蒙 PC 的那些天:一个 Tauri 应用的移植笔记

把 cc-switch 搬上鸿蒙 PC 的那些天:一个 Tauri 应用的移植笔记 这篇写给自己也写给打算把 Tauri 应用搬到鸿蒙 PC 上的人。全程真实记录坑都是踩过的代码都是能跑的。项目地址https://atomgit.com/qq8864/cc-switch-harmonyos更多交流学习欢迎加入开源鸿蒙PC社区https://harmonypc.csdn.net/欢迎在PC社区平台申请新建项目https://atomgit.com/OpenHarmonyPCDeveloper猫哥的博客https://blog.csdn.net/qq8864起因事情得从某天下午说起。我盯着电脑屏幕上的一个 GitHub 仓库心里冒出一个念头「这玩意儿要是能跑在鸿蒙 PC 上应该挺有意思的。」我说的这个仓库就是cc-switchgithub.com/farion1231/cc-switch——一个火得离谱的开源项目GitHub 上 12 万 star常年挂在 Trending 榜上。它是给 AI 编程工具用的「All-in-One 配置管理器」Claude Code、Claude Desktop、Codex、Gemini CLI、Grok Build、OpenCode、OpenClaw、Hermes Agent这些工具的环境变量、API 配置、供应商切换它一个界面全给你管了还带代理、用量统计、MCP 管理这些进阶功能。写 AI 代码的人基本人手一个说它是这个品类的头部工具毫不夸张。它本身是 Tauri v2 写的——React 前端 Rust 后端Windows/macOS/Linux 三端齐活。但偏偏没有鸿蒙版。鸿蒙 PC那些能跑桌面应用的鸿蒙笔记本和二合一设备这两年生态起来了桌面应用却还少得可怜。我寻思Tauri 应用能不能也搬过去cc-switch 这种重后端数据库、代理服务、OAuth 全都用 Rust 写死的桌面工具恰恰是检验鸿蒙桌面生态成色的好靶子。于是我就这么开工了。说实话一开始心里是有点打鼓的——鸿蒙 PC 的应用生态跟 Windows 完全不是一个体系Rust 能不能交叉编译过去、WebView 用谁家的、界面怎么渲染全是未知数。但这事儿越未知越有意思干就完了。我手头有个现成的参考答案社区里有人维护了一个专门给 OpenHarmony 用的 Tauri forkatomgit.com/qq8864/tauri把 tauri 2.8.x 魔改成了能跑在鸿蒙上的版本原理是用 napi 桥接 ArkWeb 渲染。也就是说前端基本不用动Rust 后端交叉编译成 .so套一个 DevEco 工程壳子打包成 HAP就能装到鸿蒙设备上。听着挺美对吧后来的事证明理想很丰满现实很骨感。这一趟下来光编译就炸了十几轮真机上又炸了好几轮前后折腾了整整一天半。环境准备先把地基打牢我的开发机是 Windows 11。要交叉编译到鸿蒙的 aarch64需要这些东西Rust 工具链stable-x86_64-pc-windows-gnullvmgnullvm 是给交叉编译用的普通 gnu 工具链链接器会报-lgcc_eh找不到鸿蒙目标aarch64-unknown-linux-ohosrustup 直接加鸿蒙 SDK 的 NDK 部分里面有 clang、llvm-ar、sysrootDevEco Studio打包 HAP 用自带 hvigor、ohpm、node一台鸿蒙 PC 真机HUAWEI MateBook ProAPI 24aarch64这里有个小坑先说SDK 有两个一个是纯 NDK只有 native 工具链一个是完整 SDK含 ets/js/toolchains。交叉编译 Rust 用 NDKDevEco 打包必须指向完整 SDK指错了会报SDK component missing。我当时差点被这个绕进去。工具链装完验证一下cargo tauri ohos --help能看到 init/build 子命令说明 fork 的 CLI 装好了。好地基算打完了。第一场硬仗Cargo 解析器大战按文档把 Cargo.toml 里的 tauri 依赖指到 fork 的路径上然后编译。结果第一炮就哑了error: failed to select a version for tauri package tauri links to the native library Tauri, but it conflicts with a previous package which links to Tauri as well: tauri v2.8.5 (path)我盯着这个错误看了半天。什么意思呢就是说 cargo 同时拉了两个 tauri一个是我的路径依赖fork一个是 crates.io 上的官方版插件们要求的。两个都声明了links Tauricargo 觉得这俩是死对头直接罢工。这玩意儿特别坑因为它跟我以前理解的 cargo 行为完全不一样。我以为路径依赖会自动满足所有对tauri的版本要求但实际是解析器根本不会拿路径依赖去给其他 crate 的 registry 需求当候选。我甚至写了个空工程复现就一个路径 tauri 一个插件照样炸。不是移植对象的问题是这条路本身就走不通。后来我试了[patch.crates-io]——把 fork 的 tauri 系列七个 crate 全部 patch 进去把声明保留成tauri 2.8.2。这一下就通了。原理也简单patch 是指定替换cargo 会把 registry 里的 tauri 整体换成 fork 的不存在两个并存的问题。这里还埋了个连环雷改完 manifest 之后解析还是失败提示 patch 版本和 Cargo.lock 不一致。因为 lock 里还钉着官方 tauri 2.10.3。得跑cargo update -p tauri -p tauri-build ...把 patch 的那些包全部列上让 lock 刷新。千万别手贱删 Cargo.lock——删了会全量重拉索引我们这网络环境直接超时。光解决 tauri 本身还不够。fork 的 tauri 是 2.8.5但 cc-switch 生态早就走到 2.10 了插件们一个个都要求新版本。于是开始了痛苦的锁版本环节tauri-plugin-log 锁 2.7.1、dialog 锁 2.4.2、opener 锁 2.5.2……这些还都能靠查索引解决。真正阴险的是传递依赖。tauri-plugin-dialog 依赖 tauri-plugin-fslock 里 fs 是 2.4.5而 2.4.5 要求 tauri ^2.9.3——cargo 的依赖解析是全目标统一的不管你 cfg 段怎么写只要进了解析图它就会把官方 tauri 拉回来然后跟 patch 的 2.8.5 撞车。我一开始还以为是锁错了 dialog排查了半天才意识到是 dialog 背后的 fs 在作妖。解决方式很粗暴把tauri-plugin-fs 2.4.4显式写进依赖钉死。这轮下来我最大的体会是移植的第一步不是改代码是跟依赖解析器搏斗。不把版本图理清楚后面全是白搭。第二场硬仗编译连环炸解析器消停了编译又开始了它的表演。第一个炸的是 openssl-sysCould not find directory of OpenSSL installation查了一下是 reqwest 没关默认特性——默认特性带 native-tlsnative-tls 在 Linux 系鸿蒙的 target_os 就是 linux要调 openssl。可鸿蒙哪有系统 openssl 给你用。解法reqwest 加default-features false改走 rustls鸿蒙段再用rustls-tls-webpki-roots内置根证书因为鸿蒙没有系统证书库。然后是 rquickjs-syscouldnt read bindings\aarch64-unknown-linux-ohos.rs这个库的绑定是按 target 文件名生成的没有鸿蒙的份。我看了眼它的 build.rs它的 bindgen 特性会把 Rust triple 直接当 clang 的--target传——aarch64-unknown-linux-ohos这玩意儿根本不是合法的 LLVM triplebindgen 必然死。所以正经路子走不通直接土办法把aarch64-unknown-linux-gnu.rs复制一份改名成aarch64-unknown-linux-ohos.rs。反正是 C API 声明跟架构无关能编过就行。注意这改的是 cargo 缓存换台机器要重做——以后有空给上游提个 PR。接着是链接器。按文档配了个ohos-clang.cmd包装脚本当链接器结果linking with ...\ohos-clang.cmd failed ... 不是内部或外部命令Windows 上 rustc 是直接 CreateProcess 调链接器的根本不会去解析 .cmd/.bat。得把链接器直接指到 NDK 的clang.exetarget 和 sysroot 通过 rustflags 的 link-arg 传进去。这个改法实测有效。然后 build.rs 又给我上了一课。它里面有个 Windows manifest 的逻辑#[cfg(target_os windows)]{println!(cargo:rustc-link-arg/MANIFEST:EMBED);}看着挺正常对吧但build script 是按宿主编译的——我在 Windows 上交叉编译这个 cfg 恒为真于是/MANIFEST:EMBED这种 MSVC 链接器参数就泄漏给了鸿蒙的 clangclang 直接报no such file: /MANIFEST:EMBED。改成运行时判断CARGO_CFG_TARGET_OS环境变量才治本。这个坑特别隐蔽因为它只在交叉编译时出现平时桌面构建完全正常。还有个小的ohos-arkui-sys 这个 crate 的 build.rs 要求OHOS_NDK_HOME环境变量不设就 panic。设了就行。编译本身也磨人。全量依赖树六百多个 crate第一次编十几分钟起步。我一开始用async后台跑结果每次 300 秒就被杀——后来才知道是超时限制改成同步跑加长超时才消停。中间还踩了个rustup的坑仓库根目录有个rust-toolchain.toml钉了 1.95我们这儿的镜像没有这个版本导致所有 cargo 命令从根目录跑都会 404。解法就是所有构建都进src-tauri/目录再跑让子目录的 rust-toolchain.toml 生效。等到依赖全绿终于轮到应用本身的代码报错了——23 个编译错误全是no method named show/set_focus/hide...。fork 的鸿蒙 WebviewWindow API 面比桌面窄得多一大堆窗口操作方法都没有。道理也讲得通鸿蒙上窗口是系统管的轮不到你调。解法就是把所有用到这些方法的代码用#[cfg(desktop)]包起来命令级的直接给鸿蒙分支返回当前平台不支持。这一轮编译打完.so 终于出来了ELF 64-bit ARM aarch6421MB。那一刻挺有成就感的。第三场硬仗真机首跑秒崩.so 有了接着是套 DevEco 工程、打包 HAP、签名、上真机。中间踩了 hvigor 的钩子坑fork 模板在entry/hvigorfile.ts里注册了个 cargo 钩子打包时会去调 CLI 的 dev 脚本报什么 server-addr 找不到——直接把钩子文件替换成干净版签名这块必须用 DevEco Studio 图形界面生成华为账号登录自动写材料这个绕不开。HAP 装上去aa start启动。然后……进程秒没。ps看不到进程hilog 里只有一句LastFatalMessage: [OnSurfaceCreated] crash occured on callback: 0x5b73fd2724最气人的是应用里的 panic hook 压根没被触发crash.log 也没写出来。那一刻我意识到鸿蒙上调试 Rust 崩溃跟桌面上完全不是一个玩法stderr 不可见/data/local/tmp 被沙箱拦faultlog 目录 shell 没权限读——几乎所有的常规诊断手段全废了。我被逼出了一个土办法里程碑文件。在run()和setup()的关键节点往应用可写、shell 可读的路径写日志文件二分定位崩溃发生在哪一步。试了几个路径才发现应用沙箱里/data/storage/el2/base/files/是应用能写、shell 也能读的shell 读/data/app/el2/100/base/bundle/files/而/data/local/tmp和/storage/Users/currentUser应用根本写不进去。里程碑一跑真相大白run() start panic_hook ok setup start rustls ok卡在 log 插件初始化Operation not permitted (os error 1)。原因查明白了——dirs::home_dir()在鸿蒙上没有 HOME 环境变量返回 None代码回退到进程工作目录/然后 log 插件要去/下面建目录被系统按在地上摩擦。这里有个小插曲有人跟我说鸿蒙 PC 的 home 是/storage/Users/currentUser我改了结果 EPERM 依旧——应用根本没权限写用户目录的根。最后实测下来应用自己的数据目录/data/storage/el2/base/files才是真正可写的 home。把这个路径填进get_home_dir()的鸿蒙分支log 插件瞬间就过了。这一轮修完应用活了主进程、GPU 进程、render 进程三件套齐活数据库迁移、模型定价、OAuth、代理服务全都初始化成功。那一刻的心情真的只能用「热泪盈眶」来形容。第四场硬仗白屏与 localStorage应用进程活了但界面还是白屏。日志里躺着这么一行Failed to request http://localhost:3000/localhost:3000这不是 devUrl 吗我明明打的是 release。查了 fork 的 build.rs发现一行要命的逻辑letdev!custom_protocol;原来 Tauri 的生产模式是靠custom-protocol这个 feature 区分的而它通常由 CLI 在构建时注入。我之前图省事用裸cargo build交叉编译这个 feature 就没带上结果整个程序被编译成了开发模式webview 去加载 devUrl当然什么都没有。换成cargo tauri ohos build之后前端终于从tauri://localhost/assets/...加载了页面也渲染出来了。但紧接着又是新错误TypeError: Cannot read properties of null (reading getItem)React 首屏直接炸。查了半天是 ArkWeb 在自定义 schemetauri://localhost下window.localStorage居然是 null。前端一堆地方初始化时直接localStorage.getItem(...)一碰就挂。这属于平台限制前端绕不过去只能适配在main.tsx顶部加了个内存版 localStorage polyfill——检测到window.localStorage为空就用 Map 实现顶上保证不崩。代价是重启不持久化对这个场景可以接受。这里还埋了个很隐蔽的坑改完前端重新打包部署错误居然还在bundle 的 hash 还是旧的。排查发现ArkWeb 缓存了旧页面而更坑的是真正服务前端的是 .so 内嵌的资产编译期嵌入的不是 rawfile。所以改前端必须走完整链路重新构建 dist → CLI 重建 .so自动嵌入新 dist→ 重新打包 HAP → 清缓存重装。漏一步都不行。收尾最后一版部署上去三进程稳定存活日志零错误页面渲染正常。整个链路终于通了Rust 交叉编译 → libcc_switch_lib.so → DevEco 工程 → HAP 签名 → hdc 安装 → 真机跑起来回顾这一趟最值钱的经验大概是这么几条fork 的 tauri 必须走[patch.crates-io]别用 path 依赖。这是最大的坑没有之一。必须用cargo tauri ohos build构建裸 cargo 会缺custom-protocolfeature编出来是 dev 模式。鸿蒙上调试 Rust 崩溃先解决日志可见性问题。里程碑文件 双路径写入是最快的定位手段。dirs::home_dir()在鸿蒙上是废的应用的可写 home 是自己的沙箱数据目录。改前端要全链路重建.so 内嵌资产才是真正服务页面的那份。ArkWeb 自定义 scheme 下 localStorage 为 null前端要 polyfill。至于那些限制——没有系统托盘、原生对话框用不了、自更新砍了、localStorage 不持久——都在预期内桌面版功能不受影响。一个 12 万 star 的开源桌面工具数据、数据库、代理服务全都正常地在鸿蒙 PC 上跑起来了光是这一点这趟折腾就值了。至于其他细节问题待优化实测逐步完善不过那些都问题不大了。如果有人问我建不建议搞鸿蒙 PC 应用移植我的答案是工具链比想象中成熟坑比想象中多但是真的能跑起来的。希望这篇笔记能帮你少踩几个我踩过的坑。
返回列表