Files
scriptc/tests/harness/library-dispatch.test.ts
Chris Tate ee9f0d64eb feat: ship the self-hosted native scriptc CLI (#585)
* feat: ship the self-hosted native scriptc CLI

- Run the shared compiler and CLI natively, including library builds and compile-time evaluation.
- Install native platform packages and ship relocatable standalone distributions.
- Preserve program semantics and reuse validated build artifacts for fast rebuilds.

* fix: complete native CLI integration and validation

- Preserve library diagnostics and run recursive compiler work on the main stack.
- Restore packaged helper permissions and exercise native npm and Wasm library builds.
- Recover interrupted Sandbox status probes within the original command deadline.
2026-09-30 02:17:56 -05:00

51 lines
3.2 KiB
TypeScript

import { spawnSync } from "node:child_process";
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { WASI } from "node:wasi";
import { expect, test } from "vitest";
import { compileLibrary } from "@scriptc/compiler";
const fixture = join(import.meta.dirname, "../library-mode/wasm/dispatch.mjs");
const modes = ["native", "sanitized", "wasm-dev"] as const;
test.for(modes)("synchronous checked dispatch in a %s library matches Node", async (mode, context) => {
if (mode === "wasm-dev" ? spawnSync("zig", ["version"]).status !== 0 : process.platform !== "darwin" && process.platform !== "linux") context.skip();
const directory = await mkdtemp("/tmp/scriptc-library-dispatch-");
const oldTarget = process.env["SCRIPTC_TARGET"];
try {
if (mode === "wasm-dev") process.env["SCRIPTC_TARGET"] = "wasm32-wasi";
else delete process.env["SCRIPTC_TARGET"];
const profilePath = join(directory, "profile.json");
await writeFile(profilePath, JSON.stringify({
profile_format: 1, name: "sync-dispatch", entry: fixture, emission: "llvm",
optimization: mode === "wasm-dev" ? "dev" : "release",
abi: { prefix: "app_", init_symbol: "app_init", sink_register_symbol: "app_sink" },
exports: [{ export: "dispatch", symbol: "app_dispatch", params: [], returns: "f64" }],
}));
const result = await compileLibrary({ profilePath, outDir: directory, sanitize: mode === "sanitized" });
if (!result.ok) throw new Error(JSON.stringify(result.diagnostics));
const reference = spawnSync(process.execPath, ["--no-warnings", "--input-type=module", "-e", `import {dispatch} from ${JSON.stringify(fixture)}; console.log(dispatch());`], { encoding: "utf8" });
let actual: { stdout: string; stderr: string; status: number | null };
if (mode === "wasm-dev") {
const wasi = new WASI({ version: "preview1" });
const instance = new WebAssembly.Instance(new WebAssembly.Module(await readFile(result.archivePath)), {
...wasi.getImportObject(), scriptc: { panic() { throw new Error("unexpected panic"); } },
});
wasi.initialize(instance);
(instance.exports.app_init as Function)();
actual = { stdout: `${(instance.exports.app_dispatch as Function)()}\n`, stderr: "", status: 0 };
} else {
const probe = join(directory, "probe.c"), binary = join(directory, "probe");
await writeFile(probe, '#include <stdio.h>\nvoid app_init(void); double app_dispatch(void);\nint main(void) { app_init(); printf("%.0f\\n", app_dispatch()); app_init(); return 0; }\n');
const link = spawnSync("clang", [probe, result.archivePath, "-lm", ...(mode === "sanitized" ? ["-fsanitize=address"] : []), "-o", binary], { encoding: "utf8" });
expect(link.status, link.stderr).toBe(0);
actual = spawnSync(binary, [], { encoding: "utf8" });
}
expect({ stdout: actual.stdout, stderr: actual.stderr, status: actual.status }).toEqual({ stdout: reference.stdout, stderr: reference.stderr, status: reference.status });
} finally {
if (oldTarget === undefined) delete process.env["SCRIPTC_TARGET"];
else process.env["SCRIPTC_TARGET"] = oldTarget;
await rm(directory, { recursive: true, force: true });
}
});