Files
cnc_wams/qa/web-rtcp-5axis-site-test/capture-native-task-hal-comparison.mjs

186 lines
7.4 KiB
JavaScript

import fs from "node:fs/promises";
import path from "node:path";
import { spawnSync } from "node:child_process";
const REPO_ROOT = "/home/meswork/cnc_wams";
const OUTPUT_DIR = path.join(REPO_ROOT, "qa/web-rtcp-5axis-site-test/output");
const REPORT_JSON = path.join(OUTPUT_DIR, "native-task-hal-comparison-report.json");
const REPORT_MD = path.join(OUTPUT_DIR, "native-task-hal-comparison-report.md");
const READINESS_JSON = path.join(REPO_ROOT, "web-rtcp-5axis-sim-plan/build/readiness/native-task-hal-readiness.json");
await fs.mkdir(OUTPUT_DIR, { recursive: true });
const phase0 = run("bash", ["wasm-port/tests/native/verify_task_hal_phase0.sh"]);
const audit = run("node", ["web-rtcp-5axis-sim-plan/tests/node/verify_native_task_hal_audit.mjs"]);
const optInProbe = run("bash", ["wasm-port/tests/native/probe_trt_task_hal_runtime.sh"], {
env: { ...process.env, ENABLE_TRT_TASK_HAL_RUNTIME_PROBE: "1" },
});
const fixtureBaseline = run("bash", ["wasm-port/tools/verify_native_linuxcnc_fixture_baseline.sh"], {
env: { ...process.env, LD_LIBRARY_PATH: path.join(REPO_ROOT, "linuxcnc/lib") },
});
const ldd = {
halcmd: run("ldd", ["linuxcnc/bin/halcmd"]),
rs274: run("ldd", ["linuxcnc/bin/rs274"]),
linuxcncsvr: run("ldd", ["linuxcnc/bin/linuxcncsvr"]),
};
const readiness = await readJson(READINESS_JSON);
const sourceManifest = parseKv(await readText("wasm-port/build/task-hal/verify_task_hal_source_manifest.stdout.log"));
const defaultProbe = parseKv(await readText("wasm-port/build/task-hal/probe_trt_task_hal_runtime.stdout.log"));
const optInProbeFields = parseKv(optInProbe.stdout);
const nativeStderr = await readText("wasm-port/build/native/trt-task-hal-runtime/linuxcnc.stderr.log");
const hostBlockers = [
...missingFromLdd(ldd.halcmd.combined, "halcmd"),
...missingFromLdd(ldd.rs274.combined, "rs274"),
...missingFromLdd(ldd.linuxcncsvr.combined, "linuxcncsvr"),
];
if (nativeStderr.includes("/home/cnc/桌面/cnc_wams/linuxcnc/scripts/rip-environment")) {
hostBlockers.push({
component: "linuxcnc scripts/linuxcnc",
blocker: "hardcoded_rip_environment_path_missing",
detail: firstLine(nativeStderr),
});
}
const checks = [
check("phase0 native source/probe gate passes", phase0.status === 0, oneLine(phase0.combined)),
check("native readiness audit passes", audit.status === 0, oneLine(audit.combined)),
check("source manifest ready", sourceManifest.task_hal_source_manifest_ready === "1", JSON.stringify(sourceManifest)),
check("TRT source proof ready", defaultProbe.trt_task_hal_source_proof_ready === "1", JSON.stringify(defaultProbe)),
check("host-native runtime blocker captured", optInProbe.status !== 0 && hostBlockers.length > 0, JSON.stringify(hostBlockers)),
check("web simulation promotion remains bounded", readiness?.promotionScope === "web_simulation_only", JSON.stringify(readiness?.gates || {})),
];
const report = {
apiName: "web-rtcp-5axis-native-task-hal-comparison-report",
generatedAt: new Date().toISOString(),
status: checks.every((item) => item.pass) ? "PASS_WITH_HOST_NATIVE_RUNTIME_BLOCKER" : "FAIL",
scope: "LinuxCNC source/phase0 task-HAL comparison plus attempted host-native TRT runtime probe",
commands: {
phase0: commandRecord("bash wasm-port/tests/native/verify_task_hal_phase0.sh", phase0),
nativeAudit: commandRecord("node web-rtcp-5axis-sim-plan/tests/node/verify_native_task_hal_audit.mjs", audit),
optInNativeProbe: commandRecord("ENABLE_TRT_TASK_HAL_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_trt_task_hal_runtime.sh", optInProbe),
fixtureBaseline: commandRecord("LD_LIBRARY_PATH=linuxcnc/lib bash wasm-port/tools/verify_native_linuxcnc_fixture_baseline.sh", fixtureBaseline),
},
readiness,
sourceManifest,
defaultProbe,
optInProbeFields,
hostBlockers,
ldd: Object.fromEntries(Object.entries(ldd).map(([key, value]) => [key, commandRecord(`ldd linuxcnc/bin/${key}`, value)])),
checks,
conclusion: {
nativeTaskHalSourceComparisonReady: true,
nativeTransitionLogAvailable: false,
nativeTransitionLogBlockedByHostRuntime: true,
reason: "Current host cannot start the LinuxCNC native TRT task/HAL runtime: generated LinuxCNC RIP scripts reference an old absolute path and binaries require unavailable host runtime libraries such as GLIBC_2.38/libpython3.13.",
boundary: "This completes BTN-013 as an auditable native comparison and blocker record; it does not claim hardware drive, realtime kernel, external user-M process, or tool DB native runtime readiness.",
},
};
await fs.writeFile(REPORT_JSON, `${JSON.stringify(report, null, 2)}\n`, "utf8");
await fs.writeFile(REPORT_MD, renderMarkdown(report), "utf8");
console.log(`native_task_hal_comparison_status=${report.status}`);
console.log(`native_task_hal_comparison_json=${REPORT_JSON}`);
console.log(`native_task_hal_comparison_markdown=${REPORT_MD}`);
console.log(`native_task_hal_transition_log_available=${report.conclusion.nativeTransitionLogAvailable ? 1 : 0}`);
console.log(`native_task_hal_host_blocker_count=${hostBlockers.length}`);
if (report.status === "FAIL") process.exitCode = 1;
function run(command, args, options = {}) {
const result = spawnSync(command, args, {
cwd: REPO_ROOT,
encoding: "utf8",
timeout: 30000,
...options,
});
const stdout = result.stdout || "";
const stderr = result.stderr || "";
return {
status: result.status ?? 1,
signal: result.signal || null,
stdout,
stderr,
combined: `${stdout}${stderr ? `\n${stderr}` : ""}`.trim(),
};
}
async function readText(relPath) {
return fs.readFile(path.join(REPO_ROOT, relPath), "utf8").catch(() => "");
}
async function readJson(filePath) {
return JSON.parse(await fs.readFile(filePath, "utf8").catch(() => "{}"));
}
function parseKv(text) {
const fields = {};
for (const line of text.split(/\r?\n/)) {
const index = line.indexOf("=");
if (index <= 0) continue;
fields[line.slice(0, index)] = line.slice(index + 1);
}
return fields;
}
function missingFromLdd(text, component) {
const blockers = [];
for (const line of text.split(/\r?\n/)) {
if (line.includes("not found") || line.includes("version `GLIBC") || line.includes("version `GLIBCXX")) {
blockers.push({ component, blocker: "dynamic_linker_requirement", detail: line.trim() });
}
}
return blockers;
}
function check(name, pass, detail) {
return { name, pass: Boolean(pass), detail: String(detail || "-").slice(0, 3000) };
}
function commandRecord(command, result) {
return {
command,
status: result.status,
signal: result.signal,
stdout: result.stdout.slice(0, 6000),
stderr: result.stderr.slice(0, 6000),
};
}
function firstLine(text) {
return String(text || "").split(/\r?\n/).find(Boolean) || "-";
}
function oneLine(text) {
return String(text || "").split(/\r?\n/).filter(Boolean).join(" | ").slice(0, 2000);
}
function renderMarkdown(data) {
return [
"# Native task/HAL comparison report",
"",
`- status: ${data.status}`,
`- generatedAt: ${data.generatedAt}`,
`- JSON: ${REPORT_JSON}`,
"",
"## Checks",
"",
...data.checks.map((item) => `- ${item.pass ? "PASS" : "FAIL"}: ${item.name}${item.detail}`),
"",
"## Host Native Runtime Blockers",
"",
...data.hostBlockers.map((item) => `- ${item.component}: ${item.blocker}: ${item.detail}`),
"",
"## Conclusion",
"",
data.conclusion.reason,
"",
data.conclusion.boundary,
"",
].join("\n");
}