@scriptc/compiler 0.0.32 → 0.0.34
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.
- package/dist/backend/cc-cache.test.js +430 -1
- package/dist/backend/cc-cache.test.js.map +1 -1
- package/dist/backend/cc.d.ts +25 -0
- package/dist/backend/cc.js +818 -88
- package/dist/backend/cc.js.map +1 -1
- package/dist/backend/emission/emit-exprs.js +79 -3
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emit-shapes.js +37 -4
- package/dist/backend/emission/emit-shapes.js.map +1 -1
- package/dist/backend/emission/emitter.d.ts +23 -7
- package/dist/backend/emission/emitter.js +171 -27
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/emission/may-throw.js +7 -4
- package/dist/backend/emission/may-throw.js.map +1 -1
- package/dist/backend/ffi-callbacks.d.ts +38 -1
- package/dist/backend/ffi-callbacks.js +76 -3
- package/dist/backend/ffi-callbacks.js.map +1 -1
- package/dist/backend/library-identity.d.ts +22 -0
- package/dist/backend/library-identity.js +103 -0
- package/dist/backend/library-identity.js.map +1 -0
- package/dist/backend/llvm/emitter.d.ts +3 -0
- package/dist/backend/llvm/emitter.js +278 -17
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/shapes.d.ts +1 -0
- package/dist/backend/llvm/shapes.js +25 -1
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/mangle.d.ts +1 -0
- package/dist/backend/mangle.js +3 -0
- package/dist/backend/mangle.js.map +1 -1
- package/dist/backend/object-localize.d.ts +5 -5
- package/dist/backend/object-localize.js +19 -16
- package/dist/backend/object-localize.js.map +1 -1
- package/dist/backend/object-localize.test.js +37 -13
- package/dist/backend/object-localize.test.js.map +1 -1
- package/dist/diagnostics/diagnostic.js +2 -0
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/ffi/profile.d.ts +17 -5
- package/dist/ffi/profile.js +169 -22
- package/dist/ffi/profile.js.map +1 -1
- package/dist/frontend/input-tracker.d.ts +82 -0
- package/dist/frontend/input-tracker.js +380 -0
- package/dist/frontend/input-tracker.js.map +1 -0
- package/dist/frontend/input-tracker.test.d.ts +1 -0
- package/dist/frontend/input-tracker.test.js +137 -0
- package/dist/frontend/input-tracker.test.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.js +2 -8
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +1 -0
- package/dist/frontend/lowering/lower-calls.js +244 -163
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +3 -0
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.js +113 -28
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +67 -1
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +45 -26
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +1 -1
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +48 -3
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +79 -29
- package/dist/frontend/lowering/lowerer.js +429 -87
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/npm-static-rewrite.js +25 -26
- package/dist/frontend/npm-static-rewrite.js.map +1 -1
- package/dist/frontend/npm-static.js +29 -30
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/npm-static.test.d.ts +1 -0
- package/dist/frontend/npm-static.test.js +15 -0
- package/dist/frontend/npm-static.test.js.map +1 -0
- package/dist/frontend/npm.js +5 -17
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/program.js +2 -6
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/resolve.js +8 -29
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/ts7/checker.d.ts +2 -0
- package/dist/frontend/ts7/checker.js +20 -3
- package/dist/frontend/ts7/checker.js.map +1 -1
- package/dist/frontend/ts7/program.js +17 -18
- package/dist/frontend/ts7/program.js.map +1 -1
- package/dist/frontend/types.d.ts +41 -0
- package/dist/frontend/types.js +147 -8
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +275 -41
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +42 -12
- package/dist/ir/nodes.js +7 -1
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/serialize.d.ts +1 -1
- package/dist/ir/serialize.js +1 -1
- package/dist/ir/validate.js +104 -1
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/early-cache.d.ts +65 -0
- package/dist/library/early-cache.js +380 -0
- package/dist/library/early-cache.js.map +1 -0
- package/dist/library/early-cache.test.d.ts +1 -0
- package/dist/library/early-cache.test.js +528 -0
- package/dist/library/early-cache.test.js.map +1 -0
- package/dist/library/int-infer.js +14 -0
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/int-infer.test.js +21 -4
- package/dist/library/int-infer.test.js.map +1 -1
- package/dist/library/profile.d.ts +3 -0
- package/dist/library/profile.js +9 -1
- package/dist/library/profile.js.map +1 -1
- package/dist/library/semantic-source.d.ts +13 -0
- package/dist/library/semantic-source.js +257 -0
- package/dist/library/semantic-source.js.map +1 -0
- package/dist/library/semantic-source.test.d.ts +1 -0
- package/dist/library/semantic-source.test.js +113 -0
- package/dist/library/semantic-source.test.js.map +1 -0
- package/dist/library/sidecar.d.ts +6 -0
- package/dist/library/sidecar.js +11 -0
- package/dist/library/sidecar.js.map +1 -1
- package/package.json +2 -2
- package/surface-manifest.json +1 -1
package/dist/backend/cc.js
CHANGED
|
@@ -2,9 +2,9 @@ import { execFile, spawnSync } from "node:child_process";
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { constants as fsConstants, existsSync, readdirSync } from "node:fs";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
|
-
import { access, chmod, copyFile, link, mkdir, mkdtemp, readdir, readFile, realpath, rename, rm, stat, unlink, utimes, writeFile } from "node:fs/promises";
|
|
5
|
+
import { access, chmod, copyFile, link, lstat, mkdir, mkdtemp, readdir, readFile, realpath, rename, rm, stat, unlink, utimes, writeFile } from "node:fs/promises";
|
|
6
6
|
import { availableParallelism, homedir, tmpdir } from "node:os";
|
|
7
|
-
import { basename, delimiter, dirname, extname, isAbsolute, join, resolve } from "node:path";
|
|
7
|
+
import { basename, delimiter, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
8
8
|
import { promisify } from "node:util";
|
|
9
9
|
import { localizeElfObject, mergeAndLocalizeCoffObjects } from "./object-localize.js";
|
|
10
10
|
const execFileAsync = promisify(execFile);
|
|
@@ -32,7 +32,7 @@ function stableTestMemo(cache, key, probe) {
|
|
|
32
32
|
});
|
|
33
33
|
return pending;
|
|
34
34
|
}
|
|
35
|
-
const RUNTIME_SOURCES = ["scr_number.c", "scr_string.c", "scr_array.c", "scr_bytes.c", "scr_bytes_io.c", "scr_map.c", "scr_closure.c", "scr_object.c", "scr_union.c", "scr_exception.c", "scr_error.c", "scr_console.c", "scr_lib.c", "scr_path.c", "scr_url.c", "scr_json.c", "scr_async.c", "scr_child.c", "scr_cycle.c"];
|
|
35
|
+
const RUNTIME_SOURCES = ["scr_number.c", "scr_string.c", "scr_array.c", "scr_bytes.c", "scr_bytes_io.c", "scr_map.c", "scr_closure.c", "scr_ffi.c", "scr_object.c", "scr_union.c", "scr_exception.c", "scr_error.c", "scr_console.c", "scr_lib.c", "scr_path.c", "scr_url.c", "scr_json.c", "scr_async.c", "scr_child.c", "scr_cycle.c"];
|
|
36
36
|
/** The pinned quickjs-ng snapshot under packages/runtime/vendor/quickjs-ng
|
|
37
37
|
* (see vendor/README.md — update both together). Keys the archive cache so
|
|
38
38
|
* a vendor bump can never reuse a stale engine build. */
|
|
@@ -974,13 +974,14 @@ async function ensureTlsArchive(sanitize, driver, buildIdentity, cacheRoot = ven
|
|
|
974
974
|
/** The library base: the executable lane's unconditional sources minus the
|
|
975
975
|
* fiber/loop and child-process units, plus the library-mode TU. */
|
|
976
976
|
const LIB_RUNTIME_SOURCES = [
|
|
977
|
-
...RUNTIME_SOURCES.filter((f) => f !== "scr_async.c" && f !== "scr_child.c"),
|
|
977
|
+
...RUNTIME_SOURCES.filter((f) => f !== "scr_async.c" && f !== "scr_child.c" && f !== "scr_ffi.c"),
|
|
978
978
|
"scr_library.c",
|
|
979
979
|
];
|
|
980
980
|
export async function compileLibArchive(opts) {
|
|
981
981
|
const rtDir = runtimeSrcDir();
|
|
982
982
|
const driver = resolveCc();
|
|
983
983
|
const sanitize = opts.sanitize ?? false;
|
|
984
|
+
const optimization = opts.optimization ?? "release";
|
|
984
985
|
const regex = opts.regex ?? false;
|
|
985
986
|
const sources = [
|
|
986
987
|
...LIB_RUNTIME_SOURCES,
|
|
@@ -1008,7 +1009,9 @@ export async function compileLibArchive(opts) {
|
|
|
1008
1009
|
const cflags = [
|
|
1009
1010
|
"-std=c11",
|
|
1010
1011
|
...driver.targetArgs,
|
|
1011
|
-
...(sanitize
|
|
1012
|
+
...(sanitize
|
|
1013
|
+
? ["-O1", "-fsanitize=address", "-DSCR_RC_AUDIT"]
|
|
1014
|
+
: [optimization === "dev" ? "-O0" : "-O2"]),
|
|
1012
1015
|
"-fno-math-errno",
|
|
1013
1016
|
"-fno-strict-aliasing", // the emitted object model type-puns — see compileC's buildArgs
|
|
1014
1017
|
"-Wno-deprecated-declarations",
|
|
@@ -1084,24 +1087,26 @@ export async function compileLibArchive(opts) {
|
|
|
1084
1087
|
let archiverVersion = "";
|
|
1085
1088
|
let runtimeHash = "";
|
|
1086
1089
|
let programDependencyHash = "";
|
|
1087
|
-
let cachedProgramBytes =
|
|
1090
|
+
let cachedProgramBytes = opts.programSource === undefined
|
|
1091
|
+
? null
|
|
1092
|
+
: Buffer.from(opts.programSource, "utf8");
|
|
1093
|
+
const identityBytes = opts.identityCSource === undefined
|
|
1094
|
+
? null
|
|
1095
|
+
: Buffer.from(opts.identityCSource, "utf8");
|
|
1088
1096
|
if (root !== null) {
|
|
1089
1097
|
try {
|
|
1090
1098
|
const [cv, fingerprint, programBytes] = await Promise.all([
|
|
1091
1099
|
ccVersionOnce(driver.argv, toolchainEnv, true),
|
|
1092
1100
|
runtimeFingerprint(rtDir),
|
|
1093
|
-
readFile(opts.cPath),
|
|
1101
|
+
cachedProgramBytes === null ? readFile(opts.cPath) : Promise.resolve(cachedProgramBytes),
|
|
1094
1102
|
]);
|
|
1095
1103
|
compilerVersion = cv;
|
|
1096
1104
|
runtimeHash = fingerprint;
|
|
1097
1105
|
cachedProgramBytes = programBytes;
|
|
1106
|
+
programDependencyHash = await translationUnitDependencyFingerprint(driver, cflags, opts.cPath, programBytes, toolchainEnv);
|
|
1098
1107
|
if (cacheCompleteArchive) {
|
|
1099
|
-
const
|
|
1100
|
-
toolVersionOnce(arArgv, toolchainEnv, true),
|
|
1101
|
-
translationUnitDependencyFingerprint(driver, cflags, opts.cPath, programBytes, toolchainEnv),
|
|
1102
|
-
]);
|
|
1108
|
+
const av = await toolVersionOnce(arArgv, toolchainEnv, true);
|
|
1103
1109
|
archiverVersion = av;
|
|
1104
|
-
programDependencyHash = programDependencies;
|
|
1105
1110
|
const key = createHash("sha256")
|
|
1106
1111
|
// v7 adds effective compiler-wrapper invocations for the real runtime
|
|
1107
1112
|
// and program compile flavors.
|
|
@@ -1111,7 +1116,7 @@ export async function compileLibArchive(opts) {
|
|
|
1111
1116
|
.update(implicitToolchain).update("\0")
|
|
1112
1117
|
.update(runtimeCompilerInvocation).update("\0")
|
|
1113
1118
|
.update(programCompilerInvocation).update("\0")
|
|
1114
|
-
.update(
|
|
1119
|
+
.update(programDependencyHash).update("\0")
|
|
1115
1120
|
.update(opts.cacheIdentity).update("\0")
|
|
1116
1121
|
.update(driver.argv.join("\x1f")).update("\0")
|
|
1117
1122
|
.update(cv).update("\0")
|
|
@@ -1126,6 +1131,9 @@ export async function compileLibArchive(opts) {
|
|
|
1126
1131
|
.update(opts.cPath).update("\0")
|
|
1127
1132
|
.update(resolve(opts.cPath)).update("\0")
|
|
1128
1133
|
.update(programBytes)
|
|
1134
|
+
.update("\0identity\0")
|
|
1135
|
+
.update(identityBytes === null ? "<none>" : "<generated>").update("\0")
|
|
1136
|
+
.update(identityBytes ?? Buffer.alloc(0))
|
|
1129
1137
|
.digest("hex");
|
|
1130
1138
|
cachedArchive = join(root, "lib", key);
|
|
1131
1139
|
const tmpOut = privateSiblingPath(opts.outPath, "lib-hit");
|
|
@@ -1197,7 +1205,64 @@ export async function compileLibArchive(opts) {
|
|
|
1197
1205
|
: join(buildDir, `program${opts.cPath.endsWith(".ll") ? ".ll" : ".c"}`);
|
|
1198
1206
|
if (cachedProgramBytes !== null)
|
|
1199
1207
|
await writeFile(programSource, cachedProgramBytes);
|
|
1200
|
-
|
|
1208
|
+
let cachedProgramObject = null;
|
|
1209
|
+
if (root !== null && cachedProgramBytes !== null && compilerVersion !== "" &&
|
|
1210
|
+
implicitToolchain !== null && programCompilerInvocation !== null) {
|
|
1211
|
+
const programKey = createHash("sha256")
|
|
1212
|
+
.update("lib-program-obj-v1\0")
|
|
1213
|
+
.update(cacheTargetIdentity(driver)).update("\0")
|
|
1214
|
+
.update(toolchainEnv).update("\0")
|
|
1215
|
+
.update(implicitToolchain).update("\0")
|
|
1216
|
+
.update(programCompilerInvocation).update("\0")
|
|
1217
|
+
.update(opts.cacheIdentity).update("\0")
|
|
1218
|
+
.update(driver.argv.join("\x1f")).update("\0")
|
|
1219
|
+
.update(compilerVersion).update("\0")
|
|
1220
|
+
.update(runtimeHash).update("\0")
|
|
1221
|
+
.update(programDependencyHash).update("\0")
|
|
1222
|
+
.update(programCompilerArgs.join("\x1f")).update("\0")
|
|
1223
|
+
.update(opts.cPath).update("\0")
|
|
1224
|
+
.update(resolve(opts.cPath)).update("\0")
|
|
1225
|
+
.update(cachedProgramBytes)
|
|
1226
|
+
.digest("hex");
|
|
1227
|
+
cachedProgramObject = join(root, "program-obj", programKey);
|
|
1228
|
+
}
|
|
1229
|
+
const stagedProgramObject = join(buildDir, `${stem}.program.o`);
|
|
1230
|
+
let programObject;
|
|
1231
|
+
if (cachedProgramObject !== null &&
|
|
1232
|
+
await copyValidCachedFile(cachedProgramObject, stagedProgramObject)) {
|
|
1233
|
+
programObject = stagedProgramObject;
|
|
1234
|
+
}
|
|
1235
|
+
else {
|
|
1236
|
+
programObject = await compileOne(programSource, `${stem}.program.o`, cachedProgramBytes === null ? undefined : opts.cPath);
|
|
1237
|
+
if (cachedProgramObject !== null) {
|
|
1238
|
+
try {
|
|
1239
|
+
const [currentRuntime, currentImplicit, currentInvocation, currentDependencies, currentCompiler] = await Promise.all([
|
|
1240
|
+
runtimeFingerprint(rtDir),
|
|
1241
|
+
implicitToolchainFingerprint(driver, toolchainEnv),
|
|
1242
|
+
effectiveCompilerInvocationFingerprint(driver, toolchainEnv, programCompilerArgs, programSourceExtension),
|
|
1243
|
+
translationUnitDependencyFingerprint(driver, cflags, opts.cPath, cachedProgramBytes, toolchainEnv),
|
|
1244
|
+
ccVersionOnce(driver.argv, toolchainEnv, true),
|
|
1245
|
+
]);
|
|
1246
|
+
if (currentRuntime === runtimeHash &&
|
|
1247
|
+
currentImplicit === implicitToolchain &&
|
|
1248
|
+
currentInvocation === programCompilerInvocation &&
|
|
1249
|
+
currentDependencies === programDependencyHash &&
|
|
1250
|
+
currentCompiler === compilerVersion) {
|
|
1251
|
+
await publishCachedFile(programObject, cachedProgramObject);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
catch {
|
|
1255
|
+
// Best-effort: the archive build already owns a valid object.
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
const identityObject = identityBytes === null
|
|
1260
|
+
? null
|
|
1261
|
+
: await (async () => {
|
|
1262
|
+
const source = join(buildDir, "identity.c");
|
|
1263
|
+
await writeFile(source, identityBytes);
|
|
1264
|
+
return compileOne(source, `${stem}.identity.o`);
|
|
1265
|
+
})();
|
|
1201
1266
|
let runtimeObjects = null;
|
|
1202
1267
|
let cacheInputsStable = true;
|
|
1203
1268
|
let objectImplicitVerification = null;
|
|
@@ -1230,14 +1295,14 @@ export async function compileLibArchive(opts) {
|
|
|
1230
1295
|
runtimeObjects.push(...(await Promise.all(sources.slice(i, i + width).map((f) => compileOne(join(rtDir, f), f.replace(/\.c$/, ".o"))))));
|
|
1231
1296
|
}
|
|
1232
1297
|
}
|
|
1233
|
-
const objects = [programObject, ...runtimeObjects, ...lreObjects, ...zlibObjects];
|
|
1298
|
+
const objects = [programObject, ...(identityObject === null ? [] : [identityObject]), ...runtimeObjects, ...lreObjects, ...zlibObjects];
|
|
1234
1299
|
// Multi-instance library mode: the archive's one member becomes the
|
|
1235
1300
|
// combined, symbol-localized object (cached vendor/runtime objects
|
|
1236
1301
|
// are read-only inputs here — the combine step never mutates them).
|
|
1237
1302
|
const archiveMembers = opts.localizeSymbols === undefined
|
|
1238
1303
|
? objects
|
|
1239
1304
|
: [
|
|
1240
|
-
await localizeLibraryObjects(driver, arArgv, buildDir, programObject, [...runtimeObjects, ...lreObjects, ...zlibObjects], opts.localizeSymbols, stem),
|
|
1305
|
+
await localizeLibraryObjects(driver, arArgv, buildDir, [programObject, ...(identityObject === null ? [] : [identityObject])], [...runtimeObjects, ...lreObjects, ...zlibObjects], opts.localizeSymbols, stem),
|
|
1241
1306
|
];
|
|
1242
1307
|
// A cacheable or runtime-localized build owns a private archive from
|
|
1243
1308
|
// `ar` through publication. Localized archives deliberately bypass the
|
|
@@ -1317,7 +1382,8 @@ export async function compileLibArchive(opts) {
|
|
|
1317
1382
|
* shared by design. Windows embedders additionally link advapi32, iphlpapi,
|
|
1318
1383
|
* and ws2_32.
|
|
1319
1384
|
*
|
|
1320
|
-
*
|
|
1385
|
+
* The generated program and optional identity objects are mandatory roots;
|
|
1386
|
+
* support-member selection still matters. A classic archive's unused members (and their
|
|
1321
1387
|
* undefined references to units library mode excludes, like the
|
|
1322
1388
|
* fs-promises unit's fiber symbols) never reach an embedder's link. A
|
|
1323
1389
|
* blind merge of every object would carry those references into the one
|
|
@@ -1326,7 +1392,7 @@ export async function compileLibArchive(opts) {
|
|
|
1326
1392
|
* members the program object transitively needs (the COFF arm implements
|
|
1327
1393
|
* the same member semantics in process).
|
|
1328
1394
|
*
|
|
1329
|
-
* Mach-O — one host-ld64 invocation: -r merges
|
|
1395
|
+
* Mach-O — one host-ld64 invocation: -r merges roots + needed members,
|
|
1330
1396
|
* -exported_symbols_list demotes every unlisted global to
|
|
1331
1397
|
* private extern, and -r without -keep_private_externs writes
|
|
1332
1398
|
* private externs out as non-external symbols. Apple ASan's
|
|
@@ -1357,7 +1423,7 @@ export async function compileLibArchive(opts) {
|
|
|
1357
1423
|
* mirrors MSVC link.exe; zig's COFF driver refuses multi-object
|
|
1358
1424
|
* merges) and llvm-objcopy rejects symbol-scope flags for
|
|
1359
1425
|
* COFF, so no tool pairing exists to shell out to. */
|
|
1360
|
-
async function localizeLibraryObjects(driver, arArgv, buildDir,
|
|
1426
|
+
async function localizeLibraryObjects(driver, arArgv, buildDir, rootObjects, supportObjects, keepSymbols, stem) {
|
|
1361
1427
|
const platform = targetPlatform(driver);
|
|
1362
1428
|
const combined = join(buildDir, `${stem}.localized.o`);
|
|
1363
1429
|
const staging = join(buildDir, `${stem}.localize-staging.a`);
|
|
@@ -1375,10 +1441,13 @@ async function localizeLibraryObjects(driver, arArgv, buildDir, programObject, s
|
|
|
1375
1441
|
if (platform === "win32") {
|
|
1376
1442
|
// COFF has no relocatable-link tool to stage through; the member
|
|
1377
1443
|
// selection and combine+demote happen in process over the object bytes.
|
|
1378
|
-
const [
|
|
1444
|
+
const [roots, support] = await Promise.all([
|
|
1445
|
+
Promise.all(rootObjects.map((path) => readFile(path))),
|
|
1446
|
+
Promise.all(supportObjects.map((path) => readFile(path))),
|
|
1447
|
+
]);
|
|
1379
1448
|
try {
|
|
1380
|
-
await writeFile(combined, mergeAndLocalizeCoffObjects(
|
|
1381
|
-
|
|
1449
|
+
await writeFile(combined, mergeAndLocalizeCoffObjects(roots, support, new Set(keepSymbols), {
|
|
1450
|
+
roots: rootObjects.map((path) => basename(path)),
|
|
1382
1451
|
support: supportObjects.map((path) => basename(path)),
|
|
1383
1452
|
}));
|
|
1384
1453
|
}
|
|
@@ -1390,11 +1459,11 @@ async function localizeLibraryObjects(driver, arArgv, buildDir, programObject, s
|
|
|
1390
1459
|
await run([arArgv[0] ?? "ar", ...arArgv.slice(1), "rcs", staging, ...supportObjects]);
|
|
1391
1460
|
if (platform === "darwin") {
|
|
1392
1461
|
await writeFile(keepFile, keepSymbols.map((s) => `_${s}\n`).join(""));
|
|
1393
|
-
await run(["ld", "-r",
|
|
1462
|
+
await run(["ld", "-r", ...rootObjects, staging, "-o", combined, "-exported_symbols_list", keepFile]);
|
|
1394
1463
|
}
|
|
1395
1464
|
else if (platform === "linux" && driver.target === null) {
|
|
1396
1465
|
await writeFile(keepFile, keepSymbols.map((s) => `${s}\n`).join(""));
|
|
1397
|
-
await run(["ld", "-r", "--force-group-allocation",
|
|
1466
|
+
await run(["ld", "-r", "--force-group-allocation", ...rootObjects, staging, "-o", combined]);
|
|
1398
1467
|
await run(["objcopy", `--keep-global-symbols=${keepFile}`, combined]);
|
|
1399
1468
|
}
|
|
1400
1469
|
else if (platform === "linux") {
|
|
@@ -1409,7 +1478,7 @@ async function localizeLibraryObjects(driver, arArgv, buildDir, programObject, s
|
|
|
1409
1478
|
...driver.argv.slice(1),
|
|
1410
1479
|
"-target", driver.zigTarget ?? driver.target,
|
|
1411
1480
|
"-nostdlib",
|
|
1412
|
-
"-r",
|
|
1481
|
+
"-r", ...rootObjects, staging,
|
|
1413
1482
|
"-o", combined,
|
|
1414
1483
|
]);
|
|
1415
1484
|
try {
|
|
@@ -1518,6 +1587,31 @@ export function resolveBuildCacheRoot(env = process.env, platform = process.plat
|
|
|
1518
1587
|
? resolve(userHome, "Library", "Caches", "scriptc", "build")
|
|
1519
1588
|
: resolve(userHome, ".cache", "scriptc", "build");
|
|
1520
1589
|
}
|
|
1590
|
+
/** Shared persistent-cache root for compiler-level tiers. The early library
|
|
1591
|
+
* cache deliberately follows the native cache's activation and hard-disable
|
|
1592
|
+
* contract, while native compilation retains ownership of toolchain safety. */
|
|
1593
|
+
export function buildCacheRoot() {
|
|
1594
|
+
return resolveBuildCacheRoot();
|
|
1595
|
+
}
|
|
1596
|
+
/** Harden/create a compiler-level cache root using the same privacy policy as
|
|
1597
|
+
* the artifact caches. Failure disables only the optional caller's tier. */
|
|
1598
|
+
export async function prepareBuildCacheRoot(root) {
|
|
1599
|
+
if (root === null)
|
|
1600
|
+
return null;
|
|
1601
|
+
try {
|
|
1602
|
+
await ensurePrivateCacheRoot(root, process.env["SCRIPTC_CACHE_DIR"] === undefined);
|
|
1603
|
+
return root;
|
|
1604
|
+
}
|
|
1605
|
+
catch {
|
|
1606
|
+
return null;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
/** Register a successful compiler-level cache write with the shared bounded
|
|
1610
|
+
* LRU policy. */
|
|
1611
|
+
export async function pruneBuildCache(root) {
|
|
1612
|
+
if (root !== null)
|
|
1613
|
+
await pruneCache(root).catch(() => undefined);
|
|
1614
|
+
}
|
|
1521
1615
|
function cacheRootDir() {
|
|
1522
1616
|
return resolveBuildCacheRoot();
|
|
1523
1617
|
}
|
|
@@ -1602,6 +1696,15 @@ async function resolvedToolIdentity(command, env = process.env) {
|
|
|
1602
1696
|
return (await resolvedTool(command, env))?.cacheIdentity ?? null;
|
|
1603
1697
|
}
|
|
1604
1698
|
const directCompilerDriverMemos = new Map();
|
|
1699
|
+
const directCompilerSelections = new Map();
|
|
1700
|
+
function compilerDriverProbeKey(driver, environmentFingerprint) {
|
|
1701
|
+
return [
|
|
1702
|
+
environmentFingerprint,
|
|
1703
|
+
driver.argv.join("\x1f"),
|
|
1704
|
+
driver.target ?? "<native>",
|
|
1705
|
+
driver.targetArgs.join("\x1f"),
|
|
1706
|
+
].join("\0");
|
|
1707
|
+
}
|
|
1605
1708
|
/** `/usr/bin/clang` on Darwin is Apple's immutable driver shim: its `-###`
|
|
1606
1709
|
* trace names the selected Xcode/CommandLineTools clang rather than the shim
|
|
1607
1710
|
* itself. The ordinary implicit-toolchain fingerprint below captures that
|
|
@@ -1623,12 +1726,7 @@ async function compilerDriverSupportsPersistentCache(driver, environmentFingerpr
|
|
|
1623
1726
|
// wrapper. This is deliberately undocumented and test-scoped.
|
|
1624
1727
|
if (process.env["SCRIPTC_TEST_TRUST_COMPILER_WRAPPER"] === "1")
|
|
1625
1728
|
return true;
|
|
1626
|
-
const driverKey =
|
|
1627
|
-
environmentFingerprint,
|
|
1628
|
-
driver.argv.join("\x1f"),
|
|
1629
|
-
driver.target ?? "<native>",
|
|
1630
|
-
driver.targetArgs.join("\x1f"),
|
|
1631
|
-
].join("\0");
|
|
1729
|
+
const driverKey = compilerDriverProbeKey(driver, environmentFingerprint);
|
|
1632
1730
|
const compiler = driver.argv[0] ?? "clang";
|
|
1633
1731
|
const resolvedDriver = await resolvedTool(compiler);
|
|
1634
1732
|
// A prior dependency list cannot prove that name resolution is unchanged:
|
|
@@ -1669,6 +1767,8 @@ async function compilerDriverSupportsPersistentCache(driver, environmentFingerpr
|
|
|
1669
1767
|
direct = effectiveCompiler !== null &&
|
|
1670
1768
|
(effectiveCompiler.fileIdentity === resolvedDriver.fileIdentity ||
|
|
1671
1769
|
isAppleSystemClangHandoff(resolvedDriver, effectiveCompiler));
|
|
1770
|
+
if (direct)
|
|
1771
|
+
directCompilerSelections.set(driverKey, effectiveCompiler);
|
|
1672
1772
|
}
|
|
1673
1773
|
}
|
|
1674
1774
|
catch {
|
|
@@ -1678,6 +1778,8 @@ async function compilerDriverSupportsPersistentCache(driver, environmentFingerpr
|
|
|
1678
1778
|
await rm(probeDir, { recursive: true, force: true }).catch(() => undefined);
|
|
1679
1779
|
}
|
|
1680
1780
|
directCompilerDriverMemos.set(probeKey, direct);
|
|
1781
|
+
if (!direct)
|
|
1782
|
+
directCompilerSelections.delete(driverKey);
|
|
1681
1783
|
return direct;
|
|
1682
1784
|
}
|
|
1683
1785
|
/** Complete library archives may skip `ar`, so the selected archiver must be
|
|
@@ -1758,6 +1860,29 @@ async function toolVersionOnce(argv, environmentFingerprint = toolchainEnvironme
|
|
|
1758
1860
|
toolVersionFallbacks.set(spellingKey, memo);
|
|
1759
1861
|
return memo;
|
|
1760
1862
|
}
|
|
1863
|
+
/** Dependency paths discovered while computing one strict fingerprint. The
|
|
1864
|
+
* output-local cache stamp snapshots these exact files/directories after a
|
|
1865
|
+
* validated build, then can prove a later same-output no-op without spawning
|
|
1866
|
+
* clang again. The content identity closes the gap between hashing and that
|
|
1867
|
+
* metadata snapshot: a file changed in the gap cannot make new bytes ride an
|
|
1868
|
+
* old key. Keep the map bounded for long-lived corpus/test processes. */
|
|
1869
|
+
const fingerprintDependencies = new Map();
|
|
1870
|
+
function rememberFingerprintDependencies(fingerprint, paths, contentPaths = [], contentFingerprint = null) {
|
|
1871
|
+
fingerprintDependencies.set(fingerprint, {
|
|
1872
|
+
paths: [...new Set(paths)].sort(),
|
|
1873
|
+
contentPaths: [...new Set(contentPaths)].sort(),
|
|
1874
|
+
contentFingerprint,
|
|
1875
|
+
});
|
|
1876
|
+
if (fingerprintDependencies.size > 256) {
|
|
1877
|
+
const oldest = fingerprintDependencies.keys().next().value;
|
|
1878
|
+
if (oldest !== undefined)
|
|
1879
|
+
fingerprintDependencies.delete(oldest);
|
|
1880
|
+
}
|
|
1881
|
+
return fingerprint;
|
|
1882
|
+
}
|
|
1883
|
+
function fingerprintDependencyPaths(fingerprint) {
|
|
1884
|
+
return fingerprintDependencies.get(fingerprint)?.paths ?? [];
|
|
1885
|
+
}
|
|
1761
1886
|
function parseMakeDependencies(output, cwd = process.cwd()) {
|
|
1762
1887
|
const flattened = output.replace(/\\\r?\n/g, " ");
|
|
1763
1888
|
const separator = flattened.indexOf(": ");
|
|
@@ -1839,13 +1964,14 @@ async function effectiveCompilerInvocationFingerprintFresh(driver, environmentFi
|
|
|
1839
1964
|
invocation: normalizedProbeInvocation(invocation, probeDir),
|
|
1840
1965
|
dependencies,
|
|
1841
1966
|
dependencyFingerprint: await fingerprintDependencyFiles(dependencies),
|
|
1967
|
+
invocationPaths: await existingDriverTracePaths(`${invocation.stdout}\n${invocation.stderr}`, probeDir, probeDir),
|
|
1842
1968
|
};
|
|
1843
1969
|
}
|
|
1844
1970
|
finally {
|
|
1845
1971
|
await rm(probeDir, { recursive: true, force: true }).catch(() => undefined);
|
|
1846
1972
|
}
|
|
1847
1973
|
}
|
|
1848
|
-
|
|
1974
|
+
const fingerprint = createHash("sha256")
|
|
1849
1975
|
.update("effective-compiler-invocation-v2\0")
|
|
1850
1976
|
.update(environmentFingerprint)
|
|
1851
1977
|
.update("\0")
|
|
@@ -1863,6 +1989,7 @@ async function effectiveCompilerInvocationFingerprintFresh(driver, environmentFi
|
|
|
1863
1989
|
.update("\0")
|
|
1864
1990
|
.update(probe.dependencyFingerprint)
|
|
1865
1991
|
.digest("hex");
|
|
1992
|
+
return rememberFingerprintDependencies(fingerprint, [...probe.dependencies, ...probe.invocationPaths], probe.dependencies, probe.dependencyFingerprint);
|
|
1866
1993
|
}
|
|
1867
1994
|
const stableEffectiveCompilerInvocationMemos = new Map();
|
|
1868
1995
|
function effectiveCompilerInvocationFingerprint(driver, environmentFingerprint, compileArgs, sourceExtension = ".c") {
|
|
@@ -1876,17 +2003,30 @@ function effectiveCompilerInvocationFingerprint(driver, environmentFingerprint,
|
|
|
1876
2003
|
}
|
|
1877
2004
|
async function nativeSourceFiles(directory, recursive, include = (name) => name.endsWith(".c") || name.endsWith(".h")) {
|
|
1878
2005
|
const files = [];
|
|
1879
|
-
const walk = async (current) => {
|
|
2006
|
+
const walk = async (current, ancestors) => {
|
|
2007
|
+
const canonical = await realpath(current).catch(() => resolve(current));
|
|
2008
|
+
if (ancestors.has(canonical))
|
|
2009
|
+
return;
|
|
2010
|
+
const nestedAncestors = new Set(ancestors).add(canonical);
|
|
1880
2011
|
const entries = await readdir(current, { withFileTypes: true }).catch(() => []);
|
|
1881
2012
|
for (const entry of entries) {
|
|
1882
2013
|
const path = join(current, entry.name);
|
|
1883
|
-
if (entry.isDirectory() && recursive)
|
|
1884
|
-
await walk(path);
|
|
1885
|
-
|
|
2014
|
+
if (entry.isDirectory() && recursive) {
|
|
2015
|
+
await walk(path, nestedAncestors);
|
|
2016
|
+
}
|
|
2017
|
+
else if (entry.isSymbolicLink()) {
|
|
2018
|
+
const target = await stat(path).catch(() => null);
|
|
2019
|
+
if (target?.isDirectory() && recursive)
|
|
2020
|
+
await walk(path, nestedAncestors);
|
|
2021
|
+
else if (target?.isFile() && include(entry.name))
|
|
2022
|
+
files.push(path);
|
|
2023
|
+
}
|
|
2024
|
+
else if (entry.isFile() && include(entry.name)) {
|
|
1886
2025
|
files.push(path);
|
|
2026
|
+
}
|
|
1887
2027
|
}
|
|
1888
2028
|
};
|
|
1889
|
-
await walk(directory);
|
|
2029
|
+
await walk(directory, new Set());
|
|
1890
2030
|
return files.sort();
|
|
1891
2031
|
}
|
|
1892
2032
|
/** Every system-header spelling consumed by a source tree scriptc owns. The
|
|
@@ -1938,6 +2078,18 @@ async function fingerprintDependencyFiles(paths) {
|
|
|
1938
2078
|
}
|
|
1939
2079
|
return hash.digest("hex");
|
|
1940
2080
|
}
|
|
2081
|
+
async function fingerprintDependenciesStillMatch(fingerprints) {
|
|
2082
|
+
const distinct = [...new Set(fingerprints)];
|
|
2083
|
+
const identities = distinct
|
|
2084
|
+
.map((fingerprint) => fingerprintDependencies.get(fingerprint))
|
|
2085
|
+
// A restored native-metadata stamp carries and revalidates the dependency
|
|
2086
|
+
// snapshot from the process that minted this fingerprint. Only identities
|
|
2087
|
+
// computed in this process have an additional content hash to recheck here.
|
|
2088
|
+
.filter((identity) => identity !== undefined);
|
|
2089
|
+
return (await Promise.all(identities.map(async (identity) => identity.contentFingerprint === null ||
|
|
2090
|
+
await fingerprintDependencyFiles(identity.contentPaths).catch(() => null) ===
|
|
2091
|
+
identity.contentFingerprint))).every(Boolean);
|
|
2092
|
+
}
|
|
1941
2093
|
/** Exact headers selected while preprocessing the caller's translation unit.
|
|
1942
2094
|
* The shared toolchain probe covers the runtime/vendor trees, but compileC is
|
|
1943
2095
|
* also a public API: an opted-in caller can include a system or header-only SDK
|
|
@@ -1946,7 +2098,7 @@ async function fingerprintDependencyFiles(paths) {
|
|
|
1946
2098
|
* quote-include directory and compiler-visible source spelling. */
|
|
1947
2099
|
async function translationUnitDependencyFingerprintFresh(driver, cflags, sourcePath, sourceBytes, environmentFingerprint) {
|
|
1948
2100
|
if (sourcePath.endsWith(".ll")) {
|
|
1949
|
-
return createHash("sha256").update("translation-unit-dependencies-v1\0llvm-ir").digest("hex");
|
|
2101
|
+
return rememberFingerprintDependencies(createHash("sha256").update("translation-unit-dependencies-v1\0llvm-ir").digest("hex"), []);
|
|
1950
2102
|
}
|
|
1951
2103
|
const compiler = driver.argv[0] ?? "clang";
|
|
1952
2104
|
const compilerIdentity = await resolvedToolIdentity(compiler);
|
|
@@ -1983,7 +2135,7 @@ async function translationUnitDependencyFingerprintFresh(driver, cflags, sourceP
|
|
|
1983
2135
|
await rm(probeDir, { recursive: true, force: true }).catch(() => undefined);
|
|
1984
2136
|
}
|
|
1985
2137
|
}
|
|
1986
|
-
|
|
2138
|
+
const fingerprint = createHash("sha256")
|
|
1987
2139
|
.update("translation-unit-dependencies-v1\0")
|
|
1988
2140
|
.update(environmentFingerprint)
|
|
1989
2141
|
.update("\0")
|
|
@@ -1993,6 +2145,7 @@ async function translationUnitDependencyFingerprintFresh(driver, cflags, sourceP
|
|
|
1993
2145
|
.update("\0")
|
|
1994
2146
|
.update(probe.dependencyFingerprint)
|
|
1995
2147
|
.digest("hex");
|
|
2148
|
+
return rememberFingerprintDependencies(fingerprint, probe.dependencies, probe.dependencies, probe.dependencyFingerprint);
|
|
1996
2149
|
}
|
|
1997
2150
|
const stableTranslationUnitDependencyMemos = new Map();
|
|
1998
2151
|
function translationUnitDependencyFingerprint(driver, cflags, sourcePath, sourceBytes, environmentFingerprint) {
|
|
@@ -2086,10 +2239,15 @@ async function implicitToolchainFingerprintFresh(driver, environmentFingerprint)
|
|
|
2086
2239
|
compilerInvocation: normalizedProbeInvocation(compilerInvocation, probeDir),
|
|
2087
2240
|
dependencies: dependencyPaths,
|
|
2088
2241
|
dependencyFingerprint: await fingerprintDependencyFiles(dependencyPaths),
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2242
|
+
invocationPaths: await existingDriverTracePaths(`${compilerInvocation.stdout}\n${compilerInvocation.stderr}`, probeDir, probeDir),
|
|
2243
|
+
tools: await Promise.all(toolSpellings.map(async (spelling) => {
|
|
2244
|
+
const resolved = await resolvedTool(spelling);
|
|
2245
|
+
return {
|
|
2246
|
+
spelling,
|
|
2247
|
+
identity: resolved?.cacheIdentity ?? null,
|
|
2248
|
+
path: resolved?.canonicalPath ?? null,
|
|
2249
|
+
};
|
|
2250
|
+
})),
|
|
2093
2251
|
};
|
|
2094
2252
|
}
|
|
2095
2253
|
finally {
|
|
@@ -2120,7 +2278,11 @@ async function implicitToolchainFingerprintFresh(driver, environmentFingerprint)
|
|
|
2120
2278
|
.update(currentIdentity ?? tool.identity ?? "<unresolved>")
|
|
2121
2279
|
.update("\0");
|
|
2122
2280
|
}
|
|
2123
|
-
return hash.digest("hex")
|
|
2281
|
+
return rememberFingerprintDependencies(hash.digest("hex"), [
|
|
2282
|
+
...probe.dependencies,
|
|
2283
|
+
...probe.invocationPaths,
|
|
2284
|
+
...probe.tools.flatMap((tool) => tool.path === null ? [] : [tool.path]),
|
|
2285
|
+
], probe.dependencies, probe.dependencyFingerprint);
|
|
2124
2286
|
}
|
|
2125
2287
|
const stableImplicitToolchainMemos = new Map();
|
|
2126
2288
|
function implicitToolchainFingerprint(driver, environmentFingerprint) {
|
|
@@ -2156,6 +2318,51 @@ function driverTraceCandidates(line) {
|
|
|
2156
2318
|
}
|
|
2157
2319
|
return candidates;
|
|
2158
2320
|
}
|
|
2321
|
+
async function existingDriverTracePaths(output, cwd, excludedRoot) {
|
|
2322
|
+
const candidates = new Set();
|
|
2323
|
+
for (const line of output.split(/\r?\n/)) {
|
|
2324
|
+
const tokens = driverTraceCandidates(line);
|
|
2325
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
2326
|
+
const token = tokens[index];
|
|
2327
|
+
const joinedPathOption = ["-I", "-L", "-F"].find((option) => token.startsWith(option) && token.length > option.length);
|
|
2328
|
+
const equalsPathOption = ["--sysroot=", "-resource-dir="].find((option) => token.startsWith(option));
|
|
2329
|
+
const separatePathOptions = [
|
|
2330
|
+
"-I",
|
|
2331
|
+
"-L",
|
|
2332
|
+
"-F",
|
|
2333
|
+
"--sysroot",
|
|
2334
|
+
"-isysroot",
|
|
2335
|
+
"-resource-dir",
|
|
2336
|
+
"-isystem",
|
|
2337
|
+
"-iquote",
|
|
2338
|
+
"-internal-isystem",
|
|
2339
|
+
"-internal-externc-isystem",
|
|
2340
|
+
"-internal-iframework",
|
|
2341
|
+
];
|
|
2342
|
+
const optionPath = joinedPathOption !== undefined
|
|
2343
|
+
? token.slice(joinedPathOption.length)
|
|
2344
|
+
: equalsPathOption !== undefined
|
|
2345
|
+
? token.slice(equalsPathOption.length)
|
|
2346
|
+
: separatePathOptions.includes(token)
|
|
2347
|
+
? tokens[index + 1] ?? ""
|
|
2348
|
+
: token;
|
|
2349
|
+
if (!isAbsolute(optionPath))
|
|
2350
|
+
continue;
|
|
2351
|
+
const path = resolve(cwd, optionPath);
|
|
2352
|
+
if (path === excludedRoot ||
|
|
2353
|
+
path.startsWith(`${excludedRoot}/`) ||
|
|
2354
|
+
path.startsWith(`${excludedRoot}\\`)) {
|
|
2355
|
+
continue;
|
|
2356
|
+
}
|
|
2357
|
+
candidates.add(path);
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
const existing = await Promise.all([...candidates].map(async (path) => [path, await lstat(path).catch(() => null)]));
|
|
2361
|
+
return existing
|
|
2362
|
+
.filter((entry) => entry[1] !== null)
|
|
2363
|
+
.map(([path]) => path)
|
|
2364
|
+
.sort();
|
|
2365
|
+
}
|
|
2159
2366
|
export async function parseLinkTraceFiles(output, cwd, excludedRoot, driverDryRun = false) {
|
|
2160
2367
|
const files = new Set();
|
|
2161
2368
|
for (const line of output.split(/\r?\n/)) {
|
|
@@ -2241,9 +2448,15 @@ async function implicitLinkerFingerprintFresh(driver, environmentFingerprint, li
|
|
|
2241
2448
|
linkerInvocation: normalizedProbeInvocation(driverInvocation, probeDir),
|
|
2242
2449
|
dependencies,
|
|
2243
2450
|
dependencyFingerprint: await fingerprintDependencyFiles(dependencies),
|
|
2451
|
+
invocationPaths: await existingDriverTracePaths(`${driverInvocation.stdout}\n${driverInvocation.stderr}`, probeDir, probeDir),
|
|
2244
2452
|
linker: {
|
|
2245
2453
|
spelling: linkerSpelling,
|
|
2246
|
-
identity: linkerSpelling === ""
|
|
2454
|
+
identity: linkerSpelling === ""
|
|
2455
|
+
? null
|
|
2456
|
+
: (await resolvedTool(linkerSpelling))?.cacheIdentity ?? null,
|
|
2457
|
+
path: linkerSpelling === ""
|
|
2458
|
+
? null
|
|
2459
|
+
: (await resolvedTool(linkerSpelling))?.canonicalPath ?? null,
|
|
2247
2460
|
},
|
|
2248
2461
|
};
|
|
2249
2462
|
}
|
|
@@ -2254,7 +2467,7 @@ async function implicitLinkerFingerprintFresh(driver, environmentFingerprint, li
|
|
|
2254
2467
|
const linkerIdentity = probe.linker.spelling === ""
|
|
2255
2468
|
? null
|
|
2256
2469
|
: await resolvedToolIdentity(probe.linker.spelling);
|
|
2257
|
-
|
|
2470
|
+
const fingerprint = createHash("sha256")
|
|
2258
2471
|
.update("implicit-linker-v3\0")
|
|
2259
2472
|
.update(environmentFingerprint)
|
|
2260
2473
|
.update("\0")
|
|
@@ -2280,6 +2493,11 @@ async function implicitLinkerFingerprintFresh(driver, environmentFingerprint, li
|
|
|
2280
2493
|
.update("\0")
|
|
2281
2494
|
.update(linkerIdentity ?? probe.linker.identity ?? "<unresolved>")
|
|
2282
2495
|
.digest("hex");
|
|
2496
|
+
return rememberFingerprintDependencies(fingerprint, [
|
|
2497
|
+
...probe.dependencies,
|
|
2498
|
+
...probe.invocationPaths,
|
|
2499
|
+
...(probe.linker.path === null ? [] : [probe.linker.path]),
|
|
2500
|
+
], probe.dependencies, probe.dependencyFingerprint);
|
|
2283
2501
|
}
|
|
2284
2502
|
const stableImplicitLinkerMemos = new Map();
|
|
2285
2503
|
function implicitLinkerFingerprint(driver, environmentFingerprint, linkArgs, effectiveInvocationArgs, traceInvocationArgs) {
|
|
@@ -2301,21 +2519,18 @@ function ccacheAvailable() {
|
|
|
2301
2519
|
ccacheMemo ??= execFileAsync("ccache", ["--version"]).then(() => true, () => false);
|
|
2302
2520
|
return ccacheMemo;
|
|
2303
2521
|
}
|
|
2304
|
-
/** Content hash of every .c/.h in the runtime src
|
|
2522
|
+
/** Content hash of every .c/.h in the runtime src tree, the Ryū sources
|
|
2305
2523
|
* textually included by scr_number.c, and the separately-built vendor pins —
|
|
2306
2524
|
* everything a binary links that the emitted C bytes don't already cover
|
|
2307
2525
|
* (npm-embedded C rides inside the emitted C; the engine and standalone
|
|
2308
2526
|
* vendor archives are pinned by their version constants). The small source
|
|
2309
|
-
* tree is hashed on every identity calculation:
|
|
2310
|
-
*
|
|
2527
|
+
* tree is hashed on every identity calculation: recursive enumeration is
|
|
2528
|
+
* required because a newly added nested header can begin shadowing a system
|
|
2529
|
+
* include without changing any previously selected dependency, while content
|
|
2530
|
+
* hashing catches same-size edits whose timestamp was preserved by a copy/sync
|
|
2531
|
+
* tool. */
|
|
2311
2532
|
async function runtimeFingerprintFresh(rtDir) {
|
|
2312
|
-
const groups = await
|
|
2313
|
-
{ label: "runtime", dir: rtDir },
|
|
2314
|
-
{ label: "ryu", dir: join(rtDir, "..", "vendor", "ryu") },
|
|
2315
|
-
].map(async (group) => {
|
|
2316
|
-
const names = (await readdir(group.dir)).filter((n) => n.endsWith(".c") || n.endsWith(".h")).sort();
|
|
2317
|
-
return { ...group, names };
|
|
2318
|
-
}));
|
|
2533
|
+
const groups = await runtimeFingerprintInputGroups(rtDir);
|
|
2319
2534
|
const h = createHash("sha256").update(QJS_COMMIT).update(MBEDTLS_VERSION).update(ZLIB_VERSION);
|
|
2320
2535
|
for (const group of groups) {
|
|
2321
2536
|
for (const n of group.names) {
|
|
@@ -2324,6 +2539,20 @@ async function runtimeFingerprintFresh(rtDir) {
|
|
|
2324
2539
|
}
|
|
2325
2540
|
return h.digest("hex");
|
|
2326
2541
|
}
|
|
2542
|
+
async function runtimeFingerprintInputGroups(rtDir) {
|
|
2543
|
+
return Promise.all([
|
|
2544
|
+
{ label: "runtime", dir: rtDir },
|
|
2545
|
+
{ label: "ryu", dir: join(rtDir, "..", "vendor", "ryu") },
|
|
2546
|
+
].map(async (group) => {
|
|
2547
|
+
const names = (await nativeSourceFiles(group.dir, true))
|
|
2548
|
+
.map((path) => relative(group.dir, path))
|
|
2549
|
+
.sort();
|
|
2550
|
+
return { ...group, names };
|
|
2551
|
+
}));
|
|
2552
|
+
}
|
|
2553
|
+
async function runtimeFingerprintInputPaths(rtDir) {
|
|
2554
|
+
return (await runtimeFingerprintInputGroups(rtDir)).flatMap((group) => group.names.map((name) => join(group.dir, name)));
|
|
2555
|
+
}
|
|
2327
2556
|
const stableRuntimeFingerprintMemos = new Map();
|
|
2328
2557
|
export function runtimeFingerprint(rtDir) {
|
|
2329
2558
|
const key = resolve(rtDir);
|
|
@@ -2412,6 +2641,279 @@ async function publishCachedFile(source, destination) {
|
|
|
2412
2641
|
]);
|
|
2413
2642
|
}
|
|
2414
2643
|
}
|
|
2644
|
+
async function directoryTreeDigest(root, excludedPaths = []) {
|
|
2645
|
+
const hash = createHash("sha256").update("native-dependency-tree-v1\0");
|
|
2646
|
+
const visited = new Set();
|
|
2647
|
+
const excluded = excludedPaths.map((path) => resolve(path));
|
|
2648
|
+
const walk = async (directory, relative) => {
|
|
2649
|
+
const canonical = await realpath(directory);
|
|
2650
|
+
if (visited.has(canonical)) {
|
|
2651
|
+
hash.update(relative).update("\0cycle\0").update(canonical).update("\0");
|
|
2652
|
+
return;
|
|
2653
|
+
}
|
|
2654
|
+
visited.add(canonical);
|
|
2655
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
2656
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
2657
|
+
for (const entry of entries) {
|
|
2658
|
+
const path = join(directory, entry.name);
|
|
2659
|
+
const absolute = resolve(path);
|
|
2660
|
+
if (excluded.some((candidate) => absolute === candidate ||
|
|
2661
|
+
absolute.startsWith(`${candidate}/`) ||
|
|
2662
|
+
absolute.startsWith(`${candidate}\\`)))
|
|
2663
|
+
continue;
|
|
2664
|
+
const child = relative === "" ? entry.name : `${relative}/${entry.name}`;
|
|
2665
|
+
const info = await lstat(path);
|
|
2666
|
+
const kind = info.isDirectory()
|
|
2667
|
+
? "directory"
|
|
2668
|
+
: info.isFile()
|
|
2669
|
+
? "file"
|
|
2670
|
+
: info.isSymbolicLink()
|
|
2671
|
+
? "symlink"
|
|
2672
|
+
: "other";
|
|
2673
|
+
hash.update(child).update("\0").update(kind).update("\0");
|
|
2674
|
+
if (kind === "symlink") {
|
|
2675
|
+
const target = await realpath(path).catch(() => "<missing>");
|
|
2676
|
+
hash.update(target).update("\0");
|
|
2677
|
+
const targetInfo = await stat(path).catch(() => null);
|
|
2678
|
+
if (targetInfo?.isDirectory())
|
|
2679
|
+
await walk(path, child);
|
|
2680
|
+
}
|
|
2681
|
+
if (kind === "directory")
|
|
2682
|
+
await walk(path, child);
|
|
2683
|
+
}
|
|
2684
|
+
visited.delete(canonical);
|
|
2685
|
+
};
|
|
2686
|
+
await walk(root, "");
|
|
2687
|
+
return hash.digest("hex");
|
|
2688
|
+
}
|
|
2689
|
+
function localDependencyKind(info) {
|
|
2690
|
+
return info.isFile()
|
|
2691
|
+
? "file"
|
|
2692
|
+
: info.isDirectory()
|
|
2693
|
+
? "directory"
|
|
2694
|
+
: info.isSymbolicLink()
|
|
2695
|
+
? "symlink"
|
|
2696
|
+
: null;
|
|
2697
|
+
}
|
|
2698
|
+
async function snapshotLocalArtifactDependency(path, treeExclusions = null) {
|
|
2699
|
+
const info = await lstat(path);
|
|
2700
|
+
const kind = localDependencyKind(info);
|
|
2701
|
+
if (kind === null)
|
|
2702
|
+
throw new Error(`unsupported local artifact dependency: ${path}`);
|
|
2703
|
+
const dependency = {
|
|
2704
|
+
path,
|
|
2705
|
+
kind,
|
|
2706
|
+
dev: info.dev,
|
|
2707
|
+
ino: info.ino,
|
|
2708
|
+
size: info.size,
|
|
2709
|
+
mtimeMs: info.mtimeMs,
|
|
2710
|
+
ctimeMs: info.ctimeMs,
|
|
2711
|
+
};
|
|
2712
|
+
if (kind === "directory" && treeExclusions !== null) {
|
|
2713
|
+
dependency.treeExclusions = [...new Set(treeExclusions.map((entry) => resolve(entry)))].sort();
|
|
2714
|
+
dependency.treeDigest = await directoryTreeDigest(path, dependency.treeExclusions);
|
|
2715
|
+
}
|
|
2716
|
+
if (kind === "symlink") {
|
|
2717
|
+
const targetPath = await realpath(path);
|
|
2718
|
+
const target = await stat(path);
|
|
2719
|
+
const targetKind = target.isFile() ? "file" : target.isDirectory() ? "directory" : null;
|
|
2720
|
+
if (targetKind === null)
|
|
2721
|
+
throw new Error(`unsupported symlink target dependency: ${path}`);
|
|
2722
|
+
dependency.targetPath = targetPath;
|
|
2723
|
+
dependency.targetKind = targetKind;
|
|
2724
|
+
dependency.targetDev = target.dev;
|
|
2725
|
+
dependency.targetIno = target.ino;
|
|
2726
|
+
dependency.targetSize = target.size;
|
|
2727
|
+
dependency.targetMtimeMs = target.mtimeMs;
|
|
2728
|
+
dependency.targetCtimeMs = target.ctimeMs;
|
|
2729
|
+
if (targetKind === "directory" && treeExclusions !== null) {
|
|
2730
|
+
dependency.treeExclusions = [...new Set(treeExclusions.map((entry) => resolve(entry)))].sort();
|
|
2731
|
+
dependency.treeDigest = await directoryTreeDigest(path, dependency.treeExclusions);
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
return dependency;
|
|
2735
|
+
}
|
|
2736
|
+
async function snapshotLocalArtifactDependencies(dependencyPaths, recursiveDirectories = [], recursiveExclusions = []) {
|
|
2737
|
+
const recursive = new Set(recursiveDirectories.map((path) => resolve(path)));
|
|
2738
|
+
return Promise.all([...new Set(dependencyPaths)].sort().map((path) => snapshotLocalArtifactDependency(path, recursive.has(resolve(path)) ? recursiveExclusions : null)));
|
|
2739
|
+
}
|
|
2740
|
+
async function localArtifactDependenciesStillMatch(dependencies) {
|
|
2741
|
+
return (await Promise.all(dependencies.map(async (dependency) => {
|
|
2742
|
+
const current = await snapshotLocalArtifactDependency(dependency.path, dependency.treeDigest === undefined ? null : dependency.treeExclusions ?? []).catch(() => null);
|
|
2743
|
+
return current !== null && JSON.stringify(current) === JSON.stringify(dependency);
|
|
2744
|
+
}))).every(Boolean);
|
|
2745
|
+
}
|
|
2746
|
+
function nativeMetadataStampPath(root, key) {
|
|
2747
|
+
return join(root, "meta", createHash("sha256").update(key).digest("hex"));
|
|
2748
|
+
}
|
|
2749
|
+
function nativeMetadataStampIntegrity(stamp) {
|
|
2750
|
+
return createHash("sha256")
|
|
2751
|
+
.update("native-metadata-stamp-v2\0")
|
|
2752
|
+
.update(JSON.stringify(stamp))
|
|
2753
|
+
.digest("hex");
|
|
2754
|
+
}
|
|
2755
|
+
async function readNativeMetadataStamp(root, key) {
|
|
2756
|
+
try {
|
|
2757
|
+
const stamp = JSON.parse(await readFile(nativeMetadataStampPath(root, key), "utf8"));
|
|
2758
|
+
if (stamp.version !== 2 ||
|
|
2759
|
+
stamp.key !== key ||
|
|
2760
|
+
stamp.values === null ||
|
|
2761
|
+
typeof stamp.values !== "object" ||
|
|
2762
|
+
!Array.isArray(stamp.dependencies) ||
|
|
2763
|
+
!/^[0-9a-f]{64}$/.test(stamp.integrity) ||
|
|
2764
|
+
nativeMetadataStampIntegrity({
|
|
2765
|
+
version: stamp.version,
|
|
2766
|
+
key: stamp.key,
|
|
2767
|
+
values: stamp.values,
|
|
2768
|
+
dependencies: stamp.dependencies,
|
|
2769
|
+
}) !== stamp.integrity ||
|
|
2770
|
+
!(await localArtifactDependenciesStillMatch(stamp.dependencies))) {
|
|
2771
|
+
return null;
|
|
2772
|
+
}
|
|
2773
|
+
return stamp;
|
|
2774
|
+
}
|
|
2775
|
+
catch {
|
|
2776
|
+
return null;
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
async function publishNativeMetadataStamp(root, key, values, dependencyPaths, fingerprints = []) {
|
|
2780
|
+
const destination = nativeMetadataStampPath(root, key);
|
|
2781
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
2782
|
+
const dependencies = await snapshotLocalArtifactDependencies(dependencyPaths);
|
|
2783
|
+
if (!(await fingerprintDependenciesStillMatch(fingerprints))) {
|
|
2784
|
+
throw new CacheInputsChangedError();
|
|
2785
|
+
}
|
|
2786
|
+
const unsigned = { version: 2, key, values, dependencies };
|
|
2787
|
+
const stamp = {
|
|
2788
|
+
...unsigned,
|
|
2789
|
+
integrity: nativeMetadataStampIntegrity(unsigned),
|
|
2790
|
+
};
|
|
2791
|
+
const tmp = `${destination}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
2792
|
+
try {
|
|
2793
|
+
await writeFile(tmp, `${JSON.stringify(stamp)}\n`, { mode: 0o600 });
|
|
2794
|
+
await rename(tmp, destination);
|
|
2795
|
+
}
|
|
2796
|
+
finally {
|
|
2797
|
+
await rm(tmp, { force: true }).catch(() => undefined);
|
|
2798
|
+
}
|
|
2799
|
+
return stamp;
|
|
2800
|
+
}
|
|
2801
|
+
function nativeMetadataKey(kind, parts) {
|
|
2802
|
+
const hash = createHash("sha256").update(`native-metadata-${kind}-v2\0`);
|
|
2803
|
+
for (const part of parts) {
|
|
2804
|
+
hash.update(typeof part === "string" ? part : part.join("\x1f")).update("\0");
|
|
2805
|
+
}
|
|
2806
|
+
return `${kind}-${hash.digest("hex")}`;
|
|
2807
|
+
}
|
|
2808
|
+
/** The caller-visible output is itself the cheapest safe cache tier. Once a
|
|
2809
|
+
* generated TU has produced this exact binary, an unchanged rebuild need not
|
|
2810
|
+
* rediscover every SDK header and linker input merely to copy equivalent bytes
|
|
2811
|
+
* back onto the same path. This tier is deliberately narrower than the CAS:
|
|
2812
|
+
* only frontend-generated programs with no caller-owned native inputs opt in.
|
|
2813
|
+
* The generated TU bytes, every scriptc runtime source, the selected direct
|
|
2814
|
+
* compiler inode, target/options/environment, and the output path all join the
|
|
2815
|
+
* key. A digest rejects a modified/truncated output before the no-op hit. */
|
|
2816
|
+
function localArtifactIdentity(opts, driver, environmentFingerprint, compilerIdentity, runtimeHash, programBytes) {
|
|
2817
|
+
const normalizedOptions = Object.fromEntries(Object.entries(opts)
|
|
2818
|
+
.filter(([, value]) => value !== undefined)
|
|
2819
|
+
.sort(([a], [b]) => a.localeCompare(b)));
|
|
2820
|
+
return createHash("sha256")
|
|
2821
|
+
.update("local-artifact-v1\0")
|
|
2822
|
+
.update(cacheTargetIdentity(driver)).update("\0")
|
|
2823
|
+
.update(environmentFingerprint).update("\0")
|
|
2824
|
+
.update(compilerIdentity).update("\0")
|
|
2825
|
+
.update(runtimeHash).update("\0")
|
|
2826
|
+
.update(driver.argv.join("\x1f")).update("\0")
|
|
2827
|
+
.update(driver.targetArgs.join("\x1f")).update("\0")
|
|
2828
|
+
.update(driver.linkArgs.join("\x1f")).update("\0")
|
|
2829
|
+
.update(process.env["SCRIPTC_FETCH_CURL"] === "1" ? "fetch-curl" : "fetch-native").update("\0")
|
|
2830
|
+
.update(JSON.stringify(normalizedOptions)).update("\0")
|
|
2831
|
+
.update(resolve(opts.cPath)).update("\0")
|
|
2832
|
+
.update(resolve(opts.outPath)).update("\0")
|
|
2833
|
+
.update(programBytes)
|
|
2834
|
+
.digest("hex");
|
|
2835
|
+
}
|
|
2836
|
+
function localArtifactStampPath(root, outPath) {
|
|
2837
|
+
const outputKey = createHash("sha256").update(resolve(outPath)).digest("hex");
|
|
2838
|
+
return join(root, "local", outputKey);
|
|
2839
|
+
}
|
|
2840
|
+
function localArtifactStampIntegrity(stamp) {
|
|
2841
|
+
return createHash("sha256")
|
|
2842
|
+
.update("local-artifact-stamp-v2\0")
|
|
2843
|
+
.update(JSON.stringify(stamp))
|
|
2844
|
+
.digest("hex");
|
|
2845
|
+
}
|
|
2846
|
+
async function localArtifactHit(stampPath, outPath, key) {
|
|
2847
|
+
try {
|
|
2848
|
+
const stamp = JSON.parse(await readFile(stampPath, "utf8"));
|
|
2849
|
+
const output = await lstat(outPath);
|
|
2850
|
+
const expectedMode = 0o777 & ~process.umask();
|
|
2851
|
+
if (stamp.version !== 2 ||
|
|
2852
|
+
stamp.key !== key ||
|
|
2853
|
+
!/^[0-9a-f]{64}$/.test(stamp.digest ?? "") ||
|
|
2854
|
+
!Array.isArray(stamp.dependencies) ||
|
|
2855
|
+
!/^[0-9a-f]{64}$/.test(stamp.integrity ?? "") ||
|
|
2856
|
+
localArtifactStampIntegrity({
|
|
2857
|
+
version: stamp.version,
|
|
2858
|
+
key: stamp.key,
|
|
2859
|
+
digest: stamp.digest,
|
|
2860
|
+
dependencies: stamp.dependencies,
|
|
2861
|
+
}) !== stamp.integrity ||
|
|
2862
|
+
!output.isFile() ||
|
|
2863
|
+
(output.mode & 0o777) !== expectedMode ||
|
|
2864
|
+
stamp.dependencies.some((dependency) => dependency === null ||
|
|
2865
|
+
typeof dependency !== "object" ||
|
|
2866
|
+
typeof dependency.path !== "string" ||
|
|
2867
|
+
dependency.kind !== "file" &&
|
|
2868
|
+
dependency.kind !== "directory" &&
|
|
2869
|
+
dependency.kind !== "symlink" ||
|
|
2870
|
+
typeof dependency.dev !== "number" ||
|
|
2871
|
+
typeof dependency.ino !== "number" ||
|
|
2872
|
+
typeof dependency.size !== "number" ||
|
|
2873
|
+
typeof dependency.mtimeMs !== "number" ||
|
|
2874
|
+
typeof dependency.ctimeMs !== "number" ||
|
|
2875
|
+
dependency.treeDigest !== undefined && typeof dependency.treeDigest !== "string" ||
|
|
2876
|
+
dependency.kind === "symlink" && (typeof dependency.targetPath !== "string" ||
|
|
2877
|
+
dependency.targetKind !== "file" && dependency.targetKind !== "directory" ||
|
|
2878
|
+
typeof dependency.targetDev !== "number" ||
|
|
2879
|
+
typeof dependency.targetIno !== "number" ||
|
|
2880
|
+
typeof dependency.targetSize !== "number" ||
|
|
2881
|
+
typeof dependency.targetMtimeMs !== "number" ||
|
|
2882
|
+
typeof dependency.targetCtimeMs !== "number")) ||
|
|
2883
|
+
!(await localArtifactDependenciesStillMatch(stamp.dependencies)) ||
|
|
2884
|
+
await fileDigest(outPath) !== stamp.digest) {
|
|
2885
|
+
return false;
|
|
2886
|
+
}
|
|
2887
|
+
const now = new Date();
|
|
2888
|
+
await utimes(stampPath, now, now).catch(() => undefined);
|
|
2889
|
+
return true;
|
|
2890
|
+
}
|
|
2891
|
+
catch {
|
|
2892
|
+
return false;
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
async function publishLocalArtifactStamp(stampPath, outPath, key, dependencyPaths, recursiveDirectories = [], recursiveExclusions = []) {
|
|
2896
|
+
await mkdir(dirname(stampPath), { recursive: true });
|
|
2897
|
+
const tmp = `${stampPath}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
2898
|
+
try {
|
|
2899
|
+
const dependencies = await snapshotLocalArtifactDependencies(dependencyPaths, recursiveDirectories, recursiveExclusions);
|
|
2900
|
+
const unsigned = {
|
|
2901
|
+
version: 2,
|
|
2902
|
+
key,
|
|
2903
|
+
digest: await fileDigest(outPath),
|
|
2904
|
+
dependencies,
|
|
2905
|
+
};
|
|
2906
|
+
const stamp = {
|
|
2907
|
+
...unsigned,
|
|
2908
|
+
integrity: localArtifactStampIntegrity(unsigned),
|
|
2909
|
+
};
|
|
2910
|
+
await writeFile(tmp, `${JSON.stringify(stamp)}\n`, { mode: 0o600 });
|
|
2911
|
+
await rename(tmp, stampPath);
|
|
2912
|
+
}
|
|
2913
|
+
finally {
|
|
2914
|
+
await rm(tmp, { force: true }).catch(() => undefined);
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2415
2917
|
/** The cached .o set for one flag flavor, compiled on first need. Concurrent
|
|
2416
2918
|
* first builds (parallel test workers on a cold cache) may duplicate work;
|
|
2417
2919
|
* per-file atomic renames make every winner equivalent. Publication is held
|
|
@@ -2445,7 +2947,22 @@ async function ensureRuntimeObjects(root, ccArgv, cflags, sources, keyPrefix, ve
|
|
|
2445
2947
|
await Promise.all(missing.slice(i, i + width).map(async (src) => {
|
|
2446
2948
|
const tmpObj = join(tmpDir, `${basename(src, ".c")}.o`);
|
|
2447
2949
|
const argv = [...(useCcache ? ["ccache"] : []), ...ccArgv, ...cflags, "-c", src, "-o", tmpObj];
|
|
2448
|
-
await execFileAsync(argv[0] ?? "clang", argv.slice(1)
|
|
2950
|
+
await execFileAsync(argv[0] ?? "clang", argv.slice(1), useCcache
|
|
2951
|
+
? {
|
|
2952
|
+
// ccache direct mode remembers only the headers selected by
|
|
2953
|
+
// its previous manifest and can miss a newly created,
|
|
2954
|
+
// higher-priority header. The scriptc object-set key already
|
|
2955
|
+
// includes the recursive runtime namespace fingerprint, so
|
|
2956
|
+
// carry it into ccache's own keyspace as well.
|
|
2957
|
+
env: {
|
|
2958
|
+
...process.env,
|
|
2959
|
+
CCACHE_NAMESPACE: [
|
|
2960
|
+
process.env["CCACHE_NAMESPACE"],
|
|
2961
|
+
`scriptc-${setKey}`,
|
|
2962
|
+
].filter((value) => value !== undefined && value !== "").join(":"),
|
|
2963
|
+
},
|
|
2964
|
+
}
|
|
2965
|
+
: undefined);
|
|
2449
2966
|
compiled.set(src, { object: tmpObj, digest: await fileDigest(tmpObj) });
|
|
2450
2967
|
}));
|
|
2451
2968
|
}
|
|
@@ -2657,10 +3174,88 @@ export async function compileC(opts) {
|
|
|
2657
3174
|
root = null;
|
|
2658
3175
|
}
|
|
2659
3176
|
}
|
|
3177
|
+
let localArtifact = null;
|
|
3178
|
+
// Generated executable TUs are closed over scriptc's own runtime tree. A
|
|
3179
|
+
// same-output rebuild can therefore check those bytes directly before the
|
|
3180
|
+
// broader cross-output CAS performs its compiler/SDK/linker rediscovery.
|
|
3181
|
+
// FFI/native-input builds and the public arbitrary-C cache API stay on the
|
|
3182
|
+
// strict path because their dependency graphs are caller-owned.
|
|
3183
|
+
if (root !== null &&
|
|
3184
|
+
cachePolicy.completeArtifacts &&
|
|
3185
|
+
opts.cacheIdentity === "scriptc-generated-v1" &&
|
|
3186
|
+
(opts.linkInputs?.length ?? 0) === 0 &&
|
|
3187
|
+
(opts.systemLibraries?.length ?? 0) === 0 &&
|
|
3188
|
+
process.env["SCRIPTC_TEST_TRUST_COMPILER_WRAPPER"] !== "1") {
|
|
3189
|
+
try {
|
|
3190
|
+
const [compiler, runtimeHash, programBytes] = await Promise.all([
|
|
3191
|
+
resolvedTool(driver.argv[0] ?? "clang"),
|
|
3192
|
+
runtimeFingerprint(rtDir),
|
|
3193
|
+
readFile(opts.cPath),
|
|
3194
|
+
]);
|
|
3195
|
+
if (compiler !== null) {
|
|
3196
|
+
const effectiveCompiler = directCompilerSelections.get(compilerDriverProbeKey(driver, toolchainEnv)) ?? compiler;
|
|
3197
|
+
const key = localArtifactIdentity(opts, driver, toolchainEnv, `${compiler.cacheIdentity}\0${effectiveCompiler.cacheIdentity}`, runtimeHash, programBytes);
|
|
3198
|
+
const stampPath = localArtifactStampPath(root, opts.outPath);
|
|
3199
|
+
localArtifact = {
|
|
3200
|
+
stampPath,
|
|
3201
|
+
key,
|
|
3202
|
+
runtimeHash,
|
|
3203
|
+
programBytes,
|
|
3204
|
+
compilerPath: effectiveCompiler.canonicalPath,
|
|
3205
|
+
};
|
|
3206
|
+
if (await localArtifactHit(stampPath, opts.outPath, key))
|
|
3207
|
+
return;
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
catch {
|
|
3211
|
+
// The output-local tier is only an optimization; the fully validated
|
|
3212
|
+
// CAS below remains the source of truth on any metadata trouble.
|
|
3213
|
+
localArtifact = null;
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
2660
3216
|
let implicitToolchain = null;
|
|
3217
|
+
let toolchainMetadataStamp = null;
|
|
3218
|
+
const metadataCompiler = root === null
|
|
3219
|
+
? null
|
|
3220
|
+
: await resolvedTool(driver.argv[0] ?? "clang");
|
|
3221
|
+
const metadataEffectiveCompiler = directCompilerSelections.get(compilerDriverProbeKey(driver, toolchainEnv)) ?? metadataCompiler;
|
|
3222
|
+
const toolchainMetadataKey = root === null ||
|
|
3223
|
+
metadataCompiler === null ||
|
|
3224
|
+
metadataEffectiveCompiler === null ||
|
|
3225
|
+
process.env["SCRIPTC_TEST_TRUST_COMPILER_WRAPPER"] === "1"
|
|
3226
|
+
? null
|
|
3227
|
+
: nativeMetadataKey("toolchain", [
|
|
3228
|
+
cacheTargetIdentity(driver),
|
|
3229
|
+
toolchainEnv,
|
|
3230
|
+
driver.argv,
|
|
3231
|
+
driver.targetArgs,
|
|
3232
|
+
metadataCompiler.cacheIdentity,
|
|
3233
|
+
metadataEffectiveCompiler.cacheIdentity,
|
|
3234
|
+
rtDir,
|
|
3235
|
+
]);
|
|
2661
3236
|
if (persistentDriverCache) {
|
|
2662
3237
|
try {
|
|
2663
|
-
|
|
3238
|
+
toolchainMetadataStamp =
|
|
3239
|
+
root === null || toolchainMetadataKey === null
|
|
3240
|
+
? null
|
|
3241
|
+
: await readNativeMetadataStamp(root, toolchainMetadataKey);
|
|
3242
|
+
implicitToolchain = toolchainMetadataStamp?.values["implicitToolchain"] ??
|
|
3243
|
+
await implicitToolchainFingerprint(driver, toolchainEnv);
|
|
3244
|
+
let compilerVersion = toolchainMetadataStamp?.values["compilerVersion"];
|
|
3245
|
+
if (compilerVersion === undefined) {
|
|
3246
|
+
compilerVersion = await ccVersionOnce(driver.argv, toolchainEnv, true);
|
|
3247
|
+
}
|
|
3248
|
+
if (toolchainMetadataStamp === null &&
|
|
3249
|
+
root !== null &&
|
|
3250
|
+
toolchainMetadataKey !== null) {
|
|
3251
|
+
if (metadataCompiler !== null && metadataEffectiveCompiler !== null) {
|
|
3252
|
+
toolchainMetadataStamp = await publishNativeMetadataStamp(root, toolchainMetadataKey, { implicitToolchain, compilerVersion }, [
|
|
3253
|
+
metadataCompiler.canonicalPath,
|
|
3254
|
+
metadataEffectiveCompiler.canonicalPath,
|
|
3255
|
+
...fingerprintDependencyPaths(implicitToolchain),
|
|
3256
|
+
], [implicitToolchain]);
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
2664
3259
|
}
|
|
2665
3260
|
catch {
|
|
2666
3261
|
// Cache discovery is best-effort. In particular, a compiler wrapper can
|
|
@@ -2817,6 +3412,7 @@ export async function compileC(opts) {
|
|
|
2817
3412
|
...(opts.http2 ?? false ? [rt(join(rtDir, "scr_http2.c"))] : []),
|
|
2818
3413
|
...(opts.dgram ? [rt(join(rtDir, "scr_dgram.c"))] : []),
|
|
2819
3414
|
...(opts.watch ? [rt(join(rtDir, "scr_watch.c"))] : []),
|
|
3415
|
+
...(opts.foreignFfi ? [rt(join(rtDir, "scr_ffi_queue.c"))] : []),
|
|
2820
3416
|
...(opts.nodeTest ? [rt(join(rtDir, "scr_test.c"))] : []),
|
|
2821
3417
|
// The CA-store unit rides its own gate OR the tls one: scr_tls.c
|
|
2822
3418
|
// references its default-set override unconditionally.
|
|
@@ -2959,12 +3555,63 @@ export async function compileC(opts) {
|
|
|
2959
3555
|
};
|
|
2960
3556
|
let runtimeCompilerInvocation = null;
|
|
2961
3557
|
let programCompilerInvocation = null;
|
|
3558
|
+
let payloadMetadata = null;
|
|
3559
|
+
let compileMetadataStamp = null;
|
|
3560
|
+
const compileMetadataKey = root === null || process.env["SCRIPTC_TEST_TRUST_COMPILER_WRAPPER"] === "1"
|
|
3561
|
+
? null
|
|
3562
|
+
: nativeMetadataKey("compile", [
|
|
3563
|
+
cacheTargetIdentity(driver),
|
|
3564
|
+
toolchainEnv,
|
|
3565
|
+
implicitToolchain ?? "<uncached>",
|
|
3566
|
+
driver.argv,
|
|
3567
|
+
cflags,
|
|
3568
|
+
programCompilerArgs,
|
|
3569
|
+
programSourceExtension,
|
|
3570
|
+
]);
|
|
2962
3571
|
if (root !== null) {
|
|
2963
3572
|
try {
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
3573
|
+
// These probes inspect disjoint inputs. Start the payload reads here as
|
|
3574
|
+
// well so runtime hashing and clang's dry-run traces overlap instead of
|
|
3575
|
+
// forming a serial prelude before every cache lookup.
|
|
3576
|
+
payloadMetadata = Promise.all([
|
|
3577
|
+
Promise.resolve(toolchainMetadataStamp?.values["compilerVersion"] ??
|
|
3578
|
+
ccVersionOnce(driver.argv, toolchainEnv, true)),
|
|
3579
|
+
localArtifact === null
|
|
3580
|
+
? runtimeFingerprint(rtDir)
|
|
3581
|
+
: Promise.resolve(localArtifact.runtimeHash),
|
|
3582
|
+
localArtifact === null
|
|
3583
|
+
? readFile(opts.cPath)
|
|
3584
|
+
: Promise.resolve(localArtifact.programBytes),
|
|
3585
|
+
]);
|
|
3586
|
+
compileMetadataStamp = compileMetadataKey === null
|
|
3587
|
+
? null
|
|
3588
|
+
: await readNativeMetadataStamp(root, compileMetadataKey);
|
|
3589
|
+
if (compileMetadataStamp !== null) {
|
|
3590
|
+
runtimeCompilerInvocation = compileMetadataStamp.values["runtimeInvocation"] ?? null;
|
|
3591
|
+
programCompilerInvocation = compileMetadataStamp.values["programInvocation"] ?? null;
|
|
3592
|
+
if (runtimeCompilerInvocation === null || programCompilerInvocation === null) {
|
|
3593
|
+
compileMetadataStamp = null;
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
if (compileMetadataStamp === null) {
|
|
3597
|
+
const [runtimeInvocation, programInvocation] = await Promise.all([
|
|
3598
|
+
effectiveCompilerInvocationFingerprint(driver, toolchainEnv, cflags),
|
|
3599
|
+
programSourceExtension === ".ll"
|
|
3600
|
+
? effectiveCompilerInvocationFingerprint(driver, toolchainEnv, programCompilerArgs, programSourceExtension)
|
|
3601
|
+
: Promise.resolve(null),
|
|
3602
|
+
]);
|
|
3603
|
+
runtimeCompilerInvocation = runtimeInvocation;
|
|
3604
|
+
programCompilerInvocation = programInvocation ?? runtimeInvocation;
|
|
3605
|
+
if (compileMetadataKey !== null) {
|
|
3606
|
+
compileMetadataStamp = await publishNativeMetadataStamp(root, compileMetadataKey, {
|
|
3607
|
+
runtimeInvocation: runtimeCompilerInvocation,
|
|
3608
|
+
programInvocation: programCompilerInvocation,
|
|
3609
|
+
}, [
|
|
3610
|
+
...fingerprintDependencyPaths(runtimeCompilerInvocation),
|
|
3611
|
+
...fingerprintDependencyPaths(programCompilerInvocation),
|
|
3612
|
+
], [runtimeCompilerInvocation, programCompilerInvocation]);
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
2968
3615
|
}
|
|
2969
3616
|
catch {
|
|
2970
3617
|
// Preserve the uncached build for wrappers that compile successfully but
|
|
@@ -2989,11 +3636,11 @@ export async function compileC(opts) {
|
|
|
2989
3636
|
let fingerprint;
|
|
2990
3637
|
let cBytes;
|
|
2991
3638
|
try {
|
|
2992
|
-
[cv, fingerprint, cBytes] = await Promise.all([
|
|
3639
|
+
[cv, fingerprint, cBytes] = await (payloadMetadata ?? Promise.all([
|
|
2993
3640
|
ccVersionOnce(driver.argv, toolchainEnv, true),
|
|
2994
3641
|
runtimeFingerprint(rtDir),
|
|
2995
3642
|
readFile(opts.cPath),
|
|
2996
|
-
]);
|
|
3643
|
+
]));
|
|
2997
3644
|
}
|
|
2998
3645
|
catch {
|
|
2999
3646
|
// A version/fingerprint probe is an optimization boundary. If the compiler
|
|
@@ -3012,16 +3659,6 @@ export async function compileC(opts) {
|
|
|
3012
3659
|
(opts.linkInputs?.length ?? 0) === 0 &&
|
|
3013
3660
|
(opts.systemLibraries?.length ?? 0) === 0;
|
|
3014
3661
|
let programDependencies = null;
|
|
3015
|
-
if (cacheCompleteArtifact) {
|
|
3016
|
-
try {
|
|
3017
|
-
programDependencies = await translationUnitDependencyFingerprint(driver, cflags, opts.cPath, cBytes, toolchainEnv);
|
|
3018
|
-
}
|
|
3019
|
-
catch {
|
|
3020
|
-
// Program-header discovery is needed only for a complete hit. Runtime
|
|
3021
|
-
// objects remain safe because they never contain the caller's TU.
|
|
3022
|
-
cacheCompleteArtifact = false;
|
|
3023
|
-
}
|
|
3024
|
-
}
|
|
3025
3662
|
const linkProbeArgs = [
|
|
3026
3663
|
...(sanitize ? ["-fsanitize=address"] : []),
|
|
3027
3664
|
...threadArgs,
|
|
@@ -3062,15 +3699,89 @@ export async function compileC(opts) {
|
|
|
3062
3699
|
? effectiveLinkInvocationArgs
|
|
3063
3700
|
: effectiveLinkInvocationArgs.filter((arg) => arg !== `-L${curlStubDir}`);
|
|
3064
3701
|
let implicitLinker = null;
|
|
3702
|
+
let preBuildDependencies = null;
|
|
3703
|
+
let localArtifactDependencyPaths = null;
|
|
3704
|
+
let linkMetadataStamp = null;
|
|
3705
|
+
const linkMetadataKey = root === null || process.env["SCRIPTC_TEST_TRUST_COMPILER_WRAPPER"] === "1"
|
|
3706
|
+
? null
|
|
3707
|
+
: nativeMetadataKey("link", [
|
|
3708
|
+
cacheTargetIdentity(driver),
|
|
3709
|
+
toolchainEnv,
|
|
3710
|
+
implicitToolchain ?? "<uncached>",
|
|
3711
|
+
runtimeCompilerInvocation ?? "<uncached>",
|
|
3712
|
+
programCompilerInvocation ?? "<uncached>",
|
|
3713
|
+
driver.argv,
|
|
3714
|
+
linkProbeArgs,
|
|
3715
|
+
effectiveLinkInvocationArgs,
|
|
3716
|
+
linkTraceInvocationArgs,
|
|
3717
|
+
]);
|
|
3065
3718
|
if (cacheCompleteArtifact) {
|
|
3066
3719
|
try {
|
|
3067
|
-
|
|
3720
|
+
// Header discovery and linker tracing are independent subprocess trees.
|
|
3721
|
+
// Running them together removes one complete probe round-trip from both
|
|
3722
|
+
// cache hits and ordinary edit/build misses without changing either key.
|
|
3723
|
+
linkMetadataStamp = linkMetadataKey === null
|
|
3724
|
+
? null
|
|
3725
|
+
: await readNativeMetadataStamp(root, linkMetadataKey);
|
|
3726
|
+
if (linkMetadataStamp !== null) {
|
|
3727
|
+
implicitLinker = linkMetadataStamp.values["implicitLinker"] ?? null;
|
|
3728
|
+
if (implicitLinker === null)
|
|
3729
|
+
linkMetadataStamp = null;
|
|
3730
|
+
}
|
|
3731
|
+
[programDependencies, implicitLinker] = await Promise.all([
|
|
3732
|
+
translationUnitDependencyFingerprint(driver, cflags, opts.cPath, cBytes, toolchainEnv),
|
|
3733
|
+
linkMetadataStamp === null
|
|
3734
|
+
? implicitLinkerFingerprint(driver, toolchainEnv, linkProbeArgs, effectiveLinkInvocationArgs, linkTraceInvocationArgs)
|
|
3735
|
+
: Promise.resolve(implicitLinker),
|
|
3736
|
+
]);
|
|
3737
|
+
if (linkMetadataStamp === null && linkMetadataKey !== null) {
|
|
3738
|
+
linkMetadataStamp = await publishNativeMetadataStamp(root, linkMetadataKey, { implicitLinker }, fingerprintDependencyPaths(implicitLinker), [implicitLinker]);
|
|
3739
|
+
}
|
|
3740
|
+
preBuildDependencies = await snapshotLocalArtifactDependencies([
|
|
3741
|
+
...(await runtimeFingerprintInputPaths(rtDir)),
|
|
3742
|
+
...(toolchainMetadataStamp?.dependencies.map((dependency) => dependency.path) ??
|
|
3743
|
+
fingerprintDependencyPaths(implicitToolchain)),
|
|
3744
|
+
...(compileMetadataStamp?.dependencies.map((dependency) => dependency.path) ?? []),
|
|
3745
|
+
...(linkMetadataStamp?.dependencies.map((dependency) => dependency.path) ??
|
|
3746
|
+
fingerprintDependencyPaths(implicitLinker)),
|
|
3747
|
+
...fingerprintDependencyPaths(programDependencies),
|
|
3748
|
+
]);
|
|
3749
|
+
// Every content-bearing fingerprint was computed before this metadata
|
|
3750
|
+
// snapshot. Re-read those exact files now so the snapshot cannot certify
|
|
3751
|
+
// bytes that changed after hashing but before the final compile starts.
|
|
3752
|
+
if (!(await fingerprintDependenciesStillMatch([
|
|
3753
|
+
implicitToolchain,
|
|
3754
|
+
runtimeCompilerInvocation,
|
|
3755
|
+
programCompilerInvocation,
|
|
3756
|
+
implicitLinker,
|
|
3757
|
+
programDependencies,
|
|
3758
|
+
])) ||
|
|
3759
|
+
await runtimeFingerprint(rtDir).catch(() => null) !== fingerprint ||
|
|
3760
|
+
!(await Promise.all([toolchainMetadataStamp, compileMetadataStamp, linkMetadataStamp]
|
|
3761
|
+
.filter((stamp) => stamp !== null)
|
|
3762
|
+
.map((stamp) => localArtifactDependenciesStillMatch(stamp.dependencies)))).every(Boolean)) {
|
|
3763
|
+
throw new CacheInputsChangedError();
|
|
3764
|
+
}
|
|
3765
|
+
if (localArtifact !== null) {
|
|
3766
|
+
// Native metadata stamps persist their dependency paths across CLI
|
|
3767
|
+
// processes; fingerprintDependencyPaths deliberately does not. Build
|
|
3768
|
+
// the output-local stamp from this complete validated snapshot so a
|
|
3769
|
+
// source edit in a fresh process cannot replace it with only the two
|
|
3770
|
+
// process-local fallback paths.
|
|
3771
|
+
localArtifactDependencyPaths = [
|
|
3772
|
+
localArtifact.compilerPath,
|
|
3773
|
+
dirname(resolve(opts.cPath)),
|
|
3774
|
+
...preBuildDependencies.map((dependency) => dependency.path),
|
|
3775
|
+
];
|
|
3776
|
+
}
|
|
3068
3777
|
}
|
|
3069
3778
|
catch {
|
|
3070
|
-
//
|
|
3071
|
-
// objects remain safely cacheable
|
|
3072
|
-
//
|
|
3779
|
+
// Program-header discovery and linker tracing are both required for a
|
|
3780
|
+
// complete hit. Runtime objects remain safely cacheable if either probe
|
|
3781
|
+
// is unavailable.
|
|
3073
3782
|
cacheCompleteArtifact = false;
|
|
3783
|
+
preBuildDependencies = null;
|
|
3784
|
+
localArtifactDependencyPaths = null;
|
|
3074
3785
|
}
|
|
3075
3786
|
}
|
|
3076
3787
|
const binDir = join(root, "bin");
|
|
@@ -3123,6 +3834,9 @@ export async function compileC(opts) {
|
|
|
3123
3834
|
// cache entry populated by a less restrictive shell must not widen access.
|
|
3124
3835
|
await chmod(tmpOut, 0o777 & ~process.umask());
|
|
3125
3836
|
await rename(tmpOut, opts.outPath);
|
|
3837
|
+
if (localArtifact !== null && localArtifactDependencyPaths !== null) {
|
|
3838
|
+
await publishLocalArtifactStamp(localArtifact.stampPath, opts.outPath, localArtifact.key, localArtifactDependencyPaths, [dirname(resolve(opts.cPath))], [root]).catch(() => undefined);
|
|
3839
|
+
}
|
|
3126
3840
|
return; // hit: the program/runtime payload compile and link were skipped
|
|
3127
3841
|
}
|
|
3128
3842
|
catch {
|
|
@@ -3158,18 +3872,25 @@ export async function compileC(opts) {
|
|
|
3158
3872
|
await writeFile(programPath, cBytes);
|
|
3159
3873
|
let objects = null;
|
|
3160
3874
|
let cacheInputsStable = true;
|
|
3161
|
-
let
|
|
3875
|
+
let strictObjectVerification = null;
|
|
3162
3876
|
const objectImplicitToolchainStillMatches = () => {
|
|
3163
|
-
|
|
3877
|
+
if (preBuildDependencies !== null) {
|
|
3878
|
+
return localArtifactDependenciesStillMatch(preBuildDependencies);
|
|
3879
|
+
}
|
|
3880
|
+
// Complete-artifact caching can be disabled by caller-owned native
|
|
3881
|
+
// inputs while the safe runtime-object tier remains active. Preserve its
|
|
3882
|
+
// strict discovery fallback in that posture.
|
|
3883
|
+
strictObjectVerification ??= Promise.all([
|
|
3884
|
+
runtimeFingerprint(rtDir),
|
|
3164
3885
|
implicitToolchainFingerprint(driver, toolchainEnv),
|
|
3165
3886
|
effectiveCompilerInvocationFingerprint(driver, toolchainEnv, cflags),
|
|
3166
|
-
]).then(([currentImplicit, currentInvocation]) =>
|
|
3887
|
+
]).then(([currentRuntime, currentImplicit, currentInvocation]) => currentRuntime === fingerprint &&
|
|
3888
|
+
currentImplicit === implicitToolchain &&
|
|
3167
3889
|
currentInvocation === runtimeCompilerInvocation, () => false);
|
|
3168
|
-
return
|
|
3890
|
+
return strictObjectVerification;
|
|
3169
3891
|
};
|
|
3170
3892
|
try {
|
|
3171
|
-
const cached = await ensureRuntimeObjects(root, driver.argv, cflags, rtInputs, `obj-v5\0${cacheTargetIdentity(driver)}\0${toolchainEnv}\0${implicitToolchain}\0${runtimeCompilerInvocation}\0${ccName}\0${cv}\0${fingerprint}\0`, async () =>
|
|
3172
|
-
(await objectImplicitToolchainStillMatches()));
|
|
3893
|
+
const cached = await ensureRuntimeObjects(root, driver.argv, cflags, rtInputs, `obj-v5\0${cacheTargetIdentity(driver)}\0${toolchainEnv}\0${implicitToolchain}\0${runtimeCompilerInvocation}\0${ccName}\0${cv}\0${fingerprint}\0`, async () => await objectImplicitToolchainStillMatches());
|
|
3173
3894
|
objects = await stageRuntimeObjects(cached, join(buildDir, "runtime-objects"));
|
|
3174
3895
|
}
|
|
3175
3896
|
catch (err) {
|
|
@@ -3180,10 +3901,11 @@ export async function compileC(opts) {
|
|
|
3180
3901
|
await runClang(buildArgs((p) => objects?.get(p) ?? p, { programPath, outPath: privateOut, compilerVisibleSource: opts.cPath }));
|
|
3181
3902
|
await installArtifact(privateOut, opts.outPath);
|
|
3182
3903
|
if (cachedBin !== null && keyHex !== null) {
|
|
3183
|
-
//
|
|
3184
|
-
//
|
|
3185
|
-
//
|
|
3186
|
-
//
|
|
3904
|
+
// Metadata comparison catches ordinary changes cheaply, but cannot by
|
|
3905
|
+
// itself prove the snapshot was taken from the same bytes hashed into
|
|
3906
|
+
// the key. Recompute every content-bearing identity after the final link
|
|
3907
|
+
// so a header/SDK/compiler change in either pre-build gap cannot publish
|
|
3908
|
+
// new output under an old key.
|
|
3187
3909
|
const [currentRuntime, currentImplicit, currentRuntimeInvocation, currentProgramInvocation, currentProgramDependencies, currentLinker, currentCompiler] = await Promise.all([
|
|
3188
3910
|
runtimeFingerprint(rtDir).catch(() => null),
|
|
3189
3911
|
implicitToolchainFingerprint(driver, toolchainEnv).catch(() => null),
|
|
@@ -3195,6 +3917,8 @@ export async function compileC(opts) {
|
|
|
3195
3917
|
]);
|
|
3196
3918
|
cacheInputsStable =
|
|
3197
3919
|
cacheInputsStable &&
|
|
3920
|
+
preBuildDependencies !== null &&
|
|
3921
|
+
await localArtifactDependenciesStillMatch(preBuildDependencies) &&
|
|
3198
3922
|
currentRuntime === fingerprint &&
|
|
3199
3923
|
currentImplicit === implicitToolchain &&
|
|
3200
3924
|
currentRuntimeInvocation === runtimeCompilerInvocation &&
|
|
@@ -3214,6 +3938,12 @@ export async function compileC(opts) {
|
|
|
3214
3938
|
/* publishing is best-effort */
|
|
3215
3939
|
}
|
|
3216
3940
|
}
|
|
3941
|
+
if (localArtifact !== null &&
|
|
3942
|
+
localArtifactDependencyPaths !== null &&
|
|
3943
|
+
cacheCompleteArtifact &&
|
|
3944
|
+
cacheInputsStable) {
|
|
3945
|
+
await publishLocalArtifactStamp(localArtifact.stampPath, opts.outPath, localArtifact.key, localArtifactDependencyPaths, [dirname(resolve(opts.cPath))], [root]).catch(() => undefined);
|
|
3946
|
+
}
|
|
3217
3947
|
}
|
|
3218
3948
|
finally {
|
|
3219
3949
|
await rm(buildDir, { recursive: true, force: true }).catch(() => undefined);
|