Files
MeowOS ed2ae10320 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
只读,留给主会话打包固件)
2026-09-01 18:28:07 +08:00

118 lines
4.8 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* meow_dep.c — app.json 二进制依赖声明(depends=<name>[:<mod>][,...])解析与启动前检查。
*
* 背景(用户定案):App 某些复杂功能若依赖现成 Linux 二进制程序(板载 /usr/bin 或卡上
* MeowOS/opt/bin)能效更好,故在 app.json 声明二进制依赖;launcher 在启动 App 前检查
* required 依赖是否都存在,缺 required 则不启动(toast「缺依赖: <name>」+ 日志);
* optional 缺失仅 WARN 放行。检查只在启动时做(不做周期复查)。
*
* 与 meow_parse_app_json 的 line 解析同风格:取值串后逐项切分(不引 JSON 解析器)。
* 本文件独立成 TU,由 build_launcher.sh 编入;宿主测试用「假根(env 覆写搜索目录)」验证。 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "meow_launcher_internal.h"
/* 最近一次因缺 required 依赖而阻断启动的首个缺失名(供 menu toast「缺依赖: <name>」)。
* 每次 launch_app_chain 入口清空(meow_dep_clear_reason);无缺失则 ""。 */
static char g_dep_block_reason[32] = "";
void meow_dep_clear_reason(void)
{
g_dep_block_reason[0] = 0;
}
const char *meow_dep_block_reason(void)
{
return g_dep_block_reason;
}
/* 二进制搜索目录(真机默认):板载 /usr/bin ← 卡上 <卡根>/MeowOS/opt/bin。
* 宿主测试经 MEOW_DEP_BIN / MEOW_DEP_ROOT 覆写为假根(与 nyai MEOWROOT 同思路)。 */
static const char *dep_bin_root(void)
{
const char *e = getenv("MEOW_DEP_BIN");
return (e && e[0]) ? e : "/usr/bin";
}
static const char *dep_card_root(void)
{
const char *e = getenv("MEOW_DEP_ROOT");
return (e && e[0]) ? e : "/usr/data/mnt/sd_0";
}
/* 解析 depends 值串 → 逐个填充 deps[](最多 max 项,超量丢弃;空项/纯空白丢弃)。
* <name>[:<mod>];<mod> 缺省 required;:opt/:req 等价 optional/required;
* 未知修饰符按 required 处理并 logmsg WARN。返回写入项数。 */
int meow_parse_depends(const char *val, struct meow_dep *deps, int max)
{
if (!val || !deps || max <= 0) return 0;
int n = 0;
const char *p = val;
while (*p && n < max) {
while (*p && (*p == ' ' || *p == '\t' || *p == ',')) p++;
if (!*p || *p == '"') break; /* 结束(含 JSON 值尾引号) */
const char *start = p; /* 名称起点 */
while (*p && *p != ':' && *p != ',' && *p != '"' && *p != ' ' && *p != '\t') p++;
int nl = (int)(p - start);
if (nl > 0) {
if (nl > 31) nl = 31;
char nm[32];
memcpy(nm, start, (size_t)nl);
nm[nl] = 0;
int req = 1; /* 默认 required */
if (*p == ':') { /* 修饰符 */
const char *ms = p + 1;
while (*ms == ' ' || *ms == '\t') ms++;
const char *m = ms;
while (*m && *m != ',' && *m != '"' && *m != ' ' && *m != '\t') m++;
int ml = (int)(m - ms);
if (ml == 8 && !strncmp(ms, "optional", 8)) req = 0;
else if (ml == 3 && !strncmp(ms, "opt", 3)) req = 0;
else if (ml == 8 && !strncmp(ms, "required", 8)) req = 1;
else if (ml == 3 && !strncmp(ms, "req", 3)) req = 1;
else logmsg("WARN: depends '%s' unknown mod '%.*s' (treated required)", nm, ml, ms);
p = m;
}
snprintf(deps[n].name, sizeof deps[n].name, "%s", nm);
deps[n].req = (char)(req ? '1' : '0');
n++;
}
if (*p == ',') p++; /* 消费分隔符 */
}
return n;
}
/* 依赖二进制存在性:按序 <board>/<name> → <card>/MeowOS/opt/bin/<name>(X_OK)。返回 1=存在。 */
static int dep_bin_exists(const char *name)
{
if (!name || !name[0]) return 0;
char p[320];
snprintf(p, sizeof p, "%s/%s", dep_bin_root(), name);
if (access(p, X_OK) == 0) return 1;
snprintf(p, sizeof p, "%s/MeowOS/opt/bin/%s", dep_card_root(), name);
if (access(p, X_OK) == 0) return 1;
return 0;
}
/* 启动前检查依赖。returns: 0=全部满足 / 1=缺 required(missing 回填首个缺失名,可 NULL)。 */
int meow_check_app_depends(const struct app *a, char *missing, int mlen)
{
if (!a) return 0;
for (int i = 0; i < a->ndep; i++) {
if (dep_bin_exists(a->deps[i].name)) continue;
if (a->deps[i].req == '1') {
logmsg("launch blocked: missing required dep '%s'", a->deps[i].name);
if (missing && mlen > 0) snprintf(missing, (size_t)mlen, "%s", a->deps[i].name);
snprintf(g_dep_block_reason, sizeof g_dep_block_reason, "%s", a->deps[i].name);
return 1;
}
logmsg("WARN: optional dep '%s' missing, continuing", a->deps[i].name);
}
return 0;
}