186 lines
7.4 KiB
JavaScript
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");
|
|
}
|