Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 48 additions & 72 deletions .claude/skills/ohos-build/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ description: 编译 Tauri OpenHarmony 项目(examples/api),生成 HAP 包
> **路径约定**: 以下脚本中 `${PROJECT_ROOT}` 指 tauri 和 openharmony-ability 共同的父目录。
> 例如本地布局为 `/d/xuqiu/tauri-2.0/{tauri,openharmony-ability}` 时,`PROJECT_ROOT=/d/xuqiu/tauri-2.0`。
> 使用前请先 `export PROJECT_ROOT=...` 或在 env.sh 中设置。

## 构建方式

### 方式一:cargo tauri ohos run(推荐)
Expand Down Expand Up @@ -38,14 +38,15 @@ OHOS_DEVICE_TYPE=desktop bash ${PROJECT_ROOT}/tauri/.claude/skills/ohos-build/sc
**注意**:必须先设置 `OHOS_DEVICE_TYPE` 环境变量再调用脚本。不要先 `source env.sh`,因为 `env.sh` 会设置 `OHOS_DEVICE_TYPE` 默认值,可能覆盖你传入的参数。

脚本自动完成全部流程:
1. 检测 `openharmony-ability/` 源码变更,自动重建 HAR 包(ohpm 同步由第 3 步 `cargo tauri ohos run` 内部完成,不手动 ohpm)
2. prerequisites(CLI 不做的前置):pnpm install / build:api / 插件 dist-js / ACL 检查
3. `cargo tauri ohos run` 一步完成:
- 前端构建(beforeBuildCommand,继承 VITE_AUTOTEST=true)
- Rust 交叉编译(aarch64-unknown-linux-ohos,release,--features prod)
- .so 拷贝 + hvigorw assembleHap(TAURI_OHOS_SKIP_DEVECO_SCRIPT 禁用 tauriPlugin,build-profile.json5 证书签名)
- 安装已签名 HAP(带 hdc false-success 检测)→ 启动
4. 等待 30s → 拉取 test-report → 分析结果
1. 检测 `openharmony-ability/` 源码变更,自动重建 HAR 包并 ohpm install
2. 前端构建(pnpm + vite,VITE_AUTOTEST=true)
- Step 2: 构建 @tauri-apps/api(首次或缺失时)
- Step 2.5: 构建插件 dist-js(每次执行,防止 git pull 后插件产物过期)
- Step 3: 前端 vite 构建
3. Rust 交叉编译(aarch64-unknown-linux-ohos,release,--features prod)
4. 拷贝 .so → hvigorw assembleHap(自动禁用/恢复 tauriPlugin,使用 build-profile.json5 中的证书签名)
5. 卸载旧版 → 安装已签名 HAP → 启动
6. 等待 30s → 拉取 test-report → 分析结果

### 方式三:cargo tauri ohos build --app(多形态打包)

Expand Down Expand Up @@ -93,11 +94,10 @@ PR #59 将 app 拆分为 mobile 和 desktop 两个 entry 模块:

| 脚本 | 功能 |
|------|------|
| `env.sh` | 环境配置:DevEco Studio 路径解析(`DEV_ECO_STUDIO_INSTALL_PATH` 优先 → `DEVECO_HOME` → 自动检测,不落盘)、CC/linker/JAVA_HOME/PATH,必须在其他脚本前 source |
| `prerequisites.sh` | CLI 不做的开发期前置:pnpm install / build:api / 插件 dist-js / ACL 检查。被 build-ohos.sh 和 run-tests.sh source,不直接执行 |
| `run-tests.sh` | 一键全流程:HAR 重建 → prerequisites → `cargo tauri ohos run`(build+install+launch,带 hdc false-success 检测)→ 等待 → 拉取报告 → 分析 |
| `build-ohos.sh` | prerequisites + `cargo tauri ohos build`(Rust 编译/.so/hvigorw/签名由 CLI 处理)。项目专属 feature 经 `TAURI_BUILD_FEATURES` 传入 |
| `install.sh` | 仅安装启动(使用已签名 HAP),不构建不签名。日常流程已被 `cargo tauri ohos run` 替代;保留供单独安装场景 |
| `env.sh` | 环境配置:CC/linker/JAVA_HOME/PATH,必须在其他脚本前 source |
| `run-tests.sh` | 一键全流程(含模板检测、HAR 自动重建、tauriPlugin 自动禁用/恢复、自动测试) |
| `build-ohos.sh` | 构建全流程(模板检测 → 前端 → Rust → .so → hvigorw 签名打包),自动处理 tauriPlugin |
| `sign-and-install.sh` | 仅安装启动(使用 hvigorw 已签名的 HAP),不构建不签名 |

## 模板修改后的完整生效流程

Expand All @@ -113,17 +113,16 @@ cmd.exe /c "rmdir /s /q ${PROJECT_ROOT}\\tauri\\examples\\api\\src-tauri\\gen\\o

# 3. 执行「init 后补充步骤」(见下方章节)

# 4. 重建 HAR(仅当改了 openharmony-ability 的 ArkTS 源码;改 Rust 源码跳过此步)
# 4. 重建 HAR 并 ohpm install
cd ${PROJECT_ROOT}/openharmony-ability
source ${PROJECT_ROOT}/tauri/.claude/skills/ohos-build/scripts/env.sh
./pack.bat # Windows 批处理:同步 native_ability ETS → package/ + tar 打 ability.har
# 5. 构建 HAP —— cargo tauri 内部会自动跑 ohpm install 同步依赖,严禁手动 ohpm
cd ${PROJECT_ROOT}/tauri/examples/api/src-tauri
cargo tauri ohos build --device-type desktop --features prod
ohrs build --arch arm64 --skip-napi-check 2>&1 | tail -5 || true
bash scripts/pack.sh
tar -czf ability.har package
cd ${PROJECT_ROOT}/tauri/examples/api/src-tauri/gen/ohos
ohpm install --all
```

> **严禁手动 `ohpm install`**:`cargo tauri ohos build/run` 内部会跑 ohpm 同步依赖(受 `oh-package-lock.json5` 约束,安全)。手动 `ohpm install --all` 会删掉 lock 文件、清空 `oh_modules/@tauri/` junction、误删本地包目录,导致 hvigorw 00304056 / 00625003。详见 memory `ohos-arkts-rebuild-flow`。

## init 后补充步骤(每次 `tauri ohos init` 后必须执行)

`tauri ohos init` 重新生成 `gen/ohos/` 后,以下内容会丢失或缺失,需要手动补充:
Expand Down Expand Up @@ -233,69 +232,33 @@ cp ${PROJECT_ROOT}/tauri/.claude/skills/ohos-build/templates/testtrayability/Tes
3. **hdc 路径转义** — Git Bash 把 `/data/...` 转为 Windows 路径。设备路径命令用 `hdc shell "cat ..."` 加引号。
4. **签名由 hvigorw 完成** — build-profile.json5 中配置了含 system_basic 权限的证书,hvigorw assembleHap 自动签名。无需手动签名。
5. **HAR 缓存** — 修改了 openharmony-ability 后必须重建 HAR。`run-tests.sh` Step 0 自动检测并处理。
6. **tauriPlugin** — hvigorfile.ts 中的 tauriPlugin 需要 TCP 回调 tauri CLI,独立构建时必须禁用。`cargo tauri ohos build/run` 通过 `TAURI_OHOS_SKIP_DEVECO_SCRIPT=1` 环境变量自动处理(非 sed 改文件)
6. **tauriPlugin** — hvigorfile.ts 中的 tauriPlugin 需要 TCP 回调 tauri CLI,独立构建时必须禁用。`build-ohos.sh` 和 `run-tests.sh` 自动处理
7. **tauri-cli 修改后必须重装** — 如果修改了 `crates/tauri-cli/` 下的 Rust 源码(非模板文件),必须重新安装 tauri-cli,否则 `cargo` 使用缓存的旧二进制,改动不生效:
```bash
cargo install --path crates/tauri-cli --locked
```
仅修改模板文件(`templates/` 下)不需要重装,`cargo tauri ohos build/run` 的 `ensure_init` 会自动检测并重建 `gen/ohos/`。
8. **插件 dist-js 自动构建** — `prerequisites.sh` 每次执行 `pnpm build` 构建 `plugins-workspace/` 下所有插件的 `dist-js`,防止 git pull/rebase 后插件产物过期导致测试失败(如 notification 插件 `index.js` 过期)。
仅修改模板文件(`templates/` 下)不需要重装,`build-ohos.sh` 会自动检测模板 mtime 并重建 `gen/ohos/`。
8. **插件 dist-js 自动构建** — `build-ohos.sh` Step 2.5 每次执行 `pnpm build` 构建 `plugins-workspace/` 下所有插件的 `dist-js`,防止 git pull/rebase 后插件产物过期导致测试失败(如 notification 插件 `index.js` 过期)。
9. **cargo-mobile2 仓库** — `tauri-cli` 依赖 `cargo-mobile2`(位于 `${PROJECT_ROOT}/cargo-mobile2`)。如果 tauri-cli 编译报 `unresolved import cargo_mobile2::open_harmony::app`,说明 cargo-mobile2 需要 rebase 到 upstream/ohdev。
10. **PR #59 双 Entry 模块** — 项目结构从单一 `entry/` 拆分为 `entry_desktop/` + `entry_mobile/`。`OHOS_DEVICE_TYPE` 决定激活哪个模块(`active_entry_module()` 返回 `entry_{form}`)。`cargo tauri ohos build --app` 同时构建两个模块。
11. **@tauri ohpm junctions** — `ohpm install` 会删除 `@tauri/*` 本地链接(`oh_modules/@tauri/`)。每次 `ohpm install` 后需要手动重建 junction:
```bash
cd gen/ohos && mkdir -p oh_modules/@tauri && for pkg in app notification global-shortcut dialog; do
src=""; case $pkg in app) src="tauri" ;; *) src="$pkg" ;; esac
[ -d "$src" ] && cmd //c "mklink /J \"oh_modules\\@tauri\\$pkg\" \"$(pwd -W)\\$src\"" 2>/dev/null
done
```

## 设备日志与故障诊断

### 标准 hilog 捕获流程(调试任何窗口/IPC/能力不生效问题)

**核心原则:必须先开流式 grep 转存到本地文件,再启动 app,等测试跑完,最后读文件。**

严禁:
- ❌ 在控制台直接流式阅读(`hilog | grep` 挂在前台肉眼看)—— 行数多、易漏、无法回溯
- ❌ `nohup hdc shell hilog > /tmp/full.log` 全量落盘再 grep —— 每秒千行,文件几十万行,既慢又浪费
- ❌ 只用 `hilog -x`(dump 历史)排查已发生事件 —— 缓冲区会轮转,早于几分钟前的日志已被冲掉
- ❌ 不关流量控制就开流 —— hilog 默认按进程/domain 流量控制(drop 策略),高量时段会静默丢弃日志行,导致捕获文件缺行(现象:明明设备打了某条日志,你的文件里却没有)。**必须先 `hilog -Q pidoff` + `hilog -Q domainoff` 关闭流量控制再开流**

正确流程(5 步):

```bash
# 0. 【必须先做】关闭 hilog 流量控制,否则高量时段日志被静默丢弃导致捕获缺行
hdc shell "hilog -Q pidoff"
hdc shell "hilog -Q domainoff"

# 1. 单开一个控制台,启动 hilog 流式 grep 转存到本地文件
# 关键字: tauritest (Rust log, domain A00000) + 1999 (ArkTS hilog, domain A01999)
# 按需加业务关键字: setFullscreen / WindowManager / resizeWindow 等
nohup hdc shell "hilog 2>/dev/null | grep -aE 'tauritest|1999'" > /tmp/hilog-capture.log 2>&1 &

# 2. 重启 app 触发待调试行为(force-stop → aa start)
hdc shell "aa force-stop com.tauri.api"
sleep 2
hdc shell "aa start -b com.tauri.api -a EntryAbility"

# 3. 等待测试执行完毕(autotest 全量约 60s,单用例约 2-5s,按需调整)
sleep 60

# 4. 阅读转存文件(停止流后读,或读过程中 tail)
pkill -f "hilog 2>/dev/null" # 停止后台流
grep -aE 'setFullscreen|setWindowLayoutFullScreen' /tmp/hilog-capture.log
```

**关键字说明:**
- `tauritest` — Rust 侧 `log::info!` / `crate::info!`(domain `A00000`,tag `tauritest`)。仅当 openharmony-ability 的 `log` feature 启用时输出;examples/api 默认**未启用**,Rust 日志为 no-op。
- `1999` — ArkTS `hilog`(domain `A01999`,tag 如 `WindowManager` / `ArkHelper`)。ArkTS hilog 无条件输出,是排查"ArkTS 方法是否被调用"的首要证据。
- `-a` 强制文本匹配(避免二进制行被过滤);设备端管道 grep 完才传回,只传匹配行。

**用此流程区分"flag 翻转但效果不生效"类问题:**
- ArkTS 方法 `ENTER` 日志**有** → NAPI 调用链通,问题在 ArkTS/系统层
- ArkTS 方法 `ENTER` 日志**无** → NAPI 断裂(Rust `let _ =` 吞错),问题在 Rust→ArkTS 桥接
# 实时日志(持续输出,Ctrl+C 停止)
hdc shell "hilog | grep -i 关键词"

### 其他诊断命令

```bash

# 缓冲区历史日志(非阻塞,-x dump 历史)
# 注意:缓冲区会轮转,仅适合查近几分钟事件;查更早用下方归档文件
hdc shell "hilog -x 2>/dev/null | grep -aE '关键字'"
# 缓冲区日志(非阻塞,-z 取最后 N 行)
# Rust log::info/error 的 domain 为 A00000,tag 为 tauritest
hdc shell "hilog -x" | grep "com.tauri"

# hilog 归档文件(选 hilog.*.gz,忽略 hilog_kmsg.*.gz)
hdc shell "ls -lt /data/log/hilog/ | grep -v hilog_kmsg | grep hilog | head -3"
Expand All @@ -318,3 +281,16 @@ hdc shell "cat /data/app/el2/100/base/com.tauri.api/cache/panic.log"
|------|------|------|
| App 内部(Rust 代码中) | `/data/storage/el2/base/cache/` | 应用沙箱路径 |
| 外部(hdc 访问) | `/data/app/el2/100/base/com.tauri.api/cache/` | 实际物理路径 |

## 手动 HAR 重建(仅在脚本自动检测失效时需要)

```bash
cd ${PROJECT_ROOT}/openharmony-ability
source ${PROJECT_ROOT}/tauri/.claude/skills/ohos-build/scripts/env.sh
ohrs build --arch arm64 # 末尾 panic 是已知问题,不影响
bash scripts/pack.sh
tar -czf ability.har package
cd ${PROJECT_ROOT}/tauri/examples/api/src-tauri/gen/ohos
ohpm install --all
# 重建 @tauri junctions(见注意事项 #11)
```
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
- [ ] G7: OHOS 窗口尺寸 outer/inner 语义对齐 — `win.resize(w,h)` 设的是 **outer** 尺寸(ArkTS `WindowManager.resizeWindow` 不补偿标题栏 inset)。若 `inner_size()` 返回 content_rect(inner,比 outer 小装饰 inset),而 `set_inner_size()` 直接把该值传给 `resize_window`,则 save→restore 循环会按 inset 量级逐次缩小窗口 → 🟡。**检查方法**:确认 `inner_size()` 与 `set_inner_size()` 对 outer/inner 口径一致(要么都 outer 要么都 inner+补偿),注释说明差异
- [ ] G8: OHOS 窗口可见性 restore+show 配对 — MINIMIZE 状态的窗口 `showWindow()` 不会自动 restore 到 FLOATING,需先 `restore()`/`recover()`。`set_visible(true)` 若只调 `show_window` 不调 `restore_window`,则 minimize(或 `set_visible(false)`→hide_window→minimize)后无法恢复 → 🟡。**检查方法**:对照 `set_visible(true)` 实现确认有 restore 调用,或 ArkTS `showWindowMethod` 对 MINIMIZE 状态先 recover
- [ ] G9: OHOS 状态镜像 (AtomicBool) 需事件回灌 — 新增 tao 侧 `visible`/`fullscreen`/`maximized`/`minimized` 等 AtomicBool 镜像时,必须同时确认 EventLoop 有对应的 MainEvent 回灌(OHOS 系统发起的状态变更),否则 OS 标题栏操作后镜像 stale,`is_visible()` 等返回错误值 → 🔵。若为有意推迟(注释标注 future extension),至少在字段注释里写明"未回灌,OS 发起变更会 stale"。注意保持一致性:同类 getter 不能一部分查镜像、一部分查真实 OS 状态(如 `is_minimized` 查真实而 `is_visible` 查镜像)
- [ ] G10: OHOS no-op / 降级实现需可观测 — OHOS 上大量 API 是 no-op 或降级实现(如 `drag_window` 主窗口无 FloatPage 标题栏路径、`set_always_on_bottom` 空体、`request_redraw` no-op、`drag_resize_window` 退化为 enableDrag)。此类实现静默返回 `Ok(())` 或空 `{}` 时,调用方无法区分"API 已生效"与"此窗口类型/设备上 no-op",造成可观测性盲区。**要求**:至少 `log::debug!`(或 `log::warn!` 对有副作用的降级)标注生效与否,并在注释说明在哪些窗口类型(主 UIAbility vs Float 子窗口)/设备形态(PC freeform vs 手机)上为 no-op。来源:本次检视 F6(`drag_window` 主窗口 `Ok(())` 无日志)→ 🔵

## H — 仓库级规范

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/tauri-ohos-init/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ arkts-helper 提供 ArkTS/ArkUI 文档检索和华为官方 AI 问答能力。
3. 如未配置:
```bash
cd <项目根目录>
git clone https://github.com/LongLiveY96/arkts-helper-mcp.git
git clone
cd arkts-helper-mcp
npm install
npm run build
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
},
{
"name": "ohos.permission.SET_WINDOW_TRANSPARENT"
},
{
"name": "ohos.permission.WINDOW_TOPMOST"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@
},
{
"name": "ohos.permission.SET_WINDOW_TRANSPARENT"
},
{
"name": "ohos.permission.WINDOW_TOPMOST"
}
]
}
Expand Down
24 changes: 24 additions & 0 deletions crates/tauri-runtime-wry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4505,6 +4505,30 @@ fn handle_event_loop<T: UserEvent>(
);
}
}

// 回灌系统窗口状态到 tao 镜像位(问题五 5.3)。
// windowStatusChange 事件经 notify_window_status NAPI 入队,这里 drain 后用
// 真实 OHOS windowId 路由到对应 tao Window,调 apply_window_status 更新
// visible/fullscreen 镜像。路由模式与上方 drain_pending_window_closes 一致
// (不依赖 tao ZST WindowId,多窗口正确)。详见 doc/OHOS窗口遗留问题.md(问题五 5.3)。
let pending_status = tao::platform::ohos::ability::drain_pending_window_status();
Comment thread
ddxwzc-boop marked this conversation as resolved.
for (ohos_win_id, status) in pending_status {
let applied = windows.0.borrow().iter().find_map(|(_id, wrapper)| {
let w = wrapper.inner.as_ref()?;
if w.window_id() == Some(ohos_win_id as i64) {
w.apply_window_status(status);
Some(())
} else {
None
}
});
if applied.is_none() {
log::debug!(
"[wry] OHOS pending status: no matching Tauri window for OHOS window ID {} (status={})",
ohos_win_id, status
);
}
}
}

match event {
Expand Down
Loading
Loading