Files
scriptc/tests/harness/native-object-example.test.ts
Chris Tate 3f66c8e20b Make LLVM the sole production backend
- Remove C emission, debugging options, and automatic backend fallback.
- Complete LLVM lowering and native bootstrap coverage across supported targets.
- Link executables and libraries with precompiled C runtime packs, including iOS and Android.
2026-09-29 20:54:09 -05:00

95 lines
3.6 KiB
TypeScript

import { execFileSync } from "node:child_process";
import {
chmodSync,
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { release as osRelease, tmpdir } from "node:os";
import { join } from "node:path";
import { afterAll, describe, expect, test } from "vitest";
const supported = process.platform === "darwin" && process.arch === "arm64" &&
Number.parseInt(osRelease().split(".", 1)[0] ?? "", 10) >= 24;
const repoRoot = join(import.meta.dirname, "../..");
const fixture = join(repoRoot, "examples/native-object");
const scratch = mkdtempSync(join(tmpdir(), "scriptc-native-object-example-"));
afterAll(() => rmSync(scratch, { recursive: true, force: true }));
describe.runIf(supported)("external native object example", () => {
test("rejects a stale runtime pack before invoking the toolchain", () => {
const runtimeRoot = join(scratch, "stale-runtime");
const traps = join(scratch, "stale-traps");
const trapLog = join(scratch, "stale-toolchain.log");
const linkInfo = join(scratch, "stale-link-info.json");
mkdirSync(runtimeRoot, { recursive: true });
mkdirSync(traps, { recursive: true });
writeFileSync(join(runtimeRoot, "package.json"), JSON.stringify({
name: "@scriptc/runtime",
version: "2.0.0",
}));
const clang = join(traps, "clang");
writeFileSync(clang, `#!/bin/sh\nprintf invoked > '${trapLog}'\nexit 97\n`);
chmodSync(clang, 0o755);
writeFileSync(linkInfo, JSON.stringify({
schema: "scriptc.native-link-info.v1",
runtime_pack: {
package: "@scriptc/runtime",
version: "1.0.0",
root: runtimeRoot,
objects: [], archives: [],
},
program: { object: join(scratch, "missing.o") },
ffi: { libraries: [] },
link: { driver_flags: [], system_libraries: [], frameworks: [] },
}));
let failure: { stderr?: Buffer } | null = null;
try {
execFileSync(process.execPath, [
join(fixture, "link.mjs"), "cc", linkInfo, join(scratch, "stale-program"),
], {
env: { ...process.env, PATH: `${traps}:${process.env["PATH"] ?? ""}` },
stdio: "pipe",
});
} catch (error) {
failure = error as { stderr?: Buffer };
}
expect(failure?.stderr?.toString("utf8")).toContain(
"runtime pack identity mismatch: expected @scriptc/runtime@1.0.0",
);
expect(existsSync(trapLog)).toBe(false);
});
test("links and runs through both the C driver and Apple ld", () => {
const nativeObject = join(scratch, "native.o");
const programObject = join(scratch, "app.o");
const manifest = join(scratch, "ffi.json");
const linkInfo = join(scratch, "link-info.json");
const raw = JSON.parse(readFileSync(join(fixture, "ffi.json"), "utf8")) as { libraries: string[] };
raw.libraries = [nativeObject];
writeFileSync(manifest, JSON.stringify(raw));
execFileSync("clang", [
"-target", "arm64-apple-macosx14.0.0", "-O2", "-c",
join(fixture, "native.c"), "-o", nativeObject,
]);
const json = execFileSync("node", [
join(repoRoot, "packages/cli/dist/main.js"),
"build", join(fixture, "main.ts"),
"--ffi", manifest,
"--print=native-link-info", "-o", programObject,
], { encoding: "utf8" });
writeFileSync(linkInfo, json);
for (const mode of ["cc", "ld"] as const) {
const executable = join(scratch, `app-${mode}`);
execFileSync("node", [join(fixture, "link.mjs"), mode, linkInfo, executable]);
expect(execFileSync(executable, [], { encoding: "utf8" })).toBe("42\n");
}
}, 60_000);
});