Files
scriptc/packages/compiler/src/native/executable-cache-open.test.ts
T
Chris Tate 944cae84a8 Keep an unchanged executable without selecting the runtime pack
An unchanged rebuild that kept its output still selected the runtime pack's
artifacts first, reading the stored selection, and computed the identity of
every selected artifact to open the executable cache.

- The executable cache key names the pack by its package.json text, the
  manifest's identity (its text on darwin), and the link features and
  flavor. Together they fix which artifacts the selection names.
- The cache selects the artifacts only when it needs their identities, in
  restore() and trace(). keep() checks the recorded entry, which lists them.
- A build that does not keep or restore its executable selects the pack
  before code generation, as before.
2026-10-25 00:06:13 -05:00

94 lines
3.4 KiB
TypeScript

import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, expect, test } from "vitest";
import type { NativeLinkFeatures } from "../backend/native-link-info.js";
import type { NativeRuntimeSelection } from "../backend/runtime-pack-native.js";
import { NativeCache } from "./cache.js";
import { type ExecutableRuntimePack, openNativeExecutableCache } from "./executable-cache.js";
import type { NativeToolchain } from "./toolchain.js";
const directories: string[] = [];
const saved = new Map<string, string | undefined>();
beforeEach(() => {
for (const name of ["SCRIPTC_LINKER", "SCRIPTC_LLVM_HELPER", "CC", "LDFLAGS", "LIBRARY_PATH"]) {
saved.set(name, process.env[name]);
delete process.env[name];
}
});
afterEach(() => {
for (const [name, value] of saved) {
if (value === undefined) delete process.env[name];
else process.env[name] = value;
}
saved.clear();
for (const path of directories.splice(0)) rmSync(path, { recursive: true, force: true });
});
/** A toolchain whose linker is a driver script named `driver`; it answers
* `-print-prog-name=clang` with its own path. */
function fixture(platform: string, driver: string) {
const root = mkdtempSync(join(tmpdir(), "scriptc-executable-cache-open-"));
directories.push(root);
const bin = join(root, "bin");
const helper = join(root, "helper");
const runtime = join(root, "runtime");
for (const directory of [bin, helper, runtime]) mkdirSync(directory);
const linker = join(bin, driver);
writeFileSync(linker, '#!/bin/sh\necho "$0"\n');
chmodSync(linker, 0o755);
writeFileSync(join(helper, "codegen"), "helper");
writeFileSync(join(helper, "package.json"), "{}");
writeFileSync(join(runtime, "package.json"), "{}");
writeFileSync(join(runtime, "runtime-pack.json"), "{}");
const toolchain = {
compilerVersion: "test",
target: { platform },
helperExecutable: join(helper, "codegen"),
helperPackageRoot: helper,
runtimePackRoot: runtime,
linker,
linkerArgs: [],
dsymutil: "dsymutil",
} as unknown as NativeToolchain;
const pack: ExecutableRuntimePack = {
root: runtime,
features: {} as NativeLinkFeatures,
flavor: "release",
select: () =>
({
root: runtime,
licenses: [],
packageText: "{}",
manifestText: "{}",
selected: { runtime: [], archives: [] },
flavor: "release",
}) as unknown as NativeRuntimeSelection,
};
const open = () =>
openNativeExecutableCache(
new NativeCache(join(root, "cache")),
toolchain,
() => "llvm digest",
{ outPath: join(root, "program"), outDir: root, optimization: "release" },
null,
pack,
);
return { open };
}
test.skipIf(process.platform === "win32")(
"Linux builds that link with a clang driver use the executable cache",
() => {
expect(fixture("linux", "clang").open()).not.toBeNull();
expect(fixture("linux", "clang-22").open()).not.toBeNull();
expect(fixture("linux", "gcc").open()).toBeNull();
expect(fixture("windows", "clang").open()).toBeNull();
expect(fixture("wasi", "clang").open()).toBeNull();
// On darwin only Apple's driver at /usr/bin/clang qualifies.
expect(fixture("darwin", "clang").open()).toBeNull();
process.env["SCRIPTC_LLVM_HELPER"] = "/elsewhere/codegen";
expect(fixture("linux", "clang").open()).toBeNull();
},
);