mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-21 05:50:55 +08:00
* 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.
51 lines
3.2 KiB
TypeScript
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 });
|
|
}
|
|
});
|