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:
1 parent
9e25db77cc
commit
ed2ae10320
14 files changed
+416
-12
No files matched your search
@@ -0,0 +1,117 @@
|
||||
/* 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;
|
||||
}
|
||||
Reference in new issue
Block a user