app.json 二进制依赖 depends:启动前检查(launcher)+ 安装提示(nyai)

需求(用户定案):App 复杂功能若依赖现成 Linux 二进制(板载 /usr/bin 或卡上
MeowOS/opt/bin)能效更好,声明后系统启动 App 前检查依赖是否存在。

- depends 行式声明:depends=curl,jq:optional,ffmpeg:required(无标注默认
  required;也接受 :opt/:req;未知修饰符按 required 处理并 WARN;空项/超量丢弃)
- launch_app_chain 冷启动时按序查 /usr/bin/<name> 与 <卡>/MeowOS/opt/bin/<name>
  (X_OK,两类命中即满足);缺 required → 不启动(toast「缺依赖: <name>」+ 日志,
  沿用 CANNOT START 风格);optional 缺失 → 仅 WARN 放行;只在启动时查,不做周期复查
- nyai install 对包内 app.json depends 同查并提示(不阻断安装,缺 required 标注
  「安装后启动将失败」)
- 新增 meow_dep.c(独立 TU,解析/检查/块原因),宿主测试 test_depends.c
  解析矩阵 12 用例 + 假根检查路径 12 用例全 PASS;launcher/nyai mipsel 交叉
  -Wall -Wextra 0 warning;launcher 现有宿主测试回归全过
- 文档:APPS.md §2.1 字段表 + docs/MEOWOS_SPEC_REF.md §5.5 同步;hello app.json
  加 depends:curl:optional 示例

launcher md5 899ac22c10f596909d7f35ea685744de(fw_content.conf 已更新;/usr/bin
只读,留给主会话打包固件)
This commit is contained in:
MeowOS committed 2026-09-01 18:28:07 +08:00
1 parent 9e25db77cc
commit ed2ae10320
14 files changed
+416 -12

No files matched your search

+32
View File
@@ -807,6 +807,26 @@ void meow_parse_app_json(FILE *f, struct app *a)
tmp.nsvc++;
}
}
/* depends=<name>[:<mod>][,...]:二进制依赖声明(meow_dep.c 解析)。
* 与行式解析同风格(JSON "depends":"..." 或扁平 depends=... 均可);不引 JSON 解析器。
* <mod> 缺省 required;:opt/:req 等价 optional/required;未知修饰符按 required 处理并 WARN。 */
char *dp = strstr(buf, "depends");
if (dp) {
char *after = dp + 7; /* strlen("depends") */
while (*after == ' ' || *after == '\t') after++;
/* 紧跟 '=' / ':' / '"' 才算 depends 键(防 description 等文本误命中) */
if ((*after == '=' || *after == ':' || *after == '"') &&
tmp.ndep < MEOW_MAX_DEPS) {
char *vp = after;
while (*vp && *vp != ':' && *vp != '=') vp++; /* 跳到值分隔符 */
if (*vp == ':' || *vp == '=') vp++;
while (*vp == ' ' || *vp == '\t') vp++;
if (*vp == '"') vp++; /* 剥 JSON 值开引号 */
int got = meow_parse_depends(vp, tmp.deps + tmp.ndep,
MEOW_MAX_DEPS - tmp.ndep);
tmp.ndep += got;
}
}
}
strncpy(a->name, name, 16);
strncpy(a->exec, exec, 128);
@@ -822,6 +842,8 @@ void meow_parse_app_json(FILE *f, struct app *a)
if (tmp.nroute) memcpy(a->routes, tmp.routes, sizeof(a->routes[0]) * (size_t)tmp.nroute);
a->nsvc = tmp.nsvc;
if (tmp.nsvc) memcpy(a->svcs, tmp.svcs, sizeof(a->svcs[0]) * (size_t)tmp.nsvc);
a->ndep = tmp.ndep;
if (tmp.ndep) memcpy(a->deps, tmp.deps, sizeof(a->deps[0]) * (size_t)tmp.ndep);
}
@@ -1442,6 +1464,7 @@ static int launch_app_chain(struct app *a, const char *chain)
g_ams_pick_armed = 0;
}
if (!a->ok) { logmsg("launch blocked: exec not ok"); return -1; }
meow_dep_clear_reason(); /* 每次 launch 入口清 stale 依赖阻断原因(防上一 app 残留) */
/* M4 后台保活恢复:该 App 驻留后台(播放中经 nav-exit 转过)→ 原进程回前台,
* 不 fork/exec(进程仍存无法注入新 env:chain/picker 上下文忽略,记日志)。
@@ -1461,6 +1484,15 @@ static int launch_app_chain(struct app *a, const char *chain)
meow_nav_begin(a->id, NULL); /* 转后台时导航栈已清;回前台从裸态重建 */
}
if (!resumed) {
/* 二进制依赖启动前检查(app.json depends):required 缺失阻断启动,optional 缺失放行。
* 只在冷启动时做(不做周期复查);resumed 进程不重查(依赖无变化、无需再 fork)。 */
{
char dep_missing[32] = "";
if (meow_check_app_depends(a, dep_missing, sizeof dep_missing) != 0) {
logmsg("launch blocked: dep '%s' missing (CANNOT START: %s)", dep_missing, a->name);
return -1;
}
}
meow_nav_begin(a->id, chain); /* M3:冷启动/裸启动都以当前链深初始化导航栈(chain 空→depth 0) */
/* M2.12:启动前准备 MeowData/<pkg>/{data,cache}(幂等;卡不在/失败只 log 不崩)。