@scriptc/compiler 0.0.33 → 0.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/dist/backend/cc-cache.test.js +1091 -7
  2. package/dist/backend/cc-cache.test.js.map +1 -1
  3. package/dist/backend/cc.d.ts +109 -18
  4. package/dist/backend/cc.js +1057 -289
  5. package/dist/backend/cc.js.map +1 -1
  6. package/dist/backend/emission/emit-exprs.js +26 -1
  7. package/dist/backend/emission/emit-exprs.js.map +1 -1
  8. package/dist/backend/emission/emit-shapes.js +37 -4
  9. package/dist/backend/emission/emit-shapes.js.map +1 -1
  10. package/dist/backend/emission/emitter.d.ts +11 -2
  11. package/dist/backend/emission/emitter.js +11 -5
  12. package/dist/backend/emission/emitter.js.map +1 -1
  13. package/dist/backend/library-identity.d.ts +22 -0
  14. package/dist/backend/library-identity.js +103 -0
  15. package/dist/backend/library-identity.js.map +1 -0
  16. package/dist/backend/llvm/emitter.d.ts +3 -0
  17. package/dist/backend/llvm/emitter.js +35 -5
  18. package/dist/backend/llvm/emitter.js.map +1 -1
  19. package/dist/backend/llvm/shapes.d.ts +1 -0
  20. package/dist/backend/llvm/shapes.js +25 -1
  21. package/dist/backend/llvm/shapes.js.map +1 -1
  22. package/dist/backend/llvm/split.d.ts +43 -0
  23. package/dist/backend/llvm/split.js +227 -0
  24. package/dist/backend/llvm/split.js.map +1 -0
  25. package/dist/backend/llvm/split.test.d.ts +1 -0
  26. package/dist/backend/llvm/split.test.js +102 -0
  27. package/dist/backend/llvm/split.test.js.map +1 -0
  28. package/dist/backend/mangle.d.ts +1 -0
  29. package/dist/backend/mangle.js +3 -0
  30. package/dist/backend/mangle.js.map +1 -1
  31. package/dist/backend/object-localize.d.ts +5 -5
  32. package/dist/backend/object-localize.js +19 -16
  33. package/dist/backend/object-localize.js.map +1 -1
  34. package/dist/backend/object-localize.test.js +57 -13
  35. package/dist/backend/object-localize.test.js.map +1 -1
  36. package/dist/executable/early-cache.d.ts +86 -0
  37. package/dist/executable/early-cache.js +460 -0
  38. package/dist/executable/early-cache.js.map +1 -0
  39. package/dist/executable/early-cache.test.d.ts +1 -0
  40. package/dist/executable/early-cache.test.js +300 -0
  41. package/dist/executable/early-cache.test.js.map +1 -0
  42. package/dist/ffi/profile.d.ts +7 -2
  43. package/dist/ffi/profile.js +1 -0
  44. package/dist/ffi/profile.js.map +1 -1
  45. package/dist/frontend/input-tracker.d.ts +82 -0
  46. package/dist/frontend/input-tracker.js +380 -0
  47. package/dist/frontend/input-tracker.js.map +1 -0
  48. package/dist/frontend/input-tracker.test.d.ts +1 -0
  49. package/dist/frontend/input-tracker.test.js +137 -0
  50. package/dist/frontend/input-tracker.test.js.map +1 -0
  51. package/dist/frontend/lowering/lower-builtins.js +4 -10
  52. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  53. package/dist/frontend/lowering/lower-calls.js +169 -144
  54. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  55. package/dist/frontend/lowering/lower-classes.js +3 -0
  56. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  57. package/dist/frontend/lowering/lower-containers.js +66 -32
  58. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  59. package/dist/frontend/lowering/lower-exprs.js +89 -11
  60. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  61. package/dist/frontend/lowering/lower-inspect.js +45 -26
  62. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  63. package/dist/frontend/lowering/lower-island.js +3 -3
  64. package/dist/frontend/lowering/lower-island.js.map +1 -1
  65. package/dist/frontend/lowering/lower-mixins.js +1 -1
  66. package/dist/frontend/lowering/lower-mixins.js.map +1 -1
  67. package/dist/frontend/lowering/lower-modules.js +1 -1
  68. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  69. package/dist/frontend/lowering/lower-stmts.js +49 -4
  70. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  71. package/dist/frontend/lowering/lowerer.d.ts +73 -29
  72. package/dist/frontend/lowering/lowerer.js +588 -116
  73. package/dist/frontend/lowering/lowerer.js.map +1 -1
  74. package/dist/frontend/lowering/surfaces.js +1 -1
  75. package/dist/frontend/lowering/surfaces.js.map +1 -1
  76. package/dist/frontend/npm-static-rewrite.js +25 -26
  77. package/dist/frontend/npm-static-rewrite.js.map +1 -1
  78. package/dist/frontend/npm-static.js +29 -30
  79. package/dist/frontend/npm-static.js.map +1 -1
  80. package/dist/frontend/npm-static.test.d.ts +1 -0
  81. package/dist/frontend/npm-static.test.js +15 -0
  82. package/dist/frontend/npm-static.test.js.map +1 -0
  83. package/dist/frontend/npm.js +5 -17
  84. package/dist/frontend/npm.js.map +1 -1
  85. package/dist/frontend/program.js +44 -19
  86. package/dist/frontend/program.js.map +1 -1
  87. package/dist/frontend/resolve.js +8 -29
  88. package/dist/frontend/resolve.js.map +1 -1
  89. package/dist/frontend/ts7/checker.d.ts +40 -2
  90. package/dist/frontend/ts7/checker.js +194 -32
  91. package/dist/frontend/ts7/checker.js.map +1 -1
  92. package/dist/frontend/ts7/program.js +17 -18
  93. package/dist/frontend/ts7/program.js.map +1 -1
  94. package/dist/frontend/types.d.ts +33 -0
  95. package/dist/frontend/types.js +134 -45
  96. package/dist/frontend/types.js.map +1 -1
  97. package/dist/index.d.ts +5 -2
  98. package/dist/index.js +458 -141
  99. package/dist/index.js.map +1 -1
  100. package/dist/ir/nodes.d.ts +27 -4
  101. package/dist/ir/nodes.js +28 -0
  102. package/dist/ir/nodes.js.map +1 -1
  103. package/dist/ir/serialize.d.ts +1 -1
  104. package/dist/ir/serialize.js +1 -1
  105. package/dist/ir/validate.js +37 -1
  106. package/dist/ir/validate.js.map +1 -1
  107. package/dist/library/early-cache.d.ts +65 -0
  108. package/dist/library/early-cache.js +361 -0
  109. package/dist/library/early-cache.js.map +1 -0
  110. package/dist/library/early-cache.test.d.ts +1 -0
  111. package/dist/library/early-cache.test.js +528 -0
  112. package/dist/library/early-cache.test.js.map +1 -0
  113. package/dist/library/implementation-identity.d.ts +23 -0
  114. package/dist/library/implementation-identity.js +84 -0
  115. package/dist/library/implementation-identity.js.map +1 -0
  116. package/dist/library/int-infer.js +14 -0
  117. package/dist/library/int-infer.js.map +1 -1
  118. package/dist/library/int-infer.test.js +21 -4
  119. package/dist/library/int-infer.test.js.map +1 -1
  120. package/dist/library/semantic-source.d.ts +13 -0
  121. package/dist/library/semantic-source.js +257 -0
  122. package/dist/library/semantic-source.js.map +1 -0
  123. package/dist/library/semantic-source.test.d.ts +1 -0
  124. package/dist/library/semantic-source.test.js +113 -0
  125. package/dist/library/semantic-source.test.js.map +1 -0
  126. package/dist/library/sidecar.d.ts +6 -0
  127. package/dist/library/sidecar.js +11 -0
  128. package/dist/library/sidecar.js.map +1 -1
  129. package/dist/startup-cache.d.ts +6 -0
  130. package/dist/startup-cache.js +6 -0
  131. package/dist/startup-cache.js.map +1 -0
  132. package/package.json +3 -2
  133. package/surface-manifest.json +1 -1
@@ -5,7 +5,8 @@ import { chmod, cp, mkdir, mkdtemp, readdir, readFile, rm, stat, symlink, utimes
5
5
  import { tmpdir } from "node:os";
6
6
  import { delimiter, join } from "node:path";
7
7
  import { afterAll, afterEach, expect, test as vitestTest } from "vitest";
8
- import { cacheTargetIdentity, ccVersionOnce, compileC, compileLibArchive, implicitDependencyProbeIncludes, parseLinkTraceFiles, resolveBuildCacheRoot, runtimeFingerprint, runtimeSrcDir, stageRuntimeObjects, toolchainEnvironmentCachePolicy, toolchainEnvironmentFingerprint, vendorCacheBuildIdentity, vendorCacheTargetFlavor, } from "./cc.js";
8
+ import { cacheTargetIdentity, ccVersionOnce, compileC, compileLibArchive, executableNativeEnvironmentFingerprint, implicitDependencyProbeIncludes, parseLinkTraceFiles, resolveBuildCacheRoot, runtimeFingerprint, runtimeSrcDir, stageRuntimeObjects, supportedNativeCacheWarmProfiles, toolchainEnvironmentCachePolicy, toolchainEnvironmentFingerprint, vendorCacheBuildIdentity, vendorCacheTargetFlavor, warmNativeCaches, } from "./cc.js";
9
+ import { splitLlvmProgram } from "./llvm/split.js";
9
10
  const scratch = [];
10
11
  const TEST_CACHE_IDENTITY = "cc-cache-tests-v1";
11
12
  const originalStableToolchain = process.env["SCRIPTC_TEST_STABLE_TOOLCHAIN"];
@@ -41,10 +42,20 @@ const test = Object.assign((name, ...args) => Reflect.apply(cacheTestSelected(na
41
42
  const trustInstrumentedCompilerWrapper = () => {
42
43
  process.env["SCRIPTC_TEST_TRUST_COMPILER_WRAPPER"] = "1";
43
44
  };
44
- const zigExecutable = (process.env["PATH"] ?? "")
45
+ const executableOnPath = (name) => (process.env["PATH"] ?? "")
45
46
  .split(delimiter)
46
- .map((entry) => join(entry === "" ? process.cwd() : entry, "zig"))
47
+ .map((entry) => join(entry === "" ? process.cwd() : entry, name))
47
48
  .find((candidate) => existsSync(candidate));
49
+ const zigExecutable = executableOnPath("zig");
50
+ const clangExecutable = executableOnPath("clang");
51
+ const arExecutable = executableOnPath("ar");
52
+ const ldExecutable = executableOnPath("ld");
53
+ const objcopyExecutable = executableOnPath("objcopy");
54
+ const nativeShardMergeAvailable = process.platform === "darwin"
55
+ ? ldExecutable !== undefined
56
+ : process.platform === "linux"
57
+ ? ldExecutable !== undefined && objcopyExecutable !== undefined
58
+ : process.platform === "win32" && process.arch === "x64";
48
59
  const completeArtifacts = async (root, kind) => (await readdir(join(root, kind))).filter((name) => !name.endsWith(".sha256"));
49
60
  const cacheTreeBytes = async (root) => {
50
61
  let total = 0;
@@ -85,6 +96,36 @@ test("the production cache root follows overrides, platform defaults, and the ha
85
96
  expect(resolveBuildCacheRoot({}, "darwin", "/Users/tester")).toBe("/Users/tester/Library/Caches/scriptc/build");
86
97
  expect(resolveBuildCacheRoot({ LOCALAPPDATA: "/Users/tester/AppData/Local" }, "win32", "/Users/tester")).toBe("/Users/tester/AppData/Local/scriptc/cache/build");
87
98
  });
99
+ test.skipIf(process.platform === "win32")("the early executable identity follows a compiler selected behind a stable driver", async () => {
100
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-effective-compiler-"));
101
+ scratch.push(dir);
102
+ const binDir = join(dir, "bin");
103
+ const selector = join(dir, "selected");
104
+ const firstCompiler = join(dir, "clang-first");
105
+ const secondCompiler = join(dir, "clang-second");
106
+ await mkdir(binDir);
107
+ await Promise.all([
108
+ writeFile(firstCompiler, "#!/bin/sh\nexit 0\n"),
109
+ writeFile(secondCompiler, "#!/bin/sh\nexit 0\n"),
110
+ writeFile(join(binDir, "clang"), "#!/bin/sh\nselected=$(cat \"$SCRIPTC_TEST_EFFECTIVE_COMPILER\")\nprintf '\"%s\" \"-cc1\"\\n' \"$selected\" >&2\n"),
111
+ writeFile(selector, `${firstCompiler}\n`),
112
+ ]);
113
+ await Promise.all([
114
+ chmod(firstCompiler, 0o755),
115
+ chmod(secondCompiler, 0o755),
116
+ chmod(join(binDir, "clang"), 0o755),
117
+ ]);
118
+ const env = {
119
+ ...process.env,
120
+ PATH: `${binDir}${delimiter}${process.env["PATH"] ?? ""}`,
121
+ SCRIPTC_TEST_EFFECTIVE_COMPILER: selector,
122
+ };
123
+ const first = await executableNativeEnvironmentFingerprint(env);
124
+ expect(await executableNativeEnvironmentFingerprint(env)).toBe(first);
125
+ await writeFile(selector, `${secondCompiler}\n`);
126
+ const second = await executableNativeEnvironmentFingerprint(env);
127
+ expect(second).not.toBe(first);
128
+ });
88
129
  test("native cache identities separate host architectures while cross targets remain explicit", () => {
89
130
  expect(cacheTargetIdentity({ target: null }, "darwin", "arm64")).toBe("native:darwin:arm64");
90
131
  expect(cacheTargetIdentity({ target: null }, "darwin", "x64")).toBe("native:darwin:x64");
@@ -142,9 +183,11 @@ test("the toolchain environment joins cache identities", () => {
142
183
  completeArtifacts: false,
143
184
  runtimeObjects: false,
144
185
  });
186
+ // scriptc invokes its compiler and archiver directly; conventional build-
187
+ // system variables do not alter those commands.
145
188
  expect(toolchainEnvironmentCachePolicy({ CFLAGS: "-I/headers" })).toEqual({
146
- completeArtifacts: false,
147
- runtimeObjects: false,
189
+ completeArtifacts: true,
190
+ runtimeObjects: true,
148
191
  });
149
192
  expect(toolchainEnvironmentCachePolicy({ ZIG_LIB_DIR: "/zig/lib" })).toEqual({
150
193
  completeArtifacts: false,
@@ -154,8 +197,9 @@ test("the toolchain environment joins cache identities", () => {
154
197
  completeArtifacts: false,
155
198
  runtimeObjects: false,
156
199
  });
157
- expect(vendorCacheBuildIdentity(base, "compiler-one")).not.toBe(vendorCacheBuildIdentity(base, "compiler-two"));
158
- expect(vendorCacheBuildIdentity(base, "compiler-one")).not.toBe(vendorCacheBuildIdentity(toolchainEnvironmentFingerprint({ MACOSX_DEPLOYMENT_TARGET: "11.0" }), "compiler-one"));
200
+ expect(vendorCacheBuildIdentity(base, "compiler-one", "sources-one")).not.toBe(vendorCacheBuildIdentity(base, "compiler-two", "sources-one"));
201
+ expect(vendorCacheBuildIdentity(base, "compiler-one", "sources-one")).not.toBe(vendorCacheBuildIdentity(toolchainEnvironmentFingerprint({ MACOSX_DEPLOYMENT_TARGET: "11.0" }), "compiler-one", "sources-one"));
202
+ expect(vendorCacheBuildIdentity(base, "compiler-one", "sources-one")).not.toBe(vendorCacheBuildIdentity(base, "compiler-one", "sources-two"));
159
203
  });
160
204
  test.skipIf(process.platform === "win32")("compiler version probes use and remove a private working directory", async () => {
161
205
  const dir = await mkdtemp(join(tmpdir(), "scriptc-version-probe-"));
@@ -1682,6 +1726,339 @@ test("the hard disable bypasses vendor prerequisite caches", async () => {
1682
1726
  process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
1683
1727
  }
1684
1728
  });
1729
+ test.skipIf(process.platform === "win32")("shared vendor prerequisites re-key when vendored source bytes change", async () => {
1730
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-vendor-source-identity-"));
1731
+ scratch.push(dir);
1732
+ const cacheRoot = join(dir, "cache");
1733
+ const fakeRuntimeRoot = join(dir, "runtime");
1734
+ const fakeRuntime = join(fakeRuntimeRoot, "src");
1735
+ const originalRuntime = runtimeSrcDir();
1736
+ const cPath = join(dir, "program.c");
1737
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
1738
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
1739
+ const oldRuntimeDir = process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"];
1740
+ const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1741
+ try {
1742
+ await Promise.all([
1743
+ cp(originalRuntime, fakeRuntime, { recursive: true }),
1744
+ mkdir(join(fakeRuntimeRoot, "vendor"), { recursive: true }).then(() => Promise.all(["quickjs-ng", "ryu"].map((name) => cp(join(originalRuntime, "..", "vendor", name), join(fakeRuntimeRoot, "vendor", name), { recursive: true })))),
1745
+ ]);
1746
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
1747
+ process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"] = fakeRuntime;
1748
+ delete process.env["SCRIPTC_NO_CACHE"];
1749
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1750
+ await writeFile(cPath, "int main(void) { return 0; }\n");
1751
+ await compileC({
1752
+ cPath,
1753
+ outPath: join(dir, "first"),
1754
+ cacheIdentity: TEST_CACHE_IDENTITY,
1755
+ regex: true,
1756
+ });
1757
+ const vendorRoot = join(cacheRoot, "vendor");
1758
+ expect((await readdir(vendorRoot)).filter((name) => name.includes("-lre-"))).toHaveLength(1);
1759
+ const libregexp = join(fakeRuntimeRoot, "vendor", "quickjs-ng", "libregexp.c");
1760
+ await writeFile(libregexp, `${await readFile(libregexp, "utf8")}\n/* cache identity probe */\n`);
1761
+ await writeFile(cPath, "int main(void) { return 0; } /* second */\n");
1762
+ await compileC({
1763
+ cPath,
1764
+ outPath: join(dir, "second"),
1765
+ cacheIdentity: TEST_CACHE_IDENTITY,
1766
+ regex: true,
1767
+ });
1768
+ expect((await readdir(vendorRoot)).filter((name) => name.includes("-lre-"))).toHaveLength(2);
1769
+ }
1770
+ finally {
1771
+ if (oldCacheDir === undefined)
1772
+ delete process.env["SCRIPTC_CACHE_DIR"];
1773
+ else
1774
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
1775
+ if (oldNoCache === undefined)
1776
+ delete process.env["SCRIPTC_NO_CACHE"];
1777
+ else
1778
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
1779
+ if (oldRuntimeDir === undefined)
1780
+ delete process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"];
1781
+ else
1782
+ process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"] = oldRuntimeDir;
1783
+ if (oldVendorCacheDir === undefined)
1784
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1785
+ else
1786
+ process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
1787
+ }
1788
+ }, 120_000);
1789
+ test.skipIf(process.platform === "win32")("uncached arbitrary C keeps vendor prerequisites outside a read-only runtime package", async () => {
1790
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-readonly-runtime-"));
1791
+ scratch.push(dir);
1792
+ const fakeRuntimeRoot = join(dir, "runtime");
1793
+ const fakeRuntime = join(fakeRuntimeRoot, "src");
1794
+ const fakeVendor = join(fakeRuntimeRoot, "vendor");
1795
+ const originalRuntime = runtimeSrcDir();
1796
+ const cPath = join(dir, "program.c");
1797
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
1798
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
1799
+ const oldRuntimeDir = process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"];
1800
+ const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1801
+ try {
1802
+ await Promise.all([
1803
+ cp(originalRuntime, fakeRuntime, { recursive: true }),
1804
+ mkdir(fakeVendor, { recursive: true }).then(() => Promise.all(["quickjs-ng", "ryu"].map((name) => cp(join(originalRuntime, "..", "vendor", name), join(fakeVendor, name), {
1805
+ recursive: true,
1806
+ })))),
1807
+ ]);
1808
+ process.env["SCRIPTC_CACHE_DIR"] = join(dir, "cache");
1809
+ process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"] = fakeRuntime;
1810
+ delete process.env["SCRIPTC_NO_CACHE"];
1811
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1812
+ await writeFile(cPath, "int main(void) { return 0; }\n");
1813
+ await chmod(fakeVendor, 0o555);
1814
+ const outPath = join(dir, "program");
1815
+ await compileC({ cPath, outPath, regex: true });
1816
+ expect((await stat(outPath)).isFile()).toBe(true);
1817
+ await expect(stat(join(fakeVendor, ".cache"))).rejects.toMatchObject({ code: "ENOENT" });
1818
+ }
1819
+ finally {
1820
+ await chmod(fakeVendor, 0o755).catch(() => undefined);
1821
+ if (oldCacheDir === undefined)
1822
+ delete process.env["SCRIPTC_CACHE_DIR"];
1823
+ else
1824
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
1825
+ if (oldNoCache === undefined)
1826
+ delete process.env["SCRIPTC_NO_CACHE"];
1827
+ else
1828
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
1829
+ if (oldRuntimeDir === undefined)
1830
+ delete process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"];
1831
+ else
1832
+ process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"] = oldRuntimeDir;
1833
+ if (oldVendorCacheDir === undefined)
1834
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1835
+ else
1836
+ process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
1837
+ }
1838
+ }, 120_000);
1839
+ test("native cache warming seeds exact runtime and vendor families without complete binaries", async () => {
1840
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-warm-"));
1841
+ scratch.push(dir);
1842
+ const cacheRoot = join(dir, "cache");
1843
+ const vendorCacheRoot = join(cacheRoot, "vendor");
1844
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
1845
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
1846
+ const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1847
+ try {
1848
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
1849
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1850
+ delete process.env["SCRIPTC_NO_CACHE"];
1851
+ const warmed = await warmNativeCaches({ profiles: ["runtime", "tls", "dynamic"] });
1852
+ expect(warmed.cacheRoot).toBe(cacheRoot);
1853
+ expect(warmed.profiles.map(({ profile }) => profile)).toEqual(["runtime", "tls", "dynamic"]);
1854
+ expect(warmed.profiles.every(({ elapsedMs }) => elapsedMs >= 0)).toBe(true);
1855
+ expect((await readdir(join(cacheRoot, "obj"), { withFileTypes: true })).filter((e) => e.isDirectory())).toHaveLength(3);
1856
+ expect(await readdir(join(cacheRoot, "bin")).catch(() => [])).toEqual([]);
1857
+ const vendorEntries = await readdir(vendorCacheRoot);
1858
+ const tlsEntry = vendorEntries.find((name) => name.startsWith("mbedtls-"));
1859
+ const engineEntry = vendorEntries.find((name) => /^3c8f3d689539-plain-/.test(name));
1860
+ expect(tlsEntry).toBeDefined();
1861
+ expect(engineEntry).toBeDefined();
1862
+ expect((await stat(join(vendorCacheRoot, tlsEntry, "libmbedtls.a.sha256"))).isFile()).toBe(true);
1863
+ expect((await stat(join(vendorCacheRoot, engineEntry, "libqjs.a.sha256"))).isFile()).toBe(true);
1864
+ }
1865
+ finally {
1866
+ if (oldCacheDir === undefined)
1867
+ delete process.env["SCRIPTC_CACHE_DIR"];
1868
+ else
1869
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
1870
+ if (oldNoCache === undefined)
1871
+ delete process.env["SCRIPTC_NO_CACHE"];
1872
+ else
1873
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
1874
+ if (oldVendorCacheDir === undefined)
1875
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1876
+ else
1877
+ process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
1878
+ }
1879
+ }, 120_000);
1880
+ test("damaged shared vendor archives are rejected and rebuilt before linking", async () => {
1881
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-vendor-integrity-"));
1882
+ scratch.push(dir);
1883
+ const cacheRoot = join(dir, "cache");
1884
+ const cPath = join(dir, "program.c");
1885
+ const outPath = join(dir, "program");
1886
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
1887
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
1888
+ const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1889
+ const corruption = Buffer.from("scriptc-corrupt-vendor-archive\n");
1890
+ try {
1891
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
1892
+ delete process.env["SCRIPTC_NO_CACHE"];
1893
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1894
+ await writeFile(cPath, "int main(void) { return 0; }\n");
1895
+ await warmNativeCaches({ profiles: ["dynamic"] });
1896
+ const vendorRoot = join(cacheRoot, "vendor");
1897
+ const engineDir = (await readdir(vendorRoot)).find((name) => /^3c8f3d689539-plain-/.test(name));
1898
+ expect(engineDir).toBeDefined();
1899
+ const engineArchive = join(vendorRoot, engineDir, "libqjs.a");
1900
+ expect((await stat(`${engineArchive}.sha256`)).isFile()).toBe(true);
1901
+ await writeFile(engineArchive, corruption);
1902
+ await compileC({
1903
+ cPath,
1904
+ outPath,
1905
+ cacheIdentity: TEST_CACHE_IDENTITY,
1906
+ dynamic: true,
1907
+ });
1908
+ expect((await stat(outPath)).isFile()).toBe(true);
1909
+ const repaired = await readFile(engineArchive);
1910
+ expect(repaired).not.toEqual(corruption);
1911
+ expect((await readFile(`${engineArchive}.sha256`, "utf8")).trim()).toBe(createHash("sha256").update(repaired).digest("hex"));
1912
+ }
1913
+ finally {
1914
+ if (oldCacheDir === undefined)
1915
+ delete process.env["SCRIPTC_CACHE_DIR"];
1916
+ else
1917
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
1918
+ if (oldNoCache === undefined)
1919
+ delete process.env["SCRIPTC_NO_CACHE"];
1920
+ else
1921
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
1922
+ if (oldVendorCacheDir === undefined)
1923
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1924
+ else
1925
+ process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
1926
+ }
1927
+ }, 120_000);
1928
+ test("dynamic builds promote staged vendor archives before bounded LRU eviction", async () => {
1929
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-vendor-lru-"));
1930
+ scratch.push(dir);
1931
+ const cacheRoot = join(dir, "cache");
1932
+ const cPath = join(dir, "program.c");
1933
+ const outPath = join(dir, "program");
1934
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
1935
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
1936
+ const oldMax = process.env["SCRIPTC_CACHE_MAX_MB"];
1937
+ const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1938
+ try {
1939
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
1940
+ delete process.env["SCRIPTC_NO_CACHE"];
1941
+ delete process.env["SCRIPTC_CACHE_MAX_MB"];
1942
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1943
+ await writeFile(cPath, "int main(void) { return 0; }\n");
1944
+ await warmNativeCaches({ profiles: ["dynamic"] });
1945
+ const vendorRoot = join(cacheRoot, "vendor");
1946
+ const engineDir = (await readdir(vendorRoot)).find((name) => /^3c8f3d689539-plain-/.test(name));
1947
+ expect(engineDir).toBeDefined();
1948
+ const engineArchive = join(vendorRoot, engineDir, "libqjs.a");
1949
+ const initialBytes = await cacheTreeBytes(cacheRoot);
1950
+ const capMb = Math.max(4, Math.ceil(initialBytes * 2 / (1024 * 1024)));
1951
+ const filler = join(cacheRoot, "filler.bin");
1952
+ const staleArchiveTime = new Date("2000-01-01T00:00:00.000Z");
1953
+ const fillerTime = new Date("2001-01-01T00:00:00.000Z");
1954
+ await writeFile(filler, Buffer.alloc(capMb * 1024 * 1024));
1955
+ await utimes(engineArchive, staleArchiveTime, staleArchiveTime);
1956
+ await utimes(filler, fillerTime, fillerTime);
1957
+ process.env["SCRIPTC_CACHE_MAX_MB"] = String(capMb);
1958
+ await compileC({
1959
+ cPath,
1960
+ outPath,
1961
+ cacheIdentity: TEST_CACHE_IDENTITY,
1962
+ dynamic: true,
1963
+ });
1964
+ expect((await stat(outPath)).isFile()).toBe(true);
1965
+ expect((await stat(engineArchive)).mtimeMs).toBeGreaterThan(fillerTime.getTime());
1966
+ expect(await cacheTreeBytes(cacheRoot)).toBeLessThanOrEqual(capMb * 1024 * 1024);
1967
+ }
1968
+ finally {
1969
+ if (oldCacheDir === undefined)
1970
+ delete process.env["SCRIPTC_CACHE_DIR"];
1971
+ else
1972
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
1973
+ if (oldNoCache === undefined)
1974
+ delete process.env["SCRIPTC_NO_CACHE"];
1975
+ else
1976
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
1977
+ if (oldMax === undefined)
1978
+ delete process.env["SCRIPTC_CACHE_MAX_MB"];
1979
+ else
1980
+ process.env["SCRIPTC_CACHE_MAX_MB"] = oldMax;
1981
+ if (oldVendorCacheDir === undefined)
1982
+ delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
1983
+ else
1984
+ process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
1985
+ }
1986
+ }, 120_000);
1987
+ test("native cache warming follows the hard cache disable", async () => {
1988
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
1989
+ try {
1990
+ process.env["SCRIPTC_NO_CACHE"] = "1";
1991
+ await expect(warmNativeCaches({ profiles: ["runtime"] })).rejects.toThrow("native build cache is disabled");
1992
+ }
1993
+ finally {
1994
+ if (oldNoCache === undefined)
1995
+ delete process.env["SCRIPTC_NO_CACHE"];
1996
+ else
1997
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
1998
+ }
1999
+ });
2000
+ test("native cache warming refuses environments that disable persistent objects", async () => {
2001
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-warm-disabled-"));
2002
+ scratch.push(dir);
2003
+ const cacheRoot = join(dir, "cache");
2004
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
2005
+ const oldCpath = process.env["CPATH"];
2006
+ try {
2007
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
2008
+ process.env["CPATH"] = dir;
2009
+ await expect(warmNativeCaches({ profiles: ["runtime"] })).rejects.toThrow("requires a persistently cacheable compiler environment");
2010
+ expect(await readdir(cacheRoot)).toEqual([]);
2011
+ }
2012
+ finally {
2013
+ if (oldCacheDir === undefined)
2014
+ delete process.env["SCRIPTC_CACHE_DIR"];
2015
+ else
2016
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
2017
+ if (oldCpath === undefined)
2018
+ delete process.env["CPATH"];
2019
+ else
2020
+ process.env["CPATH"] = oldCpath;
2021
+ }
2022
+ });
2023
+ test("native cache warm profiles follow target capabilities", () => {
2024
+ expect(supportedNativeCacheWarmProfiles({
2025
+ argv: ["zig", "cc"],
2026
+ target: "wasm32-wasi",
2027
+ zigTarget: "wasm32-wasi",
2028
+ targetArgs: [],
2029
+ linkArgs: [],
2030
+ })).toEqual([]);
2031
+ expect(supportedNativeCacheWarmProfiles({
2032
+ argv: ["zig", "cc"],
2033
+ target: "aarch64-apple-ios",
2034
+ zigTarget: "aarch64-ios.15.0",
2035
+ targetArgs: [],
2036
+ linkArgs: [],
2037
+ })).toEqual([]);
2038
+ });
2039
+ test("parallel native cache warming fails cleanly when the bounded cache cannot retain its profiles", async () => {
2040
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-warm-cap-"));
2041
+ scratch.push(dir);
2042
+ const cacheRoot = join(dir, "cache");
2043
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
2044
+ const oldMax = process.env["SCRIPTC_CACHE_MAX_MB"];
2045
+ try {
2046
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
2047
+ process.env["SCRIPTC_CACHE_MAX_MB"] = "2.5";
2048
+ await expect(warmNativeCaches()).rejects.toThrow("SCRIPTC_CACHE_MAX_MB is too small to retain the requested native cache warm profiles");
2049
+ expect(await readdir(cacheRoot)).not.toEqual([]);
2050
+ }
2051
+ finally {
2052
+ if (oldCacheDir === undefined)
2053
+ delete process.env["SCRIPTC_CACHE_DIR"];
2054
+ else
2055
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
2056
+ if (oldMax === undefined)
2057
+ delete process.env["SCRIPTC_CACHE_MAX_MB"];
2058
+ else
2059
+ process.env["SCRIPTC_CACHE_MAX_MB"] = oldMax;
2060
+ }
2061
+ }, 120_000);
1685
2062
  test.skipIf(process.platform !== "darwin")("vendor object caches separate deployment-target environments", async () => {
1686
2063
  const dir = await mkdtemp(join(tmpdir(), "scriptc-vendor-environment-"));
1687
2064
  scratch.push(dir);
@@ -2437,6 +2814,44 @@ test("frontend-generated same-output builds no-op only while output and dependen
2437
2814
  process.umask(oldUmask);
2438
2815
  }
2439
2816
  });
2817
+ test("artifact-ready callbacks expose native dependencies on builds and validated hits", async () => {
2818
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-artifact-ready-"));
2819
+ scratch.push(dir);
2820
+ const cacheRoot = join(dir, "cache");
2821
+ const cPath = join(dir, "program.c");
2822
+ const outPath = join(dir, "program");
2823
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
2824
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
2825
+ try {
2826
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
2827
+ delete process.env["SCRIPTC_NO_CACHE"];
2828
+ await writeFile(cPath, "int main(void) { return 0; }\n");
2829
+ const observations = [];
2830
+ const build = () => compileC({
2831
+ cPath,
2832
+ outPath,
2833
+ cacheIdentity: "scriptc-generated-v1",
2834
+ onArtifactReady: async ({ dependencies }) => {
2835
+ observations.push(dependencies.map((dependency) => dependency.path));
2836
+ },
2837
+ });
2838
+ await build();
2839
+ await build();
2840
+ expect(observations).toHaveLength(2);
2841
+ expect(observations[0].length).toBeGreaterThan(0);
2842
+ expect(observations[1]).toEqual(observations[0]);
2843
+ }
2844
+ finally {
2845
+ if (oldCacheDir === undefined)
2846
+ delete process.env["SCRIPTC_CACHE_DIR"];
2847
+ else
2848
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
2849
+ if (oldNoCache === undefined)
2850
+ delete process.env["SCRIPTC_NO_CACHE"];
2851
+ else
2852
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
2853
+ }
2854
+ });
2440
2855
  test.skipIf(process.platform === "win32")("output-local hits follow symlinked header targets", async () => {
2441
2856
  const dir = await mkdtemp(join(tmpdir(), "scriptc-local-artifact-symlink-"));
2442
2857
  scratch.push(dir);
@@ -2811,4 +3226,673 @@ test("library archives hit by content, invalidate on edits, and reuse runtime ob
2811
3226
  process.env["PATH"] = oldPath;
2812
3227
  }
2813
3228
  });
3229
+ test("library identity edits reuse the cached large program object", async () => {
3230
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-program-object-"));
3231
+ scratch.push(dir);
3232
+ const cacheRoot = join(dir, "cache");
3233
+ const cPath = join(dir, "program.c");
3234
+ const outPath = join(dir, "program.lib.a");
3235
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3236
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3237
+ try {
3238
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
3239
+ delete process.env["SCRIPTC_NO_CACHE"];
3240
+ await mkdir(cacheRoot, { mode: 0o700 });
3241
+ const programSource = "int scriptc_large_program_value(void) { return 7; }\n";
3242
+ await writeFile(cPath, `${programSource}unsigned long long scriptc_build_id(void) { return 1; }\n`);
3243
+ await compileLibArchive({
3244
+ cPath,
3245
+ programSource,
3246
+ identityCSource: "unsigned long long scriptc_build_id(void) { return 1; }\n",
3247
+ outPath,
3248
+ cacheIdentity: TEST_CACHE_IDENTITY,
3249
+ });
3250
+ const [objectName] = (await readdir(join(cacheRoot, "program-obj")))
3251
+ .filter((name) => !name.endsWith(".sha256"));
3252
+ expect(objectName).toBeDefined();
3253
+ const objectPath = join(cacheRoot, "program-obj", objectName);
3254
+ const objectDigest = await readFile(`${objectPath}.sha256`, "utf8");
3255
+ const old = new Date("2000-01-01T00:00:00.000Z");
3256
+ await utimes(objectPath, old, old);
3257
+ await writeFile(cPath, `${programSource}unsigned long long scriptc_build_id(void) { return 2; }\n`);
3258
+ await compileLibArchive({
3259
+ cPath,
3260
+ programSource,
3261
+ identityCSource: "unsigned long long scriptc_build_id(void) { return 2; }\n",
3262
+ outPath,
3263
+ cacheIdentity: TEST_CACHE_IDENTITY,
3264
+ });
3265
+ expect(await readFile(`${objectPath}.sha256`, "utf8")).toBe(objectDigest);
3266
+ expect((await stat(objectPath)).mtimeMs).toBeGreaterThan(old.getTime());
3267
+ expect((await readdir(join(cacheRoot, "program-obj"))).filter((name) => !name.endsWith(".sha256"))).toEqual([objectName]);
3268
+ const probeSource = join(dir, "probe.c");
3269
+ const probe = join(dir, "probe");
3270
+ await writeFile(probeSource, "#include <stdio.h>\nint scriptc_large_program_value(void);\nunsigned long long scriptc_build_id(void);\nint main(void) { printf(\"%d %llu\\n\", scriptc_large_program_value(), scriptc_build_id()); }\n");
3271
+ execFileSync("clang", [probeSource, outPath, "-lm", "-o", probe]);
3272
+ expect(execFileSync(probe, { encoding: "utf8" })).toBe("7 2\n");
3273
+ }
3274
+ finally {
3275
+ if (oldCacheDir === undefined)
3276
+ delete process.env["SCRIPTC_CACHE_DIR"];
3277
+ else
3278
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3279
+ if (oldNoCache === undefined)
3280
+ delete process.env["SCRIPTC_NO_CACHE"];
3281
+ else
3282
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
3283
+ }
3284
+ });
3285
+ test.skipIf(!nativeShardMergeAvailable)("LLVM library shards reuse unaffected program objects after a localized edit", async () => {
3286
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-program-shards-"));
3287
+ scratch.push(dir);
3288
+ const cacheRoot = join(dir, "cache");
3289
+ const cPath = join(dir, "program.ll");
3290
+ const outPath = join(dir, "program.lib.a");
3291
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3292
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3293
+ const source = (value) => `%Pair = type { i64, i64 }
3294
+ @private_value = internal global i64 ${value}
3295
+
3296
+ define internal i64 @left() {
3297
+ entry:
3298
+ %v = load i64, ptr @private_value
3299
+ ret i64 %v
3300
+ }
3301
+
3302
+ define internal i64 @right() {
3303
+ entry:
3304
+ %v = call i64 @left()
3305
+ ret i64 %v
3306
+ }
3307
+
3308
+ define i64 @scriptc_public_value() {
3309
+ entry:
3310
+ %v = call i64 @right()
3311
+ ret i64 %v
3312
+ }
3313
+ `;
3314
+ try {
3315
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
3316
+ delete process.env["SCRIPTC_NO_CACHE"];
3317
+ await mkdir(cacheRoot, { mode: 0o700 });
3318
+ const build = async (value) => {
3319
+ const programSource = source(value);
3320
+ const split = splitLlvmProgram(programSource, { minimumBytes: 0, targetBytes: 64 * 1024 });
3321
+ expect(split?.shards).toHaveLength(3);
3322
+ await writeFile(cPath, programSource);
3323
+ await compileLibArchive({
3324
+ cPath,
3325
+ programSource,
3326
+ programShards: split.shards,
3327
+ programPublicSymbols: split.publicSymbols,
3328
+ outPath,
3329
+ cacheIdentity: TEST_CACHE_IDENTITY,
3330
+ optimization: "dev",
3331
+ });
3332
+ };
3333
+ await build(7);
3334
+ const initial = (await readdir(join(cacheRoot, "program-shard")))
3335
+ .filter((name) => !name.endsWith(".sha256"));
3336
+ expect(initial).toHaveLength(3);
3337
+ await build(9);
3338
+ const after = (await readdir(join(cacheRoot, "program-shard")))
3339
+ .filter((name) => !name.endsWith(".sha256"));
3340
+ expect(after).toHaveLength(4);
3341
+ expect(initial.filter((name) => after.includes(name))).toHaveLength(3);
3342
+ const old = new Date("2000-01-01T00:00:00.000Z");
3343
+ await Promise.all(after.map((name) => utimes(join(cacheRoot, "program-shard", name), old, old)));
3344
+ await build(9);
3345
+ for (const name of after) {
3346
+ // The completed archive hit returns before shard lookup/merge; exact
3347
+ // repeats do not even touch the per-bucket object cache.
3348
+ expect((await stat(join(cacheRoot, "program-shard", name))).mtimeMs).toBe(old.getTime());
3349
+ }
3350
+ const probeSource = join(dir, "probe.c");
3351
+ const probe = join(dir, "probe");
3352
+ await writeFile(probeSource, "#include <stdio.h>\nlong long scriptc_public_value(void);\nint main(void) { printf(\"%lld\\n\", scriptc_public_value()); }\n");
3353
+ execFileSync("clang", [probeSource, outPath, "-lm", "-o", probe]);
3354
+ expect(execFileSync(probe, { encoding: "utf8" })).toBe("9\n");
3355
+ }
3356
+ finally {
3357
+ if (oldCacheDir === undefined)
3358
+ delete process.env["SCRIPTC_CACHE_DIR"];
3359
+ else
3360
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3361
+ if (oldNoCache === undefined)
3362
+ delete process.env["SCRIPTC_NO_CACHE"];
3363
+ else
3364
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
3365
+ }
3366
+ });
3367
+ test.skipIf(!nativeShardMergeAvailable)("LLVM executable dev shards reuse unaffected program objects after a localized edit", async () => {
3368
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-exe-program-shards-"));
3369
+ scratch.push(dir);
3370
+ const cacheRoot = join(dir, "cache");
3371
+ const cPath = join(dir, "program.ll");
3372
+ const outPath = join(dir, "program");
3373
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3374
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3375
+ const source = (value) => `@private_value = internal global i64 ${value}
3376
+
3377
+ define internal i64 @left() {
3378
+ entry:
3379
+ %v = load i64, ptr @private_value
3380
+ ret i64 %v
3381
+ }
3382
+
3383
+ define internal i64 @right() {
3384
+ entry:
3385
+ %v = call i64 @left()
3386
+ ret i64 %v
3387
+ }
3388
+
3389
+ define i32 @main() {
3390
+ entry:
3391
+ %v = call i64 @right()
3392
+ %exit = trunc i64 %v to i32
3393
+ ret i32 %exit
3394
+ }
3395
+ `;
3396
+ try {
3397
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
3398
+ delete process.env["SCRIPTC_NO_CACHE"];
3399
+ await mkdir(cacheRoot, { mode: 0o700 });
3400
+ const build = async (value) => {
3401
+ const programSource = source(value);
3402
+ const split = splitLlvmProgram(programSource, { minimumBytes: 0, targetBytes: 64 * 1024 });
3403
+ expect(split?.shards).toHaveLength(3);
3404
+ await writeFile(cPath, programSource);
3405
+ await compileC({
3406
+ cPath,
3407
+ programShards: split.shards,
3408
+ programPublicSymbols: split.publicSymbols,
3409
+ outPath,
3410
+ cacheIdentity: TEST_CACHE_IDENTITY,
3411
+ optimization: "dev",
3412
+ });
3413
+ };
3414
+ await build(7);
3415
+ const initial = (await readdir(join(cacheRoot, "program-shard")))
3416
+ .filter((name) => !name.endsWith(".sha256"));
3417
+ expect(initial).toHaveLength(3);
3418
+ expect(spawnSync(outPath).status).toBe(7);
3419
+ await build(9);
3420
+ const after = (await readdir(join(cacheRoot, "program-shard")))
3421
+ .filter((name) => !name.endsWith(".sha256"));
3422
+ expect(after).toHaveLength(4);
3423
+ expect(initial.filter((name) => after.includes(name))).toHaveLength(3);
3424
+ expect(spawnSync(outPath).status).toBe(9);
3425
+ const old = new Date("2000-01-01T00:00:00.000Z");
3426
+ await Promise.all(after.map((name) => utimes(join(cacheRoot, "program-shard", name), old, old)));
3427
+ await build(9);
3428
+ for (const name of after) {
3429
+ // The completed executable hit returns before shard lookup/merge.
3430
+ expect((await stat(join(cacheRoot, "program-shard", name))).mtimeMs).toBe(old.getTime());
3431
+ }
3432
+ }
3433
+ finally {
3434
+ if (oldCacheDir === undefined)
3435
+ delete process.env["SCRIPTC_CACHE_DIR"];
3436
+ else
3437
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3438
+ if (oldNoCache === undefined)
3439
+ delete process.env["SCRIPTC_NO_CACHE"];
3440
+ else
3441
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
3442
+ }
3443
+ });
3444
+ test.skipIf(process.platform === "win32" || clangExecutable === undefined ||
3445
+ ldExecutable === undefined || (process.platform === "linux" && objcopyExecutable === undefined))("LLVM executable shards fall back without publishing a shard-derived binary", async () => {
3446
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-exe-program-shard-fallback-"));
3447
+ scratch.push(dir);
3448
+ const binDir = join(dir, "bin");
3449
+ const cacheRoot = join(dir, "cache");
3450
+ const cPath = join(dir, "program.ll");
3451
+ const outPath = join(dir, "program");
3452
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3453
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3454
+ const oldPath = process.env["PATH"];
3455
+ const oldRealLd = process.env["SCRIPTC_TEST_REAL_LD"];
3456
+ const programSource = [
3457
+ "define i32 @main() {",
3458
+ "entry:",
3459
+ " ret i32 7",
3460
+ "}",
3461
+ "",
3462
+ "define internal i64 @other_value() {",
3463
+ "entry:",
3464
+ " ret i64 9",
3465
+ "}",
3466
+ "",
3467
+ ].join("\n");
3468
+ try {
3469
+ await Promise.all([mkdir(binDir), mkdir(cacheRoot, { mode: 0o700 })]);
3470
+ await Promise.all([
3471
+ symlink(clangExecutable, join(binDir, "clang")),
3472
+ ...(process.platform === "linux"
3473
+ ? [symlink(objcopyExecutable, join(binDir, "objcopy"))]
3474
+ : []),
3475
+ writeFile(join(binDir, "ld"), "#!/bin/sh\nfor arg in \"$@\"; do if [ \"$arg\" = -r ]; then exit 1; fi; done\nexec \"$SCRIPTC_TEST_REAL_LD\" \"$@\"\n"),
3476
+ writeFile(cPath, programSource),
3477
+ ]);
3478
+ await chmod(join(binDir, "ld"), 0o755);
3479
+ process.env["PATH"] = binDir;
3480
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
3481
+ process.env["SCRIPTC_TEST_REAL_LD"] = ldExecutable;
3482
+ delete process.env["SCRIPTC_NO_CACHE"];
3483
+ await compileC({
3484
+ cPath,
3485
+ programShards: [
3486
+ {
3487
+ name: "program-f000.ll",
3488
+ source: "define i32 @main() {\nentry:\n ret i32 7\n}\ndeclare hidden i64 @other_value()\n",
3489
+ },
3490
+ {
3491
+ name: "program-f001.ll",
3492
+ source: "declare i32 @main()\ndefine hidden i64 @other_value() {\nentry:\n ret i64 9\n}\n",
3493
+ },
3494
+ ],
3495
+ programPublicSymbols: ["main"],
3496
+ outPath,
3497
+ cacheIdentity: TEST_CACHE_IDENTITY,
3498
+ optimization: "dev",
3499
+ });
3500
+ expect(spawnSync(outPath).status).toBe(7);
3501
+ // The canonical retry is valid output, but cannot occupy a key describing
3502
+ // a successful shard merge. Repairing the merge tool must retry the mode.
3503
+ expect(await readdir(join(cacheRoot, "bin")).catch(() => [])).toEqual([]);
3504
+ expect(await readdir(join(cacheRoot, "program-shard")).catch(() => [])).toEqual([]);
3505
+ }
3506
+ finally {
3507
+ if (oldCacheDir === undefined)
3508
+ delete process.env["SCRIPTC_CACHE_DIR"];
3509
+ else
3510
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3511
+ if (oldNoCache === undefined)
3512
+ delete process.env["SCRIPTC_NO_CACHE"];
3513
+ else
3514
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
3515
+ if (oldPath === undefined)
3516
+ delete process.env["PATH"];
3517
+ else
3518
+ process.env["PATH"] = oldPath;
3519
+ if (oldRealLd === undefined)
3520
+ delete process.env["SCRIPTC_TEST_REAL_LD"];
3521
+ else
3522
+ process.env["SCRIPTC_TEST_REAL_LD"] = oldRealLd;
3523
+ }
3524
+ });
3525
+ test.skipIf(!nativeShardMergeAvailable)("LLVM merged-object and archive caches separate shard projections and public-symbol keep sets", async () => {
3526
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-program-shard-abi-"));
3527
+ scratch.push(dir);
3528
+ const cacheRoot = join(dir, "cache");
3529
+ const cPath = join(dir, "program.ll");
3530
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3531
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3532
+ const programSource = [
3533
+ "define i64 @foo() {",
3534
+ "entry:",
3535
+ " ret i64 1",
3536
+ "}",
3537
+ "",
3538
+ "define i64 @bar() {",
3539
+ "entry:",
3540
+ " ret i64 2",
3541
+ "}",
3542
+ "",
3543
+ ].join("\n");
3544
+ const programShards = [
3545
+ {
3546
+ name: "program-f000.ll",
3547
+ source: "define i64 @foo() {\nentry:\n ret i64 1\n}\ndeclare i64 @bar()\n",
3548
+ },
3549
+ {
3550
+ name: "program-f001.ll",
3551
+ source: "declare i64 @foo()\ndefine i64 @bar() {\nentry:\n ret i64 2\n}\n",
3552
+ },
3553
+ ];
3554
+ try {
3555
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
3556
+ delete process.env["SCRIPTC_NO_CACHE"];
3557
+ await mkdir(cacheRoot, { mode: 0o700 });
3558
+ await writeFile(cPath, programSource);
3559
+ const build = async (symbol, outPath, shards = programShards) => {
3560
+ await compileLibArchive({
3561
+ cPath,
3562
+ programSource,
3563
+ programShards: shards,
3564
+ programPublicSymbols: [symbol],
3565
+ outPath,
3566
+ cacheIdentity: TEST_CACHE_IDENTITY,
3567
+ optimization: "dev",
3568
+ });
3569
+ };
3570
+ await build("foo", join(dir, "foo.lib.a"));
3571
+ const barArchive = join(dir, "bar.lib.a");
3572
+ await build("bar", barArchive);
3573
+ expect(await completeArtifacts(cacheRoot, "lib")).toHaveLength(2);
3574
+ expect((await readdir(join(cacheRoot, "program-obj"))).filter((name) => !name.endsWith(".sha256"))).toHaveLength(2);
3575
+ const probeSource = join(dir, "probe.c");
3576
+ const probe = join(dir, "probe");
3577
+ await writeFile(probeSource, "long long bar(void);\nint main(void) { return bar() != 2; }\n");
3578
+ execFileSync("clang", [probeSource, barArchive, "-lm", "-o", probe]);
3579
+ expect(execFileSync(probe, { encoding: "utf8" })).toBe("");
3580
+ // The canonical TU and ABI stay fixed, but the exact shard projection
3581
+ // changes. The merged-object tier must not reuse the previous layout.
3582
+ await build("bar", join(dir, "bar-commented.lib.a"), [
3583
+ { ...programShards[0], source: `; projection edit\n${programShards[0].source}` },
3584
+ programShards[1],
3585
+ ]);
3586
+ expect(await completeArtifacts(cacheRoot, "lib")).toHaveLength(3);
3587
+ expect((await readdir(join(cacheRoot, "program-obj"))).filter((name) => !name.endsWith(".sha256"))).toHaveLength(3);
3588
+ }
3589
+ finally {
3590
+ if (oldCacheDir === undefined)
3591
+ delete process.env["SCRIPTC_CACHE_DIR"];
3592
+ else
3593
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3594
+ if (oldNoCache === undefined)
3595
+ delete process.env["SCRIPTC_NO_CACHE"];
3596
+ else
3597
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
3598
+ }
3599
+ });
3600
+ test.skipIf(process.platform === "win32" || clangExecutable === undefined || arExecutable === undefined ||
3601
+ ldExecutable === undefined || (process.platform === "linux" && objcopyExecutable === undefined))("LLVM merged-object caches follow in-place shard merge-tool replacements", async () => {
3602
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-program-shard-merge-tool-"));
3603
+ scratch.push(dir);
3604
+ const binDir = join(dir, "bin");
3605
+ const cacheRoot = join(dir, "cache");
3606
+ const mergeLog = join(dir, "merge.log");
3607
+ const cPath = join(dir, "program.ll");
3608
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3609
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3610
+ const oldPath = process.env["PATH"];
3611
+ const oldRealLd = process.env["SCRIPTC_TEST_REAL_LD"];
3612
+ const oldMergeLog = process.env["SCRIPTC_TEST_MERGE_LOG"];
3613
+ const programSource = [
3614
+ "define i64 @foo() {",
3615
+ "entry:",
3616
+ " ret i64 1",
3617
+ "}",
3618
+ "",
3619
+ "define i64 @bar() {",
3620
+ "entry:",
3621
+ " ret i64 2",
3622
+ "}",
3623
+ "",
3624
+ ].join("\n");
3625
+ const programShards = [
3626
+ {
3627
+ name: "program-f000.ll",
3628
+ source: "define i64 @foo() {\nentry:\n ret i64 1\n}\ndeclare i64 @bar()\n",
3629
+ },
3630
+ {
3631
+ name: "program-f001.ll",
3632
+ source: "declare i64 @foo()\ndefine i64 @bar() {\nentry:\n ret i64 2\n}\n",
3633
+ },
3634
+ ];
3635
+ const ldWrapper = (generation) => `#!/bin/sh
3636
+ printf '${generation}\\n' >> "$SCRIPTC_TEST_MERGE_LOG"
3637
+ exec "$SCRIPTC_TEST_REAL_LD" "$@"
3638
+ `;
3639
+ try {
3640
+ await Promise.all([mkdir(binDir), mkdir(cacheRoot, { mode: 0o700 })]);
3641
+ await Promise.all([
3642
+ symlink(clangExecutable, join(binDir, "clang")),
3643
+ symlink(arExecutable, join(binDir, "ar")),
3644
+ ...(process.platform === "linux"
3645
+ ? [symlink(objcopyExecutable, join(binDir, "objcopy"))]
3646
+ : []),
3647
+ writeFile(join(binDir, "ld"), ldWrapper("first")),
3648
+ writeFile(cPath, programSource),
3649
+ ]);
3650
+ await chmod(join(binDir, "ld"), 0o755);
3651
+ process.env["PATH"] = binDir;
3652
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
3653
+ process.env["SCRIPTC_TEST_REAL_LD"] = ldExecutable;
3654
+ process.env["SCRIPTC_TEST_MERGE_LOG"] = mergeLog;
3655
+ delete process.env["SCRIPTC_NO_CACHE"];
3656
+ const build = (outPath) => compileLibArchive({
3657
+ cPath,
3658
+ programSource,
3659
+ programShards,
3660
+ programPublicSymbols: ["foo", "bar"],
3661
+ outPath,
3662
+ cacheIdentity: TEST_CACHE_IDENTITY,
3663
+ optimization: "dev",
3664
+ });
3665
+ await build(join(dir, "first.lib.a"));
3666
+ await writeFile(join(binDir, "ld"), ldWrapper("second"));
3667
+ await chmod(join(binDir, "ld"), 0o755);
3668
+ await build(join(dir, "second.lib.a"));
3669
+ expect((await readFile(mergeLog, "utf8")).trim().split("\n")).toEqual(["first", "second"]);
3670
+ expect(await completeArtifacts(cacheRoot, "lib")).toHaveLength(2);
3671
+ expect((await readdir(join(cacheRoot, "program-obj"))).filter((name) => !name.endsWith(".sha256"))).toHaveLength(2);
3672
+ // The compiler and shard IR did not change; only merged outputs split by
3673
+ // localization-tool identity.
3674
+ expect((await readdir(join(cacheRoot, "program-shard"))).filter((name) => !name.endsWith(".sha256"))).toHaveLength(2);
3675
+ }
3676
+ finally {
3677
+ if (oldCacheDir === undefined)
3678
+ delete process.env["SCRIPTC_CACHE_DIR"];
3679
+ else
3680
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3681
+ if (oldNoCache === undefined)
3682
+ delete process.env["SCRIPTC_NO_CACHE"];
3683
+ else
3684
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
3685
+ if (oldPath === undefined)
3686
+ delete process.env["PATH"];
3687
+ else
3688
+ process.env["PATH"] = oldPath;
3689
+ if (oldRealLd === undefined)
3690
+ delete process.env["SCRIPTC_TEST_REAL_LD"];
3691
+ else
3692
+ process.env["SCRIPTC_TEST_REAL_LD"] = oldRealLd;
3693
+ if (oldMergeLog === undefined)
3694
+ delete process.env["SCRIPTC_TEST_MERGE_LOG"];
3695
+ else
3696
+ process.env["SCRIPTC_TEST_MERGE_LOG"] = oldMergeLog;
3697
+ }
3698
+ });
3699
+ test.skipIf(process.platform === "win32" || clangExecutable === undefined || arExecutable === undefined)("LLVM library shards fall back to the canonical TU without host merge tools", async () => {
3700
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-program-shard-fallback-"));
3701
+ scratch.push(dir);
3702
+ const binDir = join(dir, "bin");
3703
+ const cPath = join(dir, "program.ll");
3704
+ const outPath = join(dir, "program.lib.a");
3705
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3706
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3707
+ const oldPath = process.env["PATH"];
3708
+ const programSource = "define i64 @public_value() {\nentry:\n ret i64 7\n}\n";
3709
+ try {
3710
+ await mkdir(binDir);
3711
+ await Promise.all([
3712
+ symlink(clangExecutable, join(binDir, "clang")),
3713
+ symlink(arExecutable, join(binDir, "ar")),
3714
+ ]);
3715
+ process.env["PATH"] = binDir;
3716
+ process.env["SCRIPTC_NO_CACHE"] = "1";
3717
+ delete process.env["SCRIPTC_CACHE_DIR"];
3718
+ await writeFile(cPath, programSource);
3719
+ await compileLibArchive({
3720
+ cPath,
3721
+ programSource,
3722
+ // If compilation reaches either shard, this build fails. Missing ld
3723
+ // (and objcopy on Linux) must select the valid canonical TU instead.
3724
+ programShards: [
3725
+ { name: "program-f000.ll", source: "not valid LLVM IR\n" },
3726
+ { name: "program-f001.ll", source: "also not valid LLVM IR\n" },
3727
+ ],
3728
+ programPublicSymbols: ["public_value"],
3729
+ outPath,
3730
+ optimization: "dev",
3731
+ });
3732
+ expect(await readFile(outPath)).not.toHaveLength(0);
3733
+ }
3734
+ finally {
3735
+ if (oldCacheDir === undefined)
3736
+ delete process.env["SCRIPTC_CACHE_DIR"];
3737
+ else
3738
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3739
+ if (oldNoCache === undefined)
3740
+ delete process.env["SCRIPTC_NO_CACHE"];
3741
+ else
3742
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
3743
+ if (oldPath === undefined)
3744
+ delete process.env["PATH"];
3745
+ else
3746
+ process.env["PATH"] = oldPath;
3747
+ }
3748
+ });
3749
+ test.skipIf(process.platform === "win32" || clangExecutable === undefined || arExecutable === undefined ||
3750
+ (process.platform === "linux" && objcopyExecutable === undefined))("LLVM library shards fall back when an available host merge tool fails", async () => {
3751
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-program-shard-merge-failure-"));
3752
+ scratch.push(dir);
3753
+ const binDir = join(dir, "bin");
3754
+ const cacheRoot = join(dir, "cache");
3755
+ const cPath = join(dir, "program.ll");
3756
+ const outPath = join(dir, "program.lib.a");
3757
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3758
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3759
+ const oldPath = process.env["PATH"];
3760
+ const programSource = [
3761
+ "define i64 @public_value() {",
3762
+ "entry:",
3763
+ " ret i64 7",
3764
+ "}",
3765
+ "",
3766
+ "define i64 @other_value() {",
3767
+ "entry:",
3768
+ " ret i64 9",
3769
+ "}",
3770
+ "",
3771
+ ].join("\n");
3772
+ try {
3773
+ await Promise.all([mkdir(binDir), mkdir(cacheRoot, { mode: 0o700 })]);
3774
+ await Promise.all([
3775
+ symlink(clangExecutable, join(binDir, "clang")),
3776
+ symlink(arExecutable, join(binDir, "ar")),
3777
+ ...(process.platform === "linux"
3778
+ ? [symlink(objcopyExecutable, join(binDir, "objcopy"))]
3779
+ : []),
3780
+ writeFile(join(binDir, "ld"), "#!/bin/sh\nexit 1\n"),
3781
+ writeFile(cPath, programSource),
3782
+ ]);
3783
+ await chmod(join(binDir, "ld"), 0o755);
3784
+ process.env["PATH"] = binDir;
3785
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
3786
+ delete process.env["SCRIPTC_NO_CACHE"];
3787
+ await compileLibArchive({
3788
+ cPath,
3789
+ programSource,
3790
+ programShards: [
3791
+ {
3792
+ name: "program-f000.ll",
3793
+ source: "define i64 @public_value() {\nentry:\n ret i64 7\n}\ndeclare i64 @other_value()\n",
3794
+ },
3795
+ {
3796
+ name: "program-f001.ll",
3797
+ source: "declare i64 @public_value()\ndefine i64 @other_value() {\nentry:\n ret i64 9\n}\n",
3798
+ },
3799
+ ],
3800
+ programPublicSymbols: ["public_value", "other_value"],
3801
+ outPath,
3802
+ cacheIdentity: TEST_CACHE_IDENTITY,
3803
+ optimization: "dev",
3804
+ });
3805
+ const probeSource = join(dir, "probe.c");
3806
+ const probe = join(dir, "probe");
3807
+ await writeFile(probeSource, "long long public_value(void);\nint main(void) { return public_value() != 7; }\n");
3808
+ execFileSync(clangExecutable, [probeSource, outPath, "-lm", "-o", probe]);
3809
+ expect(execFileSync(probe, { encoding: "utf8" })).toBe("");
3810
+ // The canonical retry is valid for this invocation, but must not occupy a
3811
+ // key describing merged shard bytes. A repaired merge tool retries the
3812
+ // optimization instead of receiving a poisoned completed/object hit.
3813
+ expect(await readdir(join(cacheRoot, "lib")).catch(() => [])).toEqual([]);
3814
+ expect(await readdir(join(cacheRoot, "program-obj")).catch(() => [])).toEqual([]);
3815
+ expect(await readdir(join(cacheRoot, "program-shard")).catch(() => [])).toEqual([]);
3816
+ }
3817
+ finally {
3818
+ if (oldCacheDir === undefined)
3819
+ delete process.env["SCRIPTC_CACHE_DIR"];
3820
+ else
3821
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3822
+ if (oldNoCache === undefined)
3823
+ delete process.env["SCRIPTC_NO_CACHE"];
3824
+ else
3825
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
3826
+ if (oldPath === undefined)
3827
+ delete process.env["PATH"];
3828
+ else
3829
+ process.env["PATH"] = oldPath;
3830
+ }
3831
+ });
3832
+ test.skipIf(process.platform === "win32" || zigExecutable === undefined)("cross-ELF localized archives retain unreferenced identity roots", async () => {
3833
+ const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-localized-identity-"));
3834
+ scratch.push(dir);
3835
+ const cPath = join(dir, "program.c");
3836
+ const probePath = join(dir, "probe.c");
3837
+ const archivePath = join(dir, "program.lib.a");
3838
+ const probeOutput = join(dir, "probe");
3839
+ const cacheRoot = join(dir, "cache");
3840
+ const target = "x86_64-linux-gnu.2.36";
3841
+ const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
3842
+ const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
3843
+ const oldCc = process.env["SCRIPTC_CC"];
3844
+ const oldTarget = process.env["SCRIPTC_TARGET"];
3845
+ try {
3846
+ process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
3847
+ delete process.env["SCRIPTC_NO_CACHE"];
3848
+ process.env["SCRIPTC_CC"] = "zigcc";
3849
+ process.env["SCRIPTC_TARGET"] = target;
3850
+ await writeFile(cPath, "int scriptc_large_program_value(void) { return 7; }\n");
3851
+ await compileLibArchive({
3852
+ cPath,
3853
+ identityCSource: [
3854
+ "unsigned long long scriptc_build_id(void) { return 2; }",
3855
+ "unsigned scriptc_abi_version(void) { return 1; }",
3856
+ "",
3857
+ ].join("\n"),
3858
+ outPath: archivePath,
3859
+ cacheIdentity: TEST_CACHE_IDENTITY,
3860
+ localizeSymbols: [
3861
+ "scriptc_large_program_value",
3862
+ "scriptc_build_id",
3863
+ "scriptc_abi_version",
3864
+ ],
3865
+ });
3866
+ await writeFile(probePath, [
3867
+ "int scriptc_large_program_value(void);",
3868
+ "unsigned long long scriptc_build_id(void);",
3869
+ "unsigned scriptc_abi_version(void);",
3870
+ "int main(void) {",
3871
+ " return scriptc_large_program_value() != 7 || scriptc_build_id() != 2 || scriptc_abi_version() != 1;",
3872
+ "}",
3873
+ "",
3874
+ ].join("\n"));
3875
+ execFileSync(zigExecutable, [
3876
+ "cc", "-target", target, probePath, archivePath, "-lm", "-o", probeOutput,
3877
+ ]);
3878
+ }
3879
+ finally {
3880
+ if (oldCacheDir === undefined)
3881
+ delete process.env["SCRIPTC_CACHE_DIR"];
3882
+ else
3883
+ process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
3884
+ if (oldNoCache === undefined)
3885
+ delete process.env["SCRIPTC_NO_CACHE"];
3886
+ else
3887
+ process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
3888
+ if (oldCc === undefined)
3889
+ delete process.env["SCRIPTC_CC"];
3890
+ else
3891
+ process.env["SCRIPTC_CC"] = oldCc;
3892
+ if (oldTarget === undefined)
3893
+ delete process.env["SCRIPTC_TARGET"];
3894
+ else
3895
+ process.env["SCRIPTC_TARGET"] = oldTarget;
3896
+ }
3897
+ });
2814
3898
  //# sourceMappingURL=cc-cache.test.js.map