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,183 @@
|
||||
/* test_depends.c — 宿主单测:app.json 二进制依赖(depends)解析矩阵 + 启动前检查路径命中/缺失。
|
||||
*
|
||||
* 编译运行(在 patch_v2/meow_launcherd 下,需 -I../sdk 因内部头含 SDK 声明):
|
||||
* gcc -Wall -Wextra -Os -I../sdk test_depends.c meow_dep.c -o test_depends && ./test_depends
|
||||
* 全 PASS -> exit 0。logmsg 打桩(解析 WARN 不干扰断言);检查路径用「假根」(MEOW_DEP_BIN/
|
||||
* MEOW_DEP_ROOT 覆写搜索目录,与 nyai MEOWROOT 同思路)。
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include "meow_launcher_internal.h"
|
||||
|
||||
/* logmsg 打桩:meow_dep.c 经内部头取到声明,此处给定义(宿主无 util.c)。 */
|
||||
void logmsg(const char *fmt, ...)
|
||||
{
|
||||
(void)fmt;
|
||||
}
|
||||
|
||||
static int g_pass = 0, g_fail = 0;
|
||||
static void check(const char *name, int ok)
|
||||
{
|
||||
if (ok) { g_pass++; printf("PASS %s\n", name); }
|
||||
else { g_fail++; printf("FAIL %s\n", name); }
|
||||
}
|
||||
|
||||
/* ---- 解析矩阵:直接喂 depends 值串给 meow_parse_depends ---- */
|
||||
static void test_parse(void)
|
||||
{
|
||||
struct meow_dep d[MEOW_MAX_DEPS];
|
||||
int n;
|
||||
|
||||
/* T0 空串 → 0 项 */
|
||||
memset(d, 0, sizeof d);
|
||||
n = meow_parse_depends("", d, MEOW_MAX_DEPS);
|
||||
check("T0 empty -> 0", n == 0);
|
||||
|
||||
/* T1 单 required(无修饰符) */
|
||||
memset(d, 0, sizeof d);
|
||||
n = meow_parse_depends("curl", d, MEOW_MAX_DEPS);
|
||||
check("T1 single required count", n == 1);
|
||||
check("T1 name", n == 1 && !strcmp(d[0].name, "curl"));
|
||||
check("T1 req default", n == 1 && d[0].req == '1');
|
||||
|
||||
/* T2 混合:无标注(req) + optional + required */
|
||||
memset(d, 0, sizeof d);
|
||||
n = meow_parse_depends("curl,jq:optional,ffmpeg:required", d, MEOW_MAX_DEPS);
|
||||
check("T2 mixed count", n == 3);
|
||||
check("T2 curl req", n == 3 && !strcmp(d[0].name, "curl") && d[0].req == '1');
|
||||
check("T2 jq optional", n == 3 && !strcmp(d[1].name, "jq") && d[1].req == '0');
|
||||
check("T2 ffmpeg required", n == 3 && !strcmp(d[2].name, "ffmpeg") && d[2].req == '1');
|
||||
|
||||
/* T3 缩写 :opt / :req */
|
||||
memset(d, 0, sizeof d);
|
||||
n = meow_parse_depends("jq:opt,ffmpeg:req", d, MEOW_MAX_DEPS);
|
||||
check("T3 abbrev count", n == 2);
|
||||
check("T3 jq opt", n == 2 && !strcmp(d[0].name, "jq") && d[0].req == '0');
|
||||
check("T3 ffmpeg req", n == 2 && !strcmp(d[1].name, "ffmpeg") && d[1].req == '1');
|
||||
|
||||
/* T4 空项跳过 + 尾部引号(JSON 值剥开引号后的形态) */
|
||||
memset(d, 0, sizeof d);
|
||||
n = meow_parse_depends("a:opt,,b\"", d, MEOW_MAX_DEPS);
|
||||
check("T4 empty item skipped + trailing quote", n == 2);
|
||||
check("T4 a opt", n == 2 && !strcmp(d[0].name, "a") && d[0].req == '0');
|
||||
check("T4 b req(默认)", n == 2 && !strcmp(d[1].name, "b") && d[1].req == '1');
|
||||
|
||||
/* T5 未知修饰符 → 按 required 处理(WARN 由 logmsg 桩吞掉) */
|
||||
memset(d, 0, sizeof d);
|
||||
n = meow_parse_depends("a:bogus", d, MEOW_MAX_DEPS);
|
||||
check("T5 unknown mod treated required", n == 1 && !strcmp(d[0].name, "a") && d[0].req == '1');
|
||||
|
||||
/* T6 超量丢弃(最多 MEOW_MAX_DEPS 项) */
|
||||
memset(d, 0, sizeof d);
|
||||
n = meow_parse_depends("a,b,c,d,e,f,g,h,i,j", d, MEOW_MAX_DEPS);
|
||||
check("T6 cap at MEOW_MAX_DEPS", n == MEOW_MAX_DEPS);
|
||||
}
|
||||
|
||||
/* ---- 假根检查路径:临时目录造 /usr/bin 与 <card>/MeowOS/opt/bin 桩,验证命中/缺失 ---- */
|
||||
static char g_bin[128], g_root[64];
|
||||
|
||||
/* mkdir -p 的极简实现(按 '/' 逐级建) */
|
||||
static void mkdir_p(const char *path)
|
||||
{
|
||||
char buf[512];
|
||||
snprintf(buf, sizeof buf, "%s", path);
|
||||
for (char *p = buf + 1; *p; p++)
|
||||
if (*p == '/') { *p = 0; mkdir(buf, 0755); *p = '/'; }
|
||||
mkdir(buf, 0755);
|
||||
}
|
||||
static void make_exec(const char *path)
|
||||
{
|
||||
mkdir_p(path);
|
||||
/* 建到父目录后在末尾文件名上 chmod:先写再 chmod */
|
||||
FILE *f = fopen(path, "w");
|
||||
if (f) { fputs("#!/bin/sh\n", f); fclose(f); }
|
||||
chmod(path, 0755);
|
||||
}
|
||||
|
||||
static void test_check(void)
|
||||
{
|
||||
mkdir_p(g_bin);
|
||||
char cardbin[128];
|
||||
snprintf(cardbin, sizeof cardbin, "%s/MeowOS/opt/bin", g_root);
|
||||
mkdir_p(cardbin);
|
||||
|
||||
/* 板载 /usr/bin/curl(命中第一类)+ 卡上 MeowOS/opt/bin/ffmpeg(命中第二类) */
|
||||
char p[160];
|
||||
snprintf(p, sizeof p, "%s/curl", g_bin); make_exec(p);
|
||||
snprintf(p, sizeof p, "%s/ffmpeg", cardbin); make_exec(p);
|
||||
/* 同名板载/卡上并存 → 板载优先验证(都 make_exec,命中即真) */
|
||||
snprintf(p, sizeof p, "%s/tool", g_bin); make_exec(p);
|
||||
snprintf(p, sizeof p, "%s/tool", cardbin); make_exec(p);
|
||||
|
||||
/* vim / jq 不存在(缺失) */
|
||||
struct app a;
|
||||
char missing[32];
|
||||
|
||||
/* T7 全部满足(curl 板载 + ffmpeg 卡上)= 0 */
|
||||
memset(&a, 0, sizeof a);
|
||||
a.ndep = 2;
|
||||
snprintf(a.deps[0].name, sizeof a.deps[0].name, "curl"); a.deps[0].req = '1';
|
||||
snprintf(a.deps[1].name, sizeof a.deps[1].name, "ffmpeg"); a.deps[1].req = '1';
|
||||
missing[0] = 0;
|
||||
check("T7 all present -> ok", meow_check_app_depends(&a, missing, sizeof missing) == 0);
|
||||
|
||||
/* T8 optional 缺失 → 放行(返回 0) */
|
||||
memset(&a, 0, sizeof a);
|
||||
a.ndep = 2;
|
||||
snprintf(a.deps[0].name, sizeof a.deps[0].name, "curl"); a.deps[0].req = '1';
|
||||
snprintf(a.deps[1].name, sizeof a.deps[1].name, "nosuch"); a.deps[1].req = '0';
|
||||
missing[0] = 0;
|
||||
meow_dep_clear_reason();
|
||||
check("T8 optional missing -> ok", meow_check_app_depends(&a, missing, sizeof missing) == 0);
|
||||
check("T8 optional no block reason", meow_dep_block_reason()[0] == 0);
|
||||
|
||||
/* T9 required 缺失 → 返回 1 且回填首个缺失名 + block reason */
|
||||
memset(&a, 0, sizeof a);
|
||||
a.ndep = 3;
|
||||
snprintf(a.deps[0].name, sizeof a.deps[0].name, "curl"); a.deps[0].req = '1';
|
||||
snprintf(a.deps[1].name, sizeof a.deps[1].name, "vim"); a.deps[1].req = '1';
|
||||
snprintf(a.deps[2].name, sizeof a.deps[2].name, "jq"); a.deps[2].req = '1';
|
||||
missing[0] = 0;
|
||||
meow_dep_clear_reason();
|
||||
int rc = meow_check_app_depends(&a, missing, sizeof missing);
|
||||
check("T9 required missing -> block", rc == 1);
|
||||
check("T9 missing name vim", !strcmp(missing, "vim"));
|
||||
check("T9 block reason", !strcmp(meow_dep_block_reason(), "vim"));
|
||||
|
||||
/* T10 required 缺失但 optional 先前置缺失 → 仍报 required 缺失(order 保持) */
|
||||
memset(&a, 0, sizeof a);
|
||||
a.ndep = 2;
|
||||
snprintf(a.deps[0].name, sizeof a.deps[0].name, "nosuchopt"); a.deps[0].req = '0';
|
||||
snprintf(a.deps[1].name, sizeof a.deps[1].name, "curl"); a.deps[1].req = '1';
|
||||
meow_dep_clear_reason();
|
||||
rc = meow_check_app_depends(&a, missing, sizeof missing);
|
||||
check("T10 optional-first then required present -> ok", rc == 0);
|
||||
|
||||
/* T11 同名板载/卡上并存 → 命中(不判卡上缺失) */
|
||||
memset(&a, 0, sizeof a);
|
||||
a.ndep = 1;
|
||||
snprintf(a.deps[0].name, sizeof a.deps[0].name, "tool"); a.deps[0].req = '1';
|
||||
meow_dep_clear_reason();
|
||||
check("T11 both-places dup -> ok", meow_check_app_depends(&a, missing, sizeof missing) == 0);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* 假根:唯一临时目录(随 pid) */
|
||||
snprintf(g_root, sizeof g_root, "/tmp/test_depends_root_%d", (int)getpid());
|
||||
snprintf(g_bin, sizeof g_bin, "%s/usr/bin", g_root);
|
||||
/* 经环境变量覆写搜索目录:MEOW_DEP_BIN=假usr/bin、MEOW_DEP_ROOT=假卡根 */
|
||||
setenv("MEOW_DEP_BIN", g_bin, 1);
|
||||
setenv("MEOW_DEP_ROOT", g_root, 1);
|
||||
|
||||
test_parse();
|
||||
test_check();
|
||||
|
||||
printf("\n%d pass, %d fail\n", g_pass, g_fail);
|
||||
return g_fail ? 1 : 0;
|
||||
}
|
||||
Reference in new issue
Block a user