mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-04 09:28:24 +08:00
* Separate native linking from C compilation - Split the helper-object runtime-pack path into explicit link planning and platform linking. - Reserve SCRIPTC_LINKER for object-only executable links while retaining a deprecated SCRIPTC_CC compatibility path. - Add no-legacy-C coverage and document the linker/SDK boundary. * Preserve runtime pack link errors * Fix linker cache invalidation and warnings * Fix C executable cache routing - Keep explicit C builds on compiler-backed cache identities. - Cover linker-only environment changes across exact C repeats.
89 lines
3.6 KiB
TypeScript
89 lines
3.6 KiB
TypeScript
import { execFile } from "node:child_process";
|
|
import { createRequire } from "node:module";
|
|
import { chmod, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
import { release as osRelease, tmpdir } from "node:os";
|
|
import { basename, dirname, join } from "node:path";
|
|
import { promisify } from "node:util";
|
|
import { afterEach, describe, expect, test } from "vitest";
|
|
|
|
const execFileAsync = promisify(execFile);
|
|
const require = createRequire(import.meta.url);
|
|
const repoRoot = join(import.meta.dirname, "../../..");
|
|
const cliEntry = join(repoRoot, "packages/cli/src/main.ts");
|
|
const tsxLoader = join(dirname(require.resolve("tsx/package.json")), "dist/loader.mjs");
|
|
const supported = process.platform === "darwin" && process.arch === "arm64" &&
|
|
Number.parseInt(osRelease().split(".", 1)[0] ?? "", 10) >= 24;
|
|
const dirs: string[] = [];
|
|
|
|
afterEach(async () => {
|
|
await Promise.all(dirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })));
|
|
});
|
|
|
|
describe.runIf(supported)("precompiled runtime executable builds", () => {
|
|
test("ordinary LLVM executables invoke the linker but no C compiler mode", async () => {
|
|
const dir = await mkdtemp(join(tmpdir(), "scriptc-runtime-pack-cli-"));
|
|
dirs.push(dir);
|
|
const entry = join(dir, "main.ts");
|
|
const output = join(dir, "program");
|
|
const wrapper = join(dir, "linker");
|
|
const log = join(dir, "linker.json");
|
|
await writeFile(entry, 'console.log("precompiled runtime");\n');
|
|
await writeFile(wrapper, [
|
|
"#!/bin/sh",
|
|
`node -e 'require("fs").writeFileSync(process.argv[1], JSON.stringify(process.argv.slice(2)))' '${log}' \"$@\"`,
|
|
"exec clang \"$@\"",
|
|
"",
|
|
].join("\n"));
|
|
await chmod(wrapper, 0o755);
|
|
const cliArgs = [
|
|
"--import", tsxLoader, cliEntry, "build", entry, "-o", output,
|
|
];
|
|
const env = {
|
|
...process.env,
|
|
SCRIPTC_NO_CACHE: "1",
|
|
SCRIPTC_LINKER: wrapper,
|
|
};
|
|
await execFileAsync(process.execPath, cliArgs, {
|
|
env: {
|
|
...env,
|
|
},
|
|
});
|
|
const args = JSON.parse(await readFile(log, "utf8")) as string[];
|
|
expect(args).not.toContain("-c");
|
|
expect(args.some((arg) => arg.endsWith(".c") || arg.endsWith(".ll"))).toBe(false);
|
|
expect(args.some((arg) =>
|
|
arg.includes("scriptc-runtime-pack-link-") && arg.includes("/artifacts/")
|
|
)).toBe(true);
|
|
expect(args.some((arg) => arg.includes("runtime-darwin-arm64/artifacts"))).toBe(false);
|
|
await expect(execFileAsync(output, [], { encoding: "utf8" }))
|
|
.resolves.toMatchObject({ stdout: "precompiled runtime\n" });
|
|
const firstExecutable = await readFile(output);
|
|
const signature = await execFileAsync("codesign", ["-dvvv", output], { encoding: "utf8" });
|
|
expect(signature.stderr).toContain(`Identifier=${basename(output)}`);
|
|
|
|
await execFileAsync(process.execPath, cliArgs, { env });
|
|
expect(await readFile(output)).toEqual(firstExecutable);
|
|
});
|
|
|
|
test("the object-plus-runtime-pack route remains live with the legacy C pipeline disabled", async () => {
|
|
const dir = await mkdtemp(join(tmpdir(), "scriptc-runtime-pack-no-legacy-"));
|
|
dirs.push(dir);
|
|
const entry = join(dir, "main.ts");
|
|
const output = join(dir, "program");
|
|
await writeFile(entry, 'console.log("no legacy C");\n');
|
|
await execFileAsync(
|
|
process.execPath,
|
|
["--import", tsxLoader, cliEntry, "build", entry, "-o", output],
|
|
{
|
|
env: {
|
|
...process.env,
|
|
SCRIPTC_NO_CACHE: "1",
|
|
SCRIPTC_LEGACY_C_PIPELINE: "0",
|
|
},
|
|
},
|
|
);
|
|
await expect(execFileAsync(output, [], { encoding: "utf8" }))
|
|
.resolves.toMatchObject({ stdout: "no legacy C\n" });
|
|
});
|
|
});
|