/* meow_dep.c — app.json 二进制依赖声明(depends=[:][,...])解析与启动前检查。 * * 背景(用户定案):App 某些复杂功能若依赖现成 Linux 二进制程序(板载 /usr/bin 或卡上 * MeowOS/opt/bin)能效更好,故在 app.json 声明二进制依赖;launcher 在启动 App 前检查 * required 依赖是否都存在,缺 required 则不启动(toast「缺依赖: 」+ 日志); * optional 缺失仅 WARN 放行。检查只在启动时做(不做周期复查)。 * * 与 meow_parse_app_json 的 line 解析同风格:取值串后逐项切分(不引 JSON 解析器)。 * 本文件独立成 TU,由 build_launcher.sh 编入;宿主测试用「假根(env 覆写搜索目录)」验证。 */ #include #include #include #include #include #include "meow_launcher_internal.h" /* 最近一次因缺 required 依赖而阻断启动的首个缺失名(供 menu toast「缺依赖: 」)。 * 每次 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 项,超量丢弃;空项/纯空白丢弃)。 * [:]; 缺省 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; } /* 依赖二进制存在性:按序 / → /MeowOS/opt/bin/(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; }