@scriptc/compiler 0.0.34 → 0.0.36
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/ambient/scriptc-node-fallback.d.ts +7 -6
- package/dist/backend/build-cache.d.ts +44 -0
- package/dist/backend/build-cache.js +241 -0
- package/dist/backend/build-cache.js.map +1 -0
- package/dist/backend/{emission/emit-async.d.ts → c/async.d.ts} +21 -30
- package/dist/backend/{emission/emit-async.js → c/async.js} +270 -286
- package/dist/backend/c/async.js.map +1 -0
- package/dist/backend/{emission/emitter.d.ts → c/c-emitter.d.ts} +6 -23
- package/dist/backend/{emission/emitter.js → c/c-emitter.js} +53 -107
- package/dist/backend/c/c-emitter.js.map +1 -0
- package/dist/backend/c/exprs.d.ts +8 -0
- package/dist/backend/c/exprs.js +7913 -0
- package/dist/backend/c/exprs.js.map +1 -0
- package/dist/backend/{emission/emit-island.d.ts → c/island.d.ts} +5 -5
- package/dist/backend/{emission/emit-island.js → c/island.js} +24 -22
- package/dist/backend/c/island.js.map +1 -0
- package/dist/backend/{emission → c}/may-throw.d.ts +1 -1
- package/dist/backend/{emission → c}/may-throw.js +1 -1
- package/dist/backend/c/may-throw.js.map +1 -0
- package/dist/backend/{emission/emit-shapes.d.ts → c/shapes.d.ts} +19 -19
- package/dist/backend/{emission/emit-shapes.js → c/shapes.js} +78 -87
- package/dist/backend/c/shapes.js.map +1 -0
- package/dist/backend/{emission/emit-stmts.d.ts → c/stmts.d.ts} +12 -12
- package/dist/backend/{emission/emit-stmts.js → c/stmts.js} +377 -343
- package/dist/backend/c/stmts.js.map +1 -0
- package/dist/backend/{emission/emit-types.d.ts → c/types.d.ts} +1 -1
- package/dist/backend/{emission/emit-types.js → c/types.js} +42 -286
- package/dist/backend/c/types.js.map +1 -0
- package/dist/backend/c/types.test.js +25 -0
- package/dist/backend/c/types.test.js.map +1 -0
- package/dist/backend/{emission/emit-walkers.d.ts → c/walkers.d.ts} +19 -41
- package/dist/backend/{emission/emit-walkers.js → c/walkers.js} +238 -298
- package/dist/backend/c/walkers.js.map +1 -0
- package/dist/backend/ffi-callbacks.d.ts +1 -1
- package/dist/backend/ffi-callbacks.js +4 -3
- package/dist/backend/ffi-callbacks.js.map +1 -1
- package/dist/backend/{library-identity.d.ts → library-identity-markers.d.ts} +0 -4
- package/dist/backend/{library-identity.js → library-identity-markers.js} +10 -9
- package/dist/backend/library-identity-markers.js.map +1 -0
- package/dist/backend/llvm/blocks.d.ts +4 -0
- package/dist/backend/llvm/blocks.js +29 -0
- package/dist/backend/llvm/blocks.js.map +1 -1
- package/dist/backend/llvm/classes.d.ts +2 -19
- package/dist/backend/llvm/classes.js +20 -52
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/common.d.ts +4 -0
- package/dist/backend/llvm/common.js +27 -0
- package/dist/backend/llvm/common.js.map +1 -0
- package/dist/backend/llvm/dyn.d.ts +3 -8
- package/dist/backend/llvm/dyn.js +152 -224
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +4 -1
- package/dist/backend/llvm/emitter.js +514 -9981
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/expr-async.d.ts +4 -0
- package/dist/backend/llvm/expr-async.js +726 -0
- package/dist/backend/llvm/expr-async.js.map +1 -0
- package/dist/backend/llvm/expr-bytes.d.ts +13 -0
- package/dist/backend/llvm/expr-bytes.js +456 -0
- package/dist/backend/llvm/expr-bytes.js.map +1 -0
- package/dist/backend/llvm/expr-callbacks.d.ts +22 -0
- package/dist/backend/llvm/expr-callbacks.js +520 -0
- package/dist/backend/llvm/expr-callbacks.js.map +1 -0
- package/dist/backend/llvm/expr-calls.d.ts +2 -0
- package/dist/backend/llvm/expr-calls.js +618 -0
- package/dist/backend/llvm/expr-calls.js.map +1 -0
- package/dist/backend/llvm/expr-containers.d.ts +25 -0
- package/dist/backend/llvm/expr-containers.js +623 -0
- package/dist/backend/llvm/expr-containers.js.map +1 -0
- package/dist/backend/llvm/expr-context.d.ts +255 -0
- package/dist/backend/llvm/expr-context.js +2 -0
- package/dist/backend/llvm/expr-context.js.map +1 -0
- package/dist/backend/llvm/expr-control.d.ts +2 -0
- package/dist/backend/llvm/expr-control.js +473 -0
- package/dist/backend/llvm/expr-control.js.map +1 -0
- package/dist/backend/llvm/expr-dispatch.d.ts +4 -0
- package/dist/backend/llvm/expr-dispatch.js +151 -0
- package/dist/backend/llvm/expr-dispatch.js.map +1 -0
- package/dist/backend/llvm/expr-dynamic.d.ts +2 -0
- package/dist/backend/llvm/expr-dynamic.js +810 -0
- package/dist/backend/llvm/expr-dynamic.js.map +1 -0
- package/dist/backend/llvm/expr-island.d.ts +15 -0
- package/dist/backend/llvm/expr-island.js +690 -0
- package/dist/backend/llvm/expr-island.js.map +1 -0
- package/dist/backend/llvm/expr-primitives.d.ts +6 -0
- package/dist/backend/llvm/expr-primitives.js +622 -0
- package/dist/backend/llvm/expr-primitives.js.map +1 -0
- package/dist/backend/llvm/expr-records.d.ts +9 -0
- package/dist/backend/llvm/expr-records.js +299 -0
- package/dist/backend/llvm/expr-records.js.map +1 -0
- package/dist/backend/llvm/expr-stream-bridges.d.ts +13 -0
- package/dist/backend/llvm/expr-stream-bridges.js +396 -0
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -0
- package/dist/backend/llvm/expr-stream-callbacks.d.ts +12 -0
- package/dist/backend/llvm/expr-stream-callbacks.js +360 -0
- package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -0
- package/dist/backend/llvm/lib-dispatch.d.ts +5 -0
- package/dist/backend/llvm/lib-dispatch.js +267 -0
- package/dist/backend/llvm/lib-dispatch.js.map +1 -0
- package/dist/backend/llvm/lib-filesystem.d.ts +6 -0
- package/dist/backend/llvm/lib-filesystem.js +594 -0
- package/dist/backend/llvm/lib-filesystem.js.map +1 -0
- package/dist/backend/llvm/lib-network.d.ts +2 -0
- package/dist/backend/llvm/lib-network.js +429 -0
- package/dist/backend/llvm/lib-network.js.map +1 -0
- package/dist/backend/llvm/lib-process.d.ts +5 -0
- package/dist/backend/llvm/lib-process.js +585 -0
- package/dist/backend/llvm/lib-process.js.map +1 -0
- package/dist/backend/llvm/lib-shared.d.ts +6 -0
- package/dist/backend/llvm/lib-shared.js +687 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -0
- package/dist/backend/llvm/lib-stream.d.ts +2 -0
- package/dist/backend/llvm/lib-stream.js +470 -0
- package/dist/backend/llvm/lib-stream.js.map +1 -0
- package/dist/backend/llvm/shapes.d.ts +12 -2
- package/dist/backend/llvm/shapes.js +66 -307
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/shapes.test.js +52 -0
- package/dist/backend/llvm/shapes.test.js.map +1 -0
- package/dist/backend/llvm/split.d.ts +43 -0
- package/dist/backend/llvm/split.js +227 -0
- package/dist/backend/llvm/split.js.map +1 -0
- package/dist/backend/llvm/split.test.d.ts +1 -0
- package/dist/backend/llvm/split.test.js +102 -0
- package/dist/backend/llvm/split.test.js.map +1 -0
- package/dist/backend/llvm/unsupported.d.ts +1 -1
- package/dist/backend/llvm/walkers.d.ts +2 -3
- package/dist/backend/llvm/walkers.js +164 -218
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/backend/native-codegen.d.ts +38 -0
- package/dist/backend/native-codegen.js +261 -0
- package/dist/backend/native-codegen.js.map +1 -0
- package/dist/backend/native-codegen.test.d.ts +1 -0
- package/dist/backend/native-codegen.test.js +188 -0
- package/dist/backend/native-codegen.test.js.map +1 -0
- package/dist/backend/native-link-info.d.ts +96 -0
- package/dist/backend/native-link-info.js +219 -0
- package/dist/backend/native-link-info.js.map +1 -0
- package/dist/backend/native-link-info.test.d.ts +1 -0
- package/dist/backend/native-link-info.test.js +134 -0
- package/dist/backend/native-link-info.test.js.map +1 -0
- package/dist/backend/{cc.d.ts → native-toolchain.d.ts} +139 -47
- package/dist/backend/{cc.js → native-toolchain.js} +1008 -910
- package/dist/backend/native-toolchain.js.map +1 -0
- package/dist/backend/native-toolchain.test.d.ts +1 -0
- package/dist/backend/{cc-cache.test.js → native-toolchain.test.js} +1297 -127
- package/dist/backend/native-toolchain.test.js.map +1 -0
- package/dist/backend/object-localize.js +1 -1
- package/dist/backend/object-localize.test.js +20 -0
- package/dist/backend/object-localize.test.js.map +1 -1
- package/dist/backend/runtime-abi.d.ts +10 -0
- package/dist/backend/runtime-abi.js +11 -0
- package/dist/backend/runtime-abi.js.map +1 -0
- package/dist/backend/runtime-pack.d.ts +137 -0
- package/dist/backend/runtime-pack.js +400 -0
- package/dist/backend/runtime-pack.js.map +1 -0
- package/dist/backend/runtime-pack.test.d.ts +1 -0
- package/dist/backend/runtime-pack.test.js +438 -0
- package/dist/backend/runtime-pack.test.js.map +1 -0
- package/dist/backend/targets.d.ts +30 -0
- package/dist/backend/targets.js +46 -0
- package/dist/backend/targets.js.map +1 -0
- package/dist/backend/targets.test.d.ts +1 -0
- package/dist/backend/targets.test.js +19 -0
- package/dist/backend/targets.test.js.map +1 -0
- package/dist/backend/vendor-archives.d.ts +41 -0
- package/dist/backend/vendor-archives.js +462 -0
- package/dist/backend/vendor-archives.js.map +1 -0
- package/dist/compat/fetch-profile.d.ts +0 -1
- package/dist/compat/fetch-profile.js +0 -1
- package/dist/compat/fetch-profile.js.map +1 -1
- package/dist/coverage/report.js +3 -3
- package/dist/coverage/report.js.map +1 -1
- package/dist/coverage/surface-manifest.js +25 -32
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +11 -12
- package/dist/diagnostics/diagnostic.js +19 -17
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/diagnostics/render.d.ts +1 -1
- package/dist/diagnostics/render.js +1 -1
- package/dist/diagnostics/render.js.map +1 -1
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +9 -0
- package/dist/errors.js.map +1 -0
- package/dist/executable/executable-cache.d.ts +86 -0
- package/dist/executable/executable-cache.js +398 -0
- package/dist/executable/executable-cache.js.map +1 -0
- package/dist/executable/executable-cache.test.d.ts +1 -0
- package/dist/executable/executable-cache.test.js +300 -0
- package/dist/executable/executable-cache.test.js.map +1 -0
- package/dist/ffi/{profile.d.ts → ffi-manifest.d.ts} +7 -2
- package/dist/ffi/{profile.js → ffi-manifest.js} +2 -1
- package/dist/ffi/ffi-manifest.js.map +1 -0
- package/dist/frontend/builtin-modules.d.ts +21 -0
- package/dist/frontend/builtin-modules.js +82 -0
- package/dist/frontend/builtin-modules.js.map +1 -0
- package/dist/frontend/cjs-lexer.d.ts +7 -0
- package/dist/frontend/cjs-lexer.js +15 -17
- package/dist/frontend/cjs-lexer.js.map +1 -1
- package/dist/frontend/dts-paths.d.ts +26 -0
- package/dist/frontend/dts-paths.js +41 -0
- package/dist/frontend/dts-paths.js.map +1 -0
- package/dist/frontend/input-tracker.d.ts +0 -2
- package/dist/frontend/input-tracker.js +1 -4
- package/dist/frontend/input-tracker.js.map +1 -1
- package/dist/frontend/lib-contract.d.ts +1 -5
- package/dist/frontend/lib-contract.js +1 -1
- package/dist/frontend/lib-contract.js.map +1 -1
- package/dist/frontend/lib-exports.d.ts +1 -1
- package/dist/frontend/lowering/call-position.d.ts +4 -0
- package/dist/frontend/lowering/call-position.js +7 -0
- package/dist/frontend/lowering/call-position.js.map +1 -0
- package/dist/frontend/lowering/callback-arg.d.ts +20 -0
- package/dist/frontend/lowering/callback-arg.js +44 -0
- package/dist/frontend/lowering/callback-arg.js.map +1 -0
- package/dist/frontend/lowering/lib-boundary.d.ts +1 -1
- package/dist/frontend/lowering/lib-boundary.js +34 -34
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lower-assert.d.ts +3 -3
- package/dist/frontend/lowering/lower-assert.js +214 -241
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +74 -146
- package/dist/frontend/lowering/lower-builtins.js +1126 -1234
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +56 -188
- package/dist/frontend/lowering/lower-calls.js +1409 -1494
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +62 -125
- package/dist/frontend/lowering/lower-classes.js +832 -866
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-comptime.d.ts +5 -12
- package/dist/frontend/lowering/lower-comptime.js +32 -31
- package/dist/frontend/lowering/lower-comptime.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +35 -135
- package/dist/frontend/lowering/lower-containers.js +1394 -1715
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-dgram.d.ts +3 -7
- package/dist/frontend/lowering/lower-dgram.js +97 -111
- package/dist/frontend/lowering/lower-dgram.js.map +1 -1
- package/dist/frontend/lowering/lower-enums.d.ts +4 -4
- package/dist/frontend/lowering/lower-enums.js +30 -30
- package/dist/frontend/lowering/lower-enums.js.map +1 -1
- package/dist/frontend/lowering/{lower-emitter.d.ts → lower-event-emitter.d.ts} +7 -18
- package/dist/frontend/lowering/{lower-emitter.js → lower-event-emitter.js} +172 -174
- package/dist/frontend/lowering/lower-event-emitter.js.map +1 -0
- package/dist/frontend/lowering/lower-expando.d.ts +5 -5
- package/dist/frontend/lowering/lower-expando.js +40 -40
- package/dist/frontend/lowering/lower-expando.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +56 -264
- package/dist/frontend/lowering/lower-exprs.js +1644 -1704
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.d.ts +6 -6
- package/dist/frontend/lowering/lower-generators.js +73 -72
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.d.ts +5 -17
- package/dist/frontend/lowering/lower-inspect.js +236 -238
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +34 -45
- package/dist/frontend/lowering/lower-island.js +440 -511
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-mixins.d.ts +7 -10
- package/dist/frontend/lowering/lower-mixins.js +86 -86
- package/dist/frontend/lowering/lower-mixins.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.d.ts +12 -18
- package/dist/frontend/lowering/lower-modules.js +284 -283
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-namespaces.d.ts +23 -49
- package/dist/frontend/lowering/lower-namespaces.js +123 -115
- package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
- package/dist/frontend/lowering/lower-server.d.ts +18 -25
- package/dist/frontend/lowering/lower-server.js +899 -899
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +30 -149
- package/dist/frontend/lowering/lower-stmts.js +1176 -1229
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.d.ts +12 -12
- package/dist/frontend/lowering/lower-stream.js +300 -301
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lower-test.d.ts +5 -5
- package/dist/frontend/lowering/lower-test.js +82 -82
- package/dist/frontend/lowering/lower-test.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +66 -20
- package/dist/frontend/lowering/lowerer.js +421 -198
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/pairs-snapshot.d.ts +11 -0
- package/dist/frontend/lowering/pairs-snapshot.js +85 -0
- package/dist/frontend/lowering/pairs-snapshot.js.map +1 -0
- package/dist/frontend/lowering/static-this.d.ts +6 -0
- package/dist/frontend/lowering/static-this.js +24 -0
- package/dist/frontend/lowering/static-this.js.map +1 -0
- package/dist/frontend/lowering/surfaces.d.ts +14 -33
- package/dist/frontend/lowering/surfaces.js +64 -64
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/npm-static-rewrite.js +6 -28
- package/dist/frontend/npm-static-rewrite.js.map +1 -1
- package/dist/frontend/npm-static.d.ts +0 -10
- package/dist/frontend/npm-static.js +4 -7
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/npm.d.ts +0 -24
- package/dist/frontend/npm.js +11 -85
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/program.d.ts +3 -1
- package/dist/frontend/program.js +51 -17
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/provenance-registry.d.ts +0 -2
- package/dist/frontend/provenance-registry.js +0 -10
- package/dist/frontend/provenance-registry.js.map +1 -1
- package/dist/frontend/provenance.js +4 -7
- package/dist/frontend/provenance.js.map +1 -1
- package/dist/frontend/resolve.d.ts +7 -2
- package/dist/frontend/resolve.js +12 -17
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/resolve.test.d.ts +1 -0
- package/dist/frontend/resolve.test.js +29 -0
- package/dist/frontend/resolve.test.js.map +1 -0
- package/dist/frontend/ts7/adapter.d.ts +2 -2
- package/dist/frontend/ts7/adapter.js +1 -1
- package/dist/frontend/ts7/adapter.js.map +1 -1
- package/dist/frontend/ts7/checker.d.ts +40 -2
- package/dist/frontend/ts7/checker.js +182 -54
- package/dist/frontend/ts7/checker.js.map +1 -1
- package/dist/frontend/ts7/enums.js +2 -1
- package/dist/frontend/ts7/enums.js.map +1 -1
- package/dist/frontend/ts7/{program.js → program-adapter.js} +7 -6
- package/dist/frontend/ts7/program-adapter.js.map +1 -0
- package/dist/frontend/ts7/world-check.d.ts +1 -3
- package/dist/frontend/ts7/world-check.js +3 -1
- package/dist/frontend/ts7/world-check.js.map +1 -1
- package/dist/frontend/tsc-codes.d.ts +8 -0
- package/dist/frontend/tsc-codes.js +109 -0
- package/dist/frontend/tsc-codes.js.map +1 -0
- package/dist/frontend/{types.d.ts → type-mapper.d.ts} +2 -25
- package/dist/frontend/{types.js → type-mapper.js} +31 -52
- package/dist/frontend/type-mapper.js.map +1 -0
- package/dist/frontend/workspace-registry.d.ts +26 -0
- package/dist/frontend/workspace-registry.js +71 -0
- package/dist/frontend/workspace-registry.js.map +1 -0
- package/dist/index.d.ts +115 -30
- package/dist/index.js +650 -146
- package/dist/index.js.map +1 -1
- package/dist/ir/analysis.d.ts +37 -0
- package/dist/ir/analysis.js +118 -0
- package/dist/ir/analysis.js.map +1 -0
- package/dist/ir/analysis.test.d.ts +1 -0
- package/dist/ir/analysis.test.js +21 -0
- package/dist/ir/analysis.test.js.map +1 -0
- package/dist/ir/build.d.ts +9 -0
- package/dist/ir/build.js +33 -0
- package/dist/ir/build.js.map +1 -0
- package/dist/ir/integer-loops.d.ts +1 -1
- package/dist/ir/{nodes.d.ts → ir.d.ts} +60 -26
- package/dist/ir/{nodes.js → ir.js} +173 -141
- package/dist/ir/ir.js.map +1 -0
- package/dist/ir/ir.test.d.ts +1 -0
- package/dist/ir/ir.test.js +17 -0
- package/dist/ir/ir.test.js.map +1 -0
- package/dist/ir/serialize.d.ts +1 -1
- package/dist/ir/serialize.js +2 -1
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/validate.d.ts +1 -1
- package/dist/ir/validate.js +16 -5
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/cache-primitives.d.ts +24 -0
- package/dist/library/cache-primitives.js +83 -0
- package/dist/library/cache-primitives.js.map +1 -0
- package/dist/library/cache-reset.test.d.ts +1 -0
- package/dist/library/cache-reset.test.js +47 -0
- package/dist/library/cache-reset.test.js.map +1 -0
- package/dist/library/compiler-self-identity.d.ts +23 -0
- package/dist/library/compiler-self-identity.js +84 -0
- package/dist/library/compiler-self-identity.js.map +1 -0
- package/dist/library/fence-eval.d.ts +3 -1
- package/dist/library/fence-eval.js +5 -1
- package/dist/library/fence-eval.js.map +1 -1
- package/dist/library/int-infer.d.ts +1 -36
- package/dist/library/int-infer.js +17 -17
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/{early-cache.d.ts → library-cache.d.ts} +1 -1
- package/dist/library/{early-cache.js → library-cache.js} +37 -111
- package/dist/library/library-cache.js.map +1 -0
- package/dist/library/library-cache.test.d.ts +1 -0
- package/dist/library/{early-cache.test.js → library-cache.test.js} +2 -2
- package/dist/library/library-cache.test.js.map +1 -0
- package/dist/library/{profile.d.ts → library-profile.d.ts} +4 -7
- package/dist/library/{profile.js → library-profile.js} +18 -11
- package/dist/library/library-profile.js.map +1 -0
- package/dist/library/semantic-source.js +2 -1
- package/dist/library/semantic-source.js.map +1 -1
- package/dist/library/sidecar.d.ts +5 -2
- package/dist/library/sidecar.js +11 -5
- package/dist/library/sidecar.js.map +1 -1
- package/dist/startup-cache.d.ts +6 -0
- package/dist/startup-cache.js +6 -0
- package/dist/startup-cache.js.map +1 -0
- package/package.json +7 -2
- package/surface-manifest.json +1 -1
- package/dist/backend/cc-cache.test.js.map +0 -1
- package/dist/backend/cc.js.map +0 -1
- package/dist/backend/emission/emit-async.js.map +0 -1
- package/dist/backend/emission/emit-exprs.d.ts +0 -13
- package/dist/backend/emission/emit-exprs.js +0 -7407
- package/dist/backend/emission/emit-exprs.js.map +0 -1
- package/dist/backend/emission/emit-island.js.map +0 -1
- package/dist/backend/emission/emit-shapes.js.map +0 -1
- package/dist/backend/emission/emit-stmts.js.map +0 -1
- package/dist/backend/emission/emit-types.js.map +0 -1
- package/dist/backend/emission/emit-walkers.js.map +0 -1
- package/dist/backend/emission/emitter.js.map +0 -1
- package/dist/backend/emission/may-throw.js.map +0 -1
- package/dist/backend/library-identity.js.map +0 -1
- package/dist/ffi/profile.js.map +0 -1
- package/dist/frontend/lowering/lower-emitter.js.map +0 -1
- package/dist/frontend/shared.d.ts +0 -82
- package/dist/frontend/shared.js +0 -308
- package/dist/frontend/shared.js.map +0 -1
- package/dist/frontend/ts7/program.js.map +0 -1
- package/dist/frontend/types.js.map +0 -1
- package/dist/ir/nodes.js.map +0 -1
- package/dist/library/early-cache.js.map +0 -1
- package/dist/library/early-cache.test.js.map +0 -1
- package/dist/library/profile.js.map +0 -1
- /package/dist/backend/{cc-cache.test.d.ts → c/types.test.d.ts} +0 -0
- /package/dist/{library/early-cache.test.d.ts → backend/llvm/shapes.test.d.ts} +0 -0
- /package/dist/frontend/ts7/{program.d.ts → program-adapter.d.ts} +0 -0
|
@@ -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,
|
|
8
|
+
import { cacheTargetIdentity, ccVersion, compileC, compileLibArchive, executableSectionEliminationFlags, executableNativeEnvironmentFingerprint, implicitDependencyProbeIncludes, parseLinkTraceFiles, resolveBuildCacheRoot, runtimeFingerprint, runtimeSrcDir, stageRuntimeObjects, supportedNativeCacheWarmProfiles, toolchainEnvironmentCachePolicy, toolchainEnvironmentFingerprint, vendorCacheBuildIdentity, vendorCacheTargetFlavor, warmNativeCaches, } from "./native-toolchain.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
|
|
45
|
+
const executableOnPath = (name) => (process.env["PATH"] ?? "")
|
|
45
46
|
.split(delimiter)
|
|
46
|
-
.map((entry) => join(entry === "" ? process.cwd() : entry,
|
|
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,51 @@ 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("executable section elimination flags are target-aware and never enter library recipes", () => {
|
|
100
|
+
expect(executableSectionEliminationFlags("darwin")).toEqual({
|
|
101
|
+
compile: [],
|
|
102
|
+
link: ["-Wl,-dead_strip"],
|
|
103
|
+
});
|
|
104
|
+
expect(executableSectionEliminationFlags("linux")).toEqual({
|
|
105
|
+
compile: ["-ffunction-sections", "-fdata-sections"],
|
|
106
|
+
link: ["-Wl,--gc-sections"],
|
|
107
|
+
});
|
|
108
|
+
expect(executableSectionEliminationFlags("win32")).toEqual({
|
|
109
|
+
compile: ["-ffunction-sections", "-fdata-sections"],
|
|
110
|
+
link: ["-Wl,--gc-sections"],
|
|
111
|
+
});
|
|
112
|
+
expect(executableSectionEliminationFlags("wasi")).toEqual({ compile: [], link: [] });
|
|
113
|
+
});
|
|
114
|
+
test.skipIf(process.platform === "win32")("the early executable identity follows a compiler selected behind a stable driver", async () => {
|
|
115
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-effective-compiler-"));
|
|
116
|
+
scratch.push(dir);
|
|
117
|
+
const binDir = join(dir, "bin");
|
|
118
|
+
const selector = join(dir, "selected");
|
|
119
|
+
const firstCompiler = join(dir, "clang-first");
|
|
120
|
+
const secondCompiler = join(dir, "clang-second");
|
|
121
|
+
await mkdir(binDir);
|
|
122
|
+
await Promise.all([
|
|
123
|
+
writeFile(firstCompiler, "#!/bin/sh\nexit 0\n"),
|
|
124
|
+
writeFile(secondCompiler, "#!/bin/sh\nexit 0\n"),
|
|
125
|
+
writeFile(join(binDir, "clang"), "#!/bin/sh\nselected=$(cat \"$SCRIPTC_TEST_EFFECTIVE_COMPILER\")\nprintf '\"%s\" \"-cc1\"\\n' \"$selected\" >&2\n"),
|
|
126
|
+
writeFile(selector, `${firstCompiler}\n`),
|
|
127
|
+
]);
|
|
128
|
+
await Promise.all([
|
|
129
|
+
chmod(firstCompiler, 0o755),
|
|
130
|
+
chmod(secondCompiler, 0o755),
|
|
131
|
+
chmod(join(binDir, "clang"), 0o755),
|
|
132
|
+
]);
|
|
133
|
+
const env = {
|
|
134
|
+
...process.env,
|
|
135
|
+
PATH: `${binDir}${delimiter}${process.env["PATH"] ?? ""}`,
|
|
136
|
+
SCRIPTC_TEST_EFFECTIVE_COMPILER: selector,
|
|
137
|
+
};
|
|
138
|
+
const first = await executableNativeEnvironmentFingerprint(env);
|
|
139
|
+
expect(await executableNativeEnvironmentFingerprint(env)).toBe(first);
|
|
140
|
+
await writeFile(selector, `${secondCompiler}\n`);
|
|
141
|
+
const second = await executableNativeEnvironmentFingerprint(env);
|
|
142
|
+
expect(second).not.toBe(first);
|
|
143
|
+
});
|
|
88
144
|
test("native cache identities separate host architectures while cross targets remain explicit", () => {
|
|
89
145
|
expect(cacheTargetIdentity({ target: null }, "darwin", "arm64")).toBe("native:darwin:arm64");
|
|
90
146
|
expect(cacheTargetIdentity({ target: null }, "darwin", "x64")).toBe("native:darwin:x64");
|
|
@@ -93,6 +149,95 @@ test("native cache identities separate host architectures while cross targets re
|
|
|
93
149
|
expect(vendorCacheTargetFlavor({ target: null }, "darwin", "x64")).toBe("native-darwin-x64");
|
|
94
150
|
expect(vendorCacheTargetFlavor({ target: "x86_64-linux-gnu.2.36" }, "darwin", "arm64")).toBe("x86_64-linux-gnu.2.36");
|
|
95
151
|
});
|
|
152
|
+
test.skipIf(process.platform === "win32" || zigExecutable === undefined ||
|
|
153
|
+
clangExecutable === undefined || arExecutable === undefined)("targetless Zig vendor caches are separate from host-clang and reusable", async () => {
|
|
154
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-zig-vendor-cache-"));
|
|
155
|
+
scratch.push(dir);
|
|
156
|
+
const cacheRoot = join(dir, "cache");
|
|
157
|
+
const vendorRoot = join(dir, "vendor-cache");
|
|
158
|
+
const cPath = join(dir, "program.c");
|
|
159
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
160
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
161
|
+
const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
162
|
+
const oldCc = process.env["SCRIPTC_CC"];
|
|
163
|
+
const oldTarget = process.env["SCRIPTC_TARGET"];
|
|
164
|
+
try {
|
|
165
|
+
await writeFile(cPath, "int main(void) { return 0; }\n");
|
|
166
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
167
|
+
process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = vendorRoot;
|
|
168
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
169
|
+
delete process.env["SCRIPTC_TARGET"];
|
|
170
|
+
process.env["SCRIPTC_CC"] = "clang";
|
|
171
|
+
await compileC({
|
|
172
|
+
cPath,
|
|
173
|
+
outPath: join(dir, "host"),
|
|
174
|
+
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
175
|
+
dynamic: true,
|
|
176
|
+
net: true,
|
|
177
|
+
http: true,
|
|
178
|
+
tls: true,
|
|
179
|
+
zlib: true,
|
|
180
|
+
// Keep the complete executable tier out of this test: the second Zig
|
|
181
|
+
// invocation must walk the vendor cache and prove its artifacts are
|
|
182
|
+
// reusable independently of the output path.
|
|
183
|
+
systemLibraries: ["m"],
|
|
184
|
+
});
|
|
185
|
+
const hostEngine = (await readdir(vendorRoot)).find((name) => /^3c8f3d689539-plain-/.test(name));
|
|
186
|
+
const hostTls = (await readdir(vendorRoot)).find((name) => name.startsWith("mbedtls-"));
|
|
187
|
+
expect(hostEngine).toBeDefined();
|
|
188
|
+
expect(hostTls).toBeDefined();
|
|
189
|
+
process.env["SCRIPTC_CC"] = "zigcc";
|
|
190
|
+
const zigOptions = {
|
|
191
|
+
cPath,
|
|
192
|
+
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
193
|
+
dynamic: true,
|
|
194
|
+
net: true,
|
|
195
|
+
http: true,
|
|
196
|
+
tls: true,
|
|
197
|
+
zlib: true,
|
|
198
|
+
systemLibraries: ["m"],
|
|
199
|
+
};
|
|
200
|
+
await compileC({ ...zigOptions, outPath: join(dir, "zig-first") });
|
|
201
|
+
let vendorEntries = await readdir(vendorRoot);
|
|
202
|
+
expect(vendorEntries.filter((name) => /^3c8f3d689539-plain-/.test(name))).toHaveLength(2);
|
|
203
|
+
expect(vendorEntries.filter((name) => name.startsWith("mbedtls-")).length).toBe(2);
|
|
204
|
+
// Host clang uses system zlib, so only the Zig build materializes a zlib
|
|
205
|
+
// object family in the shared vendor root.
|
|
206
|
+
expect(vendorEntries.filter((name) => name.startsWith("zlib-")).length).toBe(1);
|
|
207
|
+
// Invalidate the host archive. A targetless Zig rebuild must continue to
|
|
208
|
+
// use the separately keyed Zig archive instead of repairing or consuming
|
|
209
|
+
// the host-clang entry.
|
|
210
|
+
await writeFile(join(vendorRoot, hostEngine, "libqjs.a"), "host archive intentionally invalid\n");
|
|
211
|
+
await writeFile(join(vendorRoot, hostTls, "libmbedtls.a"), "host archive intentionally invalid\n");
|
|
212
|
+
await compileC({ ...zigOptions, outPath: join(dir, "zig-second") });
|
|
213
|
+
vendorEntries = await readdir(vendorRoot);
|
|
214
|
+
expect(vendorEntries.filter((name) => /^3c8f3d689539-plain-/.test(name))).toHaveLength(2);
|
|
215
|
+
expect(await readFile(join(vendorRoot, hostEngine, "libqjs.a"), "utf8")).toBe("host archive intentionally invalid\n");
|
|
216
|
+
expect(await readFile(join(vendorRoot, hostTls, "libmbedtls.a"), "utf8")).toBe("host archive intentionally invalid\n");
|
|
217
|
+
}
|
|
218
|
+
finally {
|
|
219
|
+
if (oldCacheDir === undefined)
|
|
220
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
221
|
+
else
|
|
222
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
223
|
+
if (oldNoCache === undefined)
|
|
224
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
225
|
+
else
|
|
226
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
227
|
+
if (oldVendorCacheDir === undefined)
|
|
228
|
+
delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
229
|
+
else
|
|
230
|
+
process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
|
|
231
|
+
if (oldCc === undefined)
|
|
232
|
+
delete process.env["SCRIPTC_CC"];
|
|
233
|
+
else
|
|
234
|
+
process.env["SCRIPTC_CC"] = oldCc;
|
|
235
|
+
if (oldTarget === undefined)
|
|
236
|
+
delete process.env["SCRIPTC_TARGET"];
|
|
237
|
+
else
|
|
238
|
+
process.env["SCRIPTC_TARGET"] = oldTarget;
|
|
239
|
+
}
|
|
240
|
+
}, 600_000);
|
|
96
241
|
test("Zig COFF dry-run parsing retains every linker input on its single command line", async () => {
|
|
97
242
|
const dir = await mkdtemp(join(tmpdir(), "scriptc-link-trace-"));
|
|
98
243
|
scratch.push(dir);
|
|
@@ -142,9 +287,11 @@ test("the toolchain environment joins cache identities", () => {
|
|
|
142
287
|
completeArtifacts: false,
|
|
143
288
|
runtimeObjects: false,
|
|
144
289
|
});
|
|
290
|
+
// scriptc invokes its compiler and archiver directly; conventional build-
|
|
291
|
+
// system variables do not alter those commands.
|
|
145
292
|
expect(toolchainEnvironmentCachePolicy({ CFLAGS: "-I/headers" })).toEqual({
|
|
146
|
-
completeArtifacts:
|
|
147
|
-
runtimeObjects:
|
|
293
|
+
completeArtifacts: true,
|
|
294
|
+
runtimeObjects: true,
|
|
148
295
|
});
|
|
149
296
|
expect(toolchainEnvironmentCachePolicy({ ZIG_LIB_DIR: "/zig/lib" })).toEqual({
|
|
150
297
|
completeArtifacts: false,
|
|
@@ -154,8 +301,9 @@ test("the toolchain environment joins cache identities", () => {
|
|
|
154
301
|
completeArtifacts: false,
|
|
155
302
|
runtimeObjects: false,
|
|
156
303
|
});
|
|
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"));
|
|
304
|
+
expect(vendorCacheBuildIdentity(base, "compiler-one", "sources-one")).not.toBe(vendorCacheBuildIdentity(base, "compiler-two", "sources-one"));
|
|
305
|
+
expect(vendorCacheBuildIdentity(base, "compiler-one", "sources-one")).not.toBe(vendorCacheBuildIdentity(toolchainEnvironmentFingerprint({ MACOSX_DEPLOYMENT_TARGET: "11.0" }), "compiler-one", "sources-one"));
|
|
306
|
+
expect(vendorCacheBuildIdentity(base, "compiler-one", "sources-one")).not.toBe(vendorCacheBuildIdentity(base, "compiler-one", "sources-two"));
|
|
159
307
|
});
|
|
160
308
|
test.skipIf(process.platform === "win32")("compiler version probes use and remove a private working directory", async () => {
|
|
161
309
|
const dir = await mkdtemp(join(tmpdir(), "scriptc-version-probe-"));
|
|
@@ -174,7 +322,7 @@ printf 'fake zig cc version probe\n'
|
|
|
174
322
|
// Zig 0.16 creates cwd/a.o for `zig cc --version`. Model that side
|
|
175
323
|
// effect directly so this regression does not depend on Zig being
|
|
176
324
|
// installed in every generic test shard.
|
|
177
|
-
await
|
|
325
|
+
await ccVersion([fakeCompiler, "cc"], `version-probe-${dir}`);
|
|
178
326
|
expect(await readdir(probeTmp)).toEqual([]);
|
|
179
327
|
}
|
|
180
328
|
finally {
|
|
@@ -305,6 +453,69 @@ test("implicit dependency seeds include separately compiled vendor system header
|
|
|
305
453
|
// to the native object/cache identity.
|
|
306
454
|
expect(includes).toContain("<stdatomic.h>");
|
|
307
455
|
});
|
|
456
|
+
test.skipIf(process.platform === "win32" || clangExecutable === undefined || arExecutable === undefined ||
|
|
457
|
+
ldExecutable === undefined)("public native builds re-probe ccache availability after PATH changes", async () => {
|
|
458
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-ccache-reset-"));
|
|
459
|
+
scratch.push(dir);
|
|
460
|
+
const binDir = join(dir, "bin");
|
|
461
|
+
const cacheRoot = join(dir, "cache");
|
|
462
|
+
const cPath = join(dir, "program.c");
|
|
463
|
+
const ccacheLog = join(dir, "ccache.log");
|
|
464
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
465
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
466
|
+
const oldPath = process.env["PATH"];
|
|
467
|
+
const oldCcacheLog = process.env["SCRIPTC_TEST_CCACHE_LOG"];
|
|
468
|
+
try {
|
|
469
|
+
await Promise.all([mkdir(binDir), mkdir(cacheRoot, { mode: 0o700 })]);
|
|
470
|
+
await Promise.all([
|
|
471
|
+
symlink(clangExecutable, join(binDir, "clang")),
|
|
472
|
+
symlink(arExecutable, join(binDir, "ar")),
|
|
473
|
+
symlink(ldExecutable, join(binDir, "ld")),
|
|
474
|
+
writeFile(cPath, "int main(void) { return 0; }\n"),
|
|
475
|
+
]);
|
|
476
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
477
|
+
process.env["PATH"] = binDir;
|
|
478
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
479
|
+
await compileC({
|
|
480
|
+
cPath,
|
|
481
|
+
outPath: join(dir, "first"),
|
|
482
|
+
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
483
|
+
regex: true,
|
|
484
|
+
});
|
|
485
|
+
await writeFile(join(binDir, "ccache"), `#!/bin/sh
|
|
486
|
+
if [ "$1" = "--version" ]; then exit 0; fi
|
|
487
|
+
printf '%s\n' "$*" >> "$SCRIPTC_TEST_CCACHE_LOG"
|
|
488
|
+
exec "$@"
|
|
489
|
+
`);
|
|
490
|
+
await chmod(join(binDir, "ccache"), 0o755);
|
|
491
|
+
process.env["SCRIPTC_TEST_CCACHE_LOG"] = ccacheLog;
|
|
492
|
+
await compileC({
|
|
493
|
+
cPath,
|
|
494
|
+
outPath: join(dir, "second"),
|
|
495
|
+
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
496
|
+
dynamic: true,
|
|
497
|
+
});
|
|
498
|
+
expect((await readFile(ccacheLog, "utf8")).trim()).not.toBe("");
|
|
499
|
+
}
|
|
500
|
+
finally {
|
|
501
|
+
if (oldCacheDir === undefined)
|
|
502
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
503
|
+
else
|
|
504
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
505
|
+
if (oldNoCache === undefined)
|
|
506
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
507
|
+
else
|
|
508
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
509
|
+
if (oldPath === undefined)
|
|
510
|
+
delete process.env["PATH"];
|
|
511
|
+
else
|
|
512
|
+
process.env["PATH"] = oldPath;
|
|
513
|
+
if (oldCcacheLog === undefined)
|
|
514
|
+
delete process.env["SCRIPTC_TEST_CCACHE_LOG"];
|
|
515
|
+
else
|
|
516
|
+
process.env["SCRIPTC_TEST_CCACHE_LOG"] = oldCcacheLog;
|
|
517
|
+
}
|
|
518
|
+
});
|
|
308
519
|
test.skipIf(process.platform === "win32")("opaque compiler wrappers bypass persistent caches", async () => {
|
|
309
520
|
const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-compiler-wrapper-"));
|
|
310
521
|
scratch.push(dir);
|
|
@@ -1682,41 +1893,46 @@ test("the hard disable bypasses vendor prerequisite caches", async () => {
|
|
|
1682
1893
|
process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
|
|
1683
1894
|
}
|
|
1684
1895
|
});
|
|
1685
|
-
test.skipIf(process.platform
|
|
1686
|
-
const dir = await mkdtemp(join(tmpdir(), "scriptc-vendor-
|
|
1896
|
+
test.skipIf(process.platform === "win32")("shared vendor prerequisites re-key when vendored source bytes change", async () => {
|
|
1897
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-vendor-source-identity-"));
|
|
1687
1898
|
scratch.push(dir);
|
|
1688
1899
|
const cacheRoot = join(dir, "cache");
|
|
1689
|
-
const
|
|
1900
|
+
const fakeRuntimeRoot = join(dir, "runtime");
|
|
1901
|
+
const fakeRuntime = join(fakeRuntimeRoot, "src");
|
|
1902
|
+
const originalRuntime = runtimeSrcDir();
|
|
1690
1903
|
const cPath = join(dir, "program.c");
|
|
1691
1904
|
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
1692
1905
|
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
1906
|
+
const oldRuntimeDir = process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"];
|
|
1693
1907
|
const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
1694
|
-
const oldDeploymentTarget = process.env["MACOSX_DEPLOYMENT_TARGET"];
|
|
1695
1908
|
try {
|
|
1909
|
+
await Promise.all([
|
|
1910
|
+
cp(originalRuntime, fakeRuntime, { recursive: true }),
|
|
1911
|
+
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 })))),
|
|
1912
|
+
]);
|
|
1696
1913
|
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
1697
|
-
process.env["
|
|
1914
|
+
process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"] = fakeRuntime;
|
|
1698
1915
|
delete process.env["SCRIPTC_NO_CACHE"];
|
|
1916
|
+
delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
1699
1917
|
await writeFile(cPath, "int main(void) { return 0; }\n");
|
|
1700
|
-
process.env["MACOSX_DEPLOYMENT_TARGET"] = "14.0";
|
|
1701
1918
|
await compileC({
|
|
1702
1919
|
cPath,
|
|
1703
|
-
outPath: join(dir, "
|
|
1920
|
+
outPath: join(dir, "first"),
|
|
1704
1921
|
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
1705
1922
|
regex: true,
|
|
1706
1923
|
});
|
|
1707
|
-
|
|
1924
|
+
const vendorRoot = join(cacheRoot, "vendor");
|
|
1925
|
+
expect((await readdir(vendorRoot)).filter((name) => name.includes("-lre-"))).toHaveLength(1);
|
|
1926
|
+
const libregexp = join(fakeRuntimeRoot, "vendor", "quickjs-ng", "libregexp.c");
|
|
1927
|
+
await writeFile(libregexp, `${await readFile(libregexp, "utf8")}\n/* cache identity probe */\n`);
|
|
1928
|
+
await writeFile(cPath, "int main(void) { return 0; } /* second */\n");
|
|
1708
1929
|
await compileC({
|
|
1709
1930
|
cPath,
|
|
1710
|
-
outPath: join(dir, "
|
|
1931
|
+
outPath: join(dir, "second"),
|
|
1711
1932
|
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
1712
1933
|
regex: true,
|
|
1713
1934
|
});
|
|
1714
|
-
|
|
1715
|
-
expect(lreCaches).toHaveLength(2);
|
|
1716
|
-
const minVersions = lreCaches.map((cache) => execFileSync("vtool", ["-show-build", join(vendorCacheRoot, cache, "libregexp.o")], {
|
|
1717
|
-
encoding: "utf8",
|
|
1718
|
-
}).match(/minos\s+(\S+)/)?.[1]);
|
|
1719
|
-
expect(new Set(minVersions)).toEqual(new Set(["11.0", "14.0"]));
|
|
1935
|
+
expect((await readdir(vendorRoot)).filter((name) => name.includes("-lre-"))).toHaveLength(2);
|
|
1720
1936
|
}
|
|
1721
1937
|
finally {
|
|
1722
1938
|
if (oldCacheDir === undefined)
|
|
@@ -1727,56 +1943,48 @@ test.skipIf(process.platform !== "darwin")("vendor object caches separate deploy
|
|
|
1727
1943
|
delete process.env["SCRIPTC_NO_CACHE"];
|
|
1728
1944
|
else
|
|
1729
1945
|
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
1946
|
+
if (oldRuntimeDir === undefined)
|
|
1947
|
+
delete process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"];
|
|
1948
|
+
else
|
|
1949
|
+
process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"] = oldRuntimeDir;
|
|
1730
1950
|
if (oldVendorCacheDir === undefined)
|
|
1731
1951
|
delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
1732
1952
|
else
|
|
1733
1953
|
process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
|
|
1734
|
-
if (oldDeploymentTarget === undefined)
|
|
1735
|
-
delete process.env["MACOSX_DEPLOYMENT_TARGET"];
|
|
1736
|
-
else
|
|
1737
|
-
process.env["MACOSX_DEPLOYMENT_TARGET"] = oldDeploymentTarget;
|
|
1738
1954
|
}
|
|
1739
|
-
});
|
|
1740
|
-
test("
|
|
1741
|
-
const dir = await mkdtemp(join(tmpdir(), "scriptc-
|
|
1955
|
+
}, 120_000);
|
|
1956
|
+
test.skipIf(process.platform === "win32")("uncached arbitrary C keeps vendor prerequisites outside a read-only runtime package", async () => {
|
|
1957
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-readonly-runtime-"));
|
|
1742
1958
|
scratch.push(dir);
|
|
1743
|
-
const
|
|
1744
|
-
const
|
|
1959
|
+
const fakeRuntimeRoot = join(dir, "runtime");
|
|
1960
|
+
const fakeRuntime = join(fakeRuntimeRoot, "src");
|
|
1961
|
+
const fakeVendor = join(fakeRuntimeRoot, "vendor");
|
|
1962
|
+
const originalRuntime = runtimeSrcDir();
|
|
1745
1963
|
const cPath = join(dir, "program.c");
|
|
1746
1964
|
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
1747
1965
|
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
1966
|
+
const oldRuntimeDir = process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"];
|
|
1748
1967
|
const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
1749
1968
|
try {
|
|
1750
|
-
|
|
1751
|
-
|
|
1969
|
+
await Promise.all([
|
|
1970
|
+
cp(originalRuntime, fakeRuntime, { recursive: true }),
|
|
1971
|
+
mkdir(fakeVendor, { recursive: true }).then(() => Promise.all(["quickjs-ng", "ryu"].map((name) => cp(join(originalRuntime, "..", "vendor", name), join(fakeVendor, name), {
|
|
1972
|
+
recursive: true,
|
|
1973
|
+
})))),
|
|
1974
|
+
]);
|
|
1975
|
+
process.env["SCRIPTC_CACHE_DIR"] = join(dir, "cache");
|
|
1976
|
+
process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"] = fakeRuntime;
|
|
1752
1977
|
delete process.env["SCRIPTC_NO_CACHE"];
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
await
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
});
|
|
1761
|
-
expect(execFileSync(firstOut, { encoding: "utf8" }).trim()).toBe("cached");
|
|
1762
|
-
expect(await completeArtifacts(cacheRoot, "bin")).toHaveLength(1);
|
|
1763
|
-
expect((await stat(vendorCacheRoot)).isDirectory()).toBe(true);
|
|
1764
|
-
// Simulate a package reinstall: the per-package vendor build disappears,
|
|
1765
|
-
// while the per-user complete executable remains. The hit must not
|
|
1766
|
-
// recreate that directory even though the compiler remains available for
|
|
1767
|
-
// fresh dependency discovery.
|
|
1768
|
-
await rm(vendorCacheRoot, { recursive: true, force: true });
|
|
1769
|
-
const hitOut = firstOut;
|
|
1770
|
-
await compileC({
|
|
1771
|
-
cPath,
|
|
1772
|
-
outPath: hitOut,
|
|
1773
|
-
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
1774
|
-
regex: true,
|
|
1775
|
-
});
|
|
1776
|
-
expect(execFileSync(hitOut, { encoding: "utf8" }).trim()).toBe("cached");
|
|
1777
|
-
await expect(stat(vendorCacheRoot)).rejects.toMatchObject({ code: "ENOENT" });
|
|
1978
|
+
delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
1979
|
+
await writeFile(cPath, "int main(void) { return 0; }\n");
|
|
1980
|
+
await chmod(fakeVendor, 0o555);
|
|
1981
|
+
const outPath = join(dir, "program");
|
|
1982
|
+
await compileC({ cPath, outPath, regex: true });
|
|
1983
|
+
expect((await stat(outPath)).isFile()).toBe(true);
|
|
1984
|
+
await expect(stat(join(fakeVendor, ".cache"))).rejects.toMatchObject({ code: "ENOENT" });
|
|
1778
1985
|
}
|
|
1779
1986
|
finally {
|
|
1987
|
+
await chmod(fakeVendor, 0o755).catch(() => undefined);
|
|
1780
1988
|
if (oldCacheDir === undefined)
|
|
1781
1989
|
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
1782
1990
|
else
|
|
@@ -1785,37 +1993,41 @@ test("complete binary hits precede missing vendor prerequisite materialization",
|
|
|
1785
1993
|
delete process.env["SCRIPTC_NO_CACHE"];
|
|
1786
1994
|
else
|
|
1787
1995
|
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
1996
|
+
if (oldRuntimeDir === undefined)
|
|
1997
|
+
delete process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"];
|
|
1998
|
+
else
|
|
1999
|
+
process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"] = oldRuntimeDir;
|
|
1788
2000
|
if (oldVendorCacheDir === undefined)
|
|
1789
2001
|
delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
1790
2002
|
else
|
|
1791
2003
|
process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
|
|
1792
2004
|
}
|
|
1793
|
-
});
|
|
1794
|
-
test("
|
|
1795
|
-
const dir = await mkdtemp(join(tmpdir(), "scriptc-
|
|
2005
|
+
}, 120_000);
|
|
2006
|
+
test("native cache warming seeds exact runtime and vendor families without complete binaries", async () => {
|
|
2007
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-warm-"));
|
|
1796
2008
|
scratch.push(dir);
|
|
1797
2009
|
const cacheRoot = join(dir, "cache");
|
|
1798
|
-
const
|
|
1799
|
-
const headerPath = join(dir, "value.h");
|
|
2010
|
+
const vendorCacheRoot = join(cacheRoot, "vendor");
|
|
1800
2011
|
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
1801
2012
|
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
2013
|
+
const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
1802
2014
|
try {
|
|
1803
2015
|
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
2016
|
+
delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
1804
2017
|
delete process.env["SCRIPTC_NO_CACHE"];
|
|
1805
|
-
await
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
expect(
|
|
1810
|
-
await
|
|
1811
|
-
const
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
await
|
|
1817
|
-
expect((await stat(
|
|
1818
|
-
await expect(stat(join(cacheRoot, "lib"))).rejects.toMatchObject({ code: "ENOENT" });
|
|
2018
|
+
const warmed = await warmNativeCaches({ profiles: ["runtime", "tls", "dynamic"] });
|
|
2019
|
+
expect(warmed.cacheRoot).toBe(cacheRoot);
|
|
2020
|
+
expect(warmed.profiles.map(({ profile }) => profile)).toEqual(["runtime", "tls", "dynamic"]);
|
|
2021
|
+
expect(warmed.profiles.every(({ elapsedMs }) => elapsedMs >= 0)).toBe(true);
|
|
2022
|
+
expect((await readdir(join(cacheRoot, "obj"), { withFileTypes: true })).filter((e) => e.isDirectory())).toHaveLength(3);
|
|
2023
|
+
expect(await readdir(join(cacheRoot, "bin")).catch(() => [])).toEqual([]);
|
|
2024
|
+
const vendorEntries = await readdir(vendorCacheRoot);
|
|
2025
|
+
const tlsEntry = vendorEntries.find((name) => name.startsWith("mbedtls-"));
|
|
2026
|
+
const engineEntry = vendorEntries.find((name) => /^3c8f3d689539-plain-/.test(name));
|
|
2027
|
+
expect(tlsEntry).toBeDefined();
|
|
2028
|
+
expect(engineEntry).toBeDefined();
|
|
2029
|
+
expect((await stat(join(vendorCacheRoot, tlsEntry, "libmbedtls.a.sha256"))).isFile()).toBe(true);
|
|
2030
|
+
expect((await stat(join(vendorCacheRoot, engineEntry, "libqjs.a.sha256"))).isFile()).toBe(true);
|
|
1819
2031
|
}
|
|
1820
2032
|
finally {
|
|
1821
2033
|
if (oldCacheDir === undefined)
|
|
@@ -1826,34 +2038,44 @@ test("arbitrary C bypasses persistent artifacts so same-path header edits cannot
|
|
|
1826
2038
|
delete process.env["SCRIPTC_NO_CACHE"];
|
|
1827
2039
|
else
|
|
1828
2040
|
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
2041
|
+
if (oldVendorCacheDir === undefined)
|
|
2042
|
+
delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
2043
|
+
else
|
|
2044
|
+
process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
|
|
1829
2045
|
}
|
|
1830
|
-
});
|
|
1831
|
-
test("
|
|
1832
|
-
const dir = await mkdtemp(join(tmpdir(), "scriptc-
|
|
2046
|
+
}, 120_000);
|
|
2047
|
+
test("damaged shared vendor archives are rejected and rebuilt before linking", async () => {
|
|
2048
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-vendor-integrity-"));
|
|
1833
2049
|
scratch.push(dir);
|
|
1834
2050
|
const cacheRoot = join(dir, "cache");
|
|
1835
|
-
const
|
|
1836
|
-
const
|
|
1837
|
-
const headerPath = join(dir, "cache_path_header.h");
|
|
1838
|
-
const source = '#include <stdio.h>\n#include "cache_path_header.h"\nint main(void) { puts(__FILE__); puts(CACHE_PATH_HEADER); return 0; }\n';
|
|
2051
|
+
const cPath = join(dir, "program.c");
|
|
2052
|
+
const outPath = join(dir, "program");
|
|
1839
2053
|
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
1840
2054
|
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
2055
|
+
const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
2056
|
+
const corruption = Buffer.from("scriptc-corrupt-vendor-archive\n");
|
|
1841
2057
|
try {
|
|
1842
2058
|
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
1843
2059
|
delete process.env["SCRIPTC_NO_CACHE"];
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
const
|
|
1851
|
-
await
|
|
1852
|
-
await
|
|
1853
|
-
await compileC({
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
2060
|
+
delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
2061
|
+
await writeFile(cPath, "int main(void) { return 0; }\n");
|
|
2062
|
+
await warmNativeCaches({ profiles: ["dynamic"] });
|
|
2063
|
+
const vendorRoot = join(cacheRoot, "vendor");
|
|
2064
|
+
const engineDir = (await readdir(vendorRoot)).find((name) => /^3c8f3d689539-plain-/.test(name));
|
|
2065
|
+
expect(engineDir).toBeDefined();
|
|
2066
|
+
const engineArchive = join(vendorRoot, engineDir, "libqjs.a");
|
|
2067
|
+
expect((await stat(`${engineArchive}.sha256`)).isFile()).toBe(true);
|
|
2068
|
+
await writeFile(engineArchive, corruption);
|
|
2069
|
+
await compileC({
|
|
2070
|
+
cPath,
|
|
2071
|
+
outPath,
|
|
2072
|
+
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
2073
|
+
dynamic: true,
|
|
2074
|
+
});
|
|
2075
|
+
expect((await stat(outPath)).isFile()).toBe(true);
|
|
2076
|
+
const repaired = await readFile(engineArchive);
|
|
2077
|
+
expect(repaired).not.toEqual(corruption);
|
|
2078
|
+
expect((await readFile(`${engineArchive}.sha256`, "utf8")).trim()).toBe(createHash("sha256").update(repaired).digest("hex"));
|
|
1857
2079
|
}
|
|
1858
2080
|
finally {
|
|
1859
2081
|
if (oldCacheDir === undefined)
|
|
@@ -1864,36 +2086,358 @@ test("cached translation units keep the compiler-visible source path in their id
|
|
|
1864
2086
|
delete process.env["SCRIPTC_NO_CACHE"];
|
|
1865
2087
|
else
|
|
1866
2088
|
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
2089
|
+
if (oldVendorCacheDir === undefined)
|
|
2090
|
+
delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
2091
|
+
else
|
|
2092
|
+
process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
|
|
1867
2093
|
}
|
|
1868
|
-
});
|
|
1869
|
-
test
|
|
1870
|
-
const dir = await mkdtemp(join(tmpdir(), "scriptc-
|
|
2094
|
+
}, 120_000);
|
|
2095
|
+
test("dynamic builds promote staged vendor archives before bounded LRU eviction", async () => {
|
|
2096
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-vendor-lru-"));
|
|
1871
2097
|
scratch.push(dir);
|
|
1872
2098
|
const cacheRoot = join(dir, "cache");
|
|
1873
|
-
const binDir = join(dir, "bin");
|
|
1874
2099
|
const cPath = join(dir, "program.c");
|
|
1875
|
-
const
|
|
1876
|
-
const release = join(dir, "compile-release");
|
|
2100
|
+
const outPath = join(dir, "program");
|
|
1877
2101
|
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
1878
2102
|
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
1879
|
-
const
|
|
1880
|
-
const
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
.
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
const
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
2103
|
+
const oldMax = process.env["SCRIPTC_CACHE_MAX_MB"];
|
|
2104
|
+
const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
2105
|
+
try {
|
|
2106
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
2107
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
2108
|
+
delete process.env["SCRIPTC_CACHE_MAX_MB"];
|
|
2109
|
+
delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
2110
|
+
await writeFile(cPath, "int main(void) { return 0; }\n");
|
|
2111
|
+
await warmNativeCaches({ profiles: ["dynamic"] });
|
|
2112
|
+
const vendorRoot = join(cacheRoot, "vendor");
|
|
2113
|
+
const engineDir = (await readdir(vendorRoot)).find((name) => /^3c8f3d689539-plain-/.test(name));
|
|
2114
|
+
expect(engineDir).toBeDefined();
|
|
2115
|
+
const engineArchive = join(vendorRoot, engineDir, "libqjs.a");
|
|
2116
|
+
const initialBytes = await cacheTreeBytes(cacheRoot);
|
|
2117
|
+
const capMb = Math.max(4, Math.ceil(initialBytes * 2 / (1024 * 1024)));
|
|
2118
|
+
const filler = join(cacheRoot, "filler.bin");
|
|
2119
|
+
const staleArchiveTime = new Date("2000-01-01T00:00:00.000Z");
|
|
2120
|
+
const fillerTime = new Date("2001-01-01T00:00:00.000Z");
|
|
2121
|
+
await writeFile(filler, Buffer.alloc(capMb * 1024 * 1024));
|
|
2122
|
+
await utimes(engineArchive, staleArchiveTime, staleArchiveTime);
|
|
2123
|
+
await utimes(filler, fillerTime, fillerTime);
|
|
2124
|
+
process.env["SCRIPTC_CACHE_MAX_MB"] = String(capMb);
|
|
2125
|
+
await compileC({
|
|
2126
|
+
cPath,
|
|
2127
|
+
outPath,
|
|
2128
|
+
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
2129
|
+
dynamic: true,
|
|
2130
|
+
});
|
|
2131
|
+
expect((await stat(outPath)).isFile()).toBe(true);
|
|
2132
|
+
expect((await stat(engineArchive)).mtimeMs).toBeGreaterThan(fillerTime.getTime());
|
|
2133
|
+
expect(await cacheTreeBytes(cacheRoot)).toBeLessThanOrEqual(capMb * 1024 * 1024);
|
|
2134
|
+
}
|
|
2135
|
+
finally {
|
|
2136
|
+
if (oldCacheDir === undefined)
|
|
2137
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
2138
|
+
else
|
|
2139
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
2140
|
+
if (oldNoCache === undefined)
|
|
2141
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
2142
|
+
else
|
|
2143
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
2144
|
+
if (oldMax === undefined)
|
|
2145
|
+
delete process.env["SCRIPTC_CACHE_MAX_MB"];
|
|
2146
|
+
else
|
|
2147
|
+
process.env["SCRIPTC_CACHE_MAX_MB"] = oldMax;
|
|
2148
|
+
if (oldVendorCacheDir === undefined)
|
|
2149
|
+
delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
2150
|
+
else
|
|
2151
|
+
process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
|
|
2152
|
+
}
|
|
2153
|
+
}, 120_000);
|
|
2154
|
+
test("native cache warming follows the hard cache disable", async () => {
|
|
2155
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
2156
|
+
try {
|
|
2157
|
+
process.env["SCRIPTC_NO_CACHE"] = "1";
|
|
2158
|
+
await expect(warmNativeCaches({ profiles: ["runtime"] })).rejects.toThrow("native build cache is disabled");
|
|
2159
|
+
}
|
|
2160
|
+
finally {
|
|
2161
|
+
if (oldNoCache === undefined)
|
|
2162
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
2163
|
+
else
|
|
2164
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
2165
|
+
}
|
|
2166
|
+
});
|
|
2167
|
+
test("native cache warming refuses environments that disable persistent objects", async () => {
|
|
2168
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-warm-disabled-"));
|
|
2169
|
+
scratch.push(dir);
|
|
2170
|
+
const cacheRoot = join(dir, "cache");
|
|
2171
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
2172
|
+
const oldCpath = process.env["CPATH"];
|
|
2173
|
+
try {
|
|
2174
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
2175
|
+
process.env["CPATH"] = dir;
|
|
2176
|
+
await expect(warmNativeCaches({ profiles: ["runtime"] })).rejects.toThrow("requires a persistently cacheable compiler environment");
|
|
2177
|
+
expect(await readdir(cacheRoot)).toEqual([]);
|
|
2178
|
+
}
|
|
2179
|
+
finally {
|
|
2180
|
+
if (oldCacheDir === undefined)
|
|
2181
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
2182
|
+
else
|
|
2183
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
2184
|
+
if (oldCpath === undefined)
|
|
2185
|
+
delete process.env["CPATH"];
|
|
2186
|
+
else
|
|
2187
|
+
process.env["CPATH"] = oldCpath;
|
|
2188
|
+
}
|
|
2189
|
+
});
|
|
2190
|
+
test("native cache warm profiles follow target capabilities", () => {
|
|
2191
|
+
expect(supportedNativeCacheWarmProfiles({
|
|
2192
|
+
argv: ["zig", "cc"],
|
|
2193
|
+
target: "wasm32-wasi",
|
|
2194
|
+
zigTarget: "wasm32-wasi",
|
|
2195
|
+
targetArgs: [],
|
|
2196
|
+
linkArgs: [],
|
|
2197
|
+
})).toEqual([]);
|
|
2198
|
+
expect(supportedNativeCacheWarmProfiles({
|
|
2199
|
+
argv: ["zig", "cc"],
|
|
2200
|
+
target: "aarch64-apple-ios",
|
|
2201
|
+
zigTarget: "aarch64-ios.15.0",
|
|
2202
|
+
targetArgs: [],
|
|
2203
|
+
linkArgs: [],
|
|
2204
|
+
})).toEqual([]);
|
|
2205
|
+
});
|
|
2206
|
+
test("parallel native cache warming fails cleanly when the bounded cache cannot retain its profiles", async () => {
|
|
2207
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-warm-cap-"));
|
|
2208
|
+
scratch.push(dir);
|
|
2209
|
+
const cacheRoot = join(dir, "cache");
|
|
2210
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
2211
|
+
const oldMax = process.env["SCRIPTC_CACHE_MAX_MB"];
|
|
2212
|
+
try {
|
|
2213
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
2214
|
+
process.env["SCRIPTC_CACHE_MAX_MB"] = "2.5";
|
|
2215
|
+
await expect(warmNativeCaches()).rejects.toThrow("SCRIPTC_CACHE_MAX_MB is too small to retain the requested native cache warm profiles");
|
|
2216
|
+
expect(await readdir(cacheRoot)).not.toEqual([]);
|
|
2217
|
+
}
|
|
2218
|
+
finally {
|
|
2219
|
+
if (oldCacheDir === undefined)
|
|
2220
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
2221
|
+
else
|
|
2222
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
2223
|
+
if (oldMax === undefined)
|
|
2224
|
+
delete process.env["SCRIPTC_CACHE_MAX_MB"];
|
|
2225
|
+
else
|
|
2226
|
+
process.env["SCRIPTC_CACHE_MAX_MB"] = oldMax;
|
|
2227
|
+
}
|
|
2228
|
+
}, 120_000);
|
|
2229
|
+
test.skipIf(process.platform !== "darwin")("vendor object caches separate deployment-target environments", async () => {
|
|
2230
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-vendor-environment-"));
|
|
2231
|
+
scratch.push(dir);
|
|
2232
|
+
const cacheRoot = join(dir, "cache");
|
|
2233
|
+
const vendorCacheRoot = join(dir, "vendor-cache");
|
|
2234
|
+
const cPath = join(dir, "program.c");
|
|
2235
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
2236
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
2237
|
+
const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
2238
|
+
const oldDeploymentTarget = process.env["MACOSX_DEPLOYMENT_TARGET"];
|
|
2239
|
+
try {
|
|
2240
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
2241
|
+
process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = vendorCacheRoot;
|
|
2242
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
2243
|
+
await writeFile(cPath, "int main(void) { return 0; }\n");
|
|
2244
|
+
process.env["MACOSX_DEPLOYMENT_TARGET"] = "14.0";
|
|
2245
|
+
await compileC({
|
|
2246
|
+
cPath,
|
|
2247
|
+
outPath: join(dir, "newer"),
|
|
2248
|
+
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
2249
|
+
regex: true,
|
|
2250
|
+
});
|
|
2251
|
+
process.env["MACOSX_DEPLOYMENT_TARGET"] = "11.0";
|
|
2252
|
+
await compileC({
|
|
2253
|
+
cPath,
|
|
2254
|
+
outPath: join(dir, "older"),
|
|
2255
|
+
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
2256
|
+
regex: true,
|
|
2257
|
+
});
|
|
2258
|
+
const lreCaches = (await readdir(vendorCacheRoot)).filter((name) => name.includes("-lre-"));
|
|
2259
|
+
expect(lreCaches).toHaveLength(2);
|
|
2260
|
+
const minVersions = lreCaches.map((cache) => execFileSync("vtool", ["-show-build", join(vendorCacheRoot, cache, "libregexp.o")], {
|
|
2261
|
+
encoding: "utf8",
|
|
2262
|
+
}).match(/minos\s+(\S+)/)?.[1]);
|
|
2263
|
+
expect(new Set(minVersions)).toEqual(new Set(["11.0", "14.0"]));
|
|
2264
|
+
}
|
|
2265
|
+
finally {
|
|
2266
|
+
if (oldCacheDir === undefined)
|
|
2267
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
2268
|
+
else
|
|
2269
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
2270
|
+
if (oldNoCache === undefined)
|
|
2271
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
2272
|
+
else
|
|
2273
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
2274
|
+
if (oldVendorCacheDir === undefined)
|
|
2275
|
+
delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
2276
|
+
else
|
|
2277
|
+
process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
|
|
2278
|
+
if (oldDeploymentTarget === undefined)
|
|
2279
|
+
delete process.env["MACOSX_DEPLOYMENT_TARGET"];
|
|
2280
|
+
else
|
|
2281
|
+
process.env["MACOSX_DEPLOYMENT_TARGET"] = oldDeploymentTarget;
|
|
2282
|
+
}
|
|
2283
|
+
});
|
|
2284
|
+
test("complete binary hits precede missing vendor prerequisite materialization", async () => {
|
|
2285
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-vendor-hit-"));
|
|
2286
|
+
scratch.push(dir);
|
|
2287
|
+
const cacheRoot = join(dir, "cache");
|
|
2288
|
+
const vendorCacheRoot = join(dir, "vendor-cache");
|
|
2289
|
+
const cPath = join(dir, "program.c");
|
|
2290
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
2291
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
2292
|
+
const oldVendorCacheDir = process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
2293
|
+
try {
|
|
2294
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
2295
|
+
process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = vendorCacheRoot;
|
|
2296
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
2297
|
+
await writeFile(cPath, '#include <stdio.h>\nint main(void) { puts("cached"); return 0; }\n');
|
|
2298
|
+
const firstOut = join(dir, "first");
|
|
2299
|
+
await compileC({
|
|
2300
|
+
cPath,
|
|
2301
|
+
outPath: firstOut,
|
|
2302
|
+
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
2303
|
+
regex: true,
|
|
2304
|
+
});
|
|
2305
|
+
expect(execFileSync(firstOut, { encoding: "utf8" }).trim()).toBe("cached");
|
|
2306
|
+
expect(await completeArtifacts(cacheRoot, "bin")).toHaveLength(1);
|
|
2307
|
+
expect((await stat(vendorCacheRoot)).isDirectory()).toBe(true);
|
|
2308
|
+
// Simulate a package reinstall: the per-package vendor build disappears,
|
|
2309
|
+
// while the per-user complete executable remains. The hit must not
|
|
2310
|
+
// recreate that directory even though the compiler remains available for
|
|
2311
|
+
// fresh dependency discovery.
|
|
2312
|
+
await rm(vendorCacheRoot, { recursive: true, force: true });
|
|
2313
|
+
const hitOut = firstOut;
|
|
2314
|
+
await compileC({
|
|
2315
|
+
cPath,
|
|
2316
|
+
outPath: hitOut,
|
|
2317
|
+
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
2318
|
+
regex: true,
|
|
2319
|
+
});
|
|
2320
|
+
expect(execFileSync(hitOut, { encoding: "utf8" }).trim()).toBe("cached");
|
|
2321
|
+
await expect(stat(vendorCacheRoot)).rejects.toMatchObject({ code: "ENOENT" });
|
|
2322
|
+
}
|
|
2323
|
+
finally {
|
|
2324
|
+
if (oldCacheDir === undefined)
|
|
2325
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
2326
|
+
else
|
|
2327
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
2328
|
+
if (oldNoCache === undefined)
|
|
2329
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
2330
|
+
else
|
|
2331
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
2332
|
+
if (oldVendorCacheDir === undefined)
|
|
2333
|
+
delete process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"];
|
|
2334
|
+
else
|
|
2335
|
+
process.env["SCRIPTC_TEST_VENDOR_CACHE_DIR"] = oldVendorCacheDir;
|
|
2336
|
+
}
|
|
2337
|
+
});
|
|
2338
|
+
test("arbitrary C bypasses persistent artifacts so same-path header edits cannot go stale", async () => {
|
|
2339
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-arbitrary-c-cache-"));
|
|
2340
|
+
scratch.push(dir);
|
|
2341
|
+
const cacheRoot = join(dir, "cache");
|
|
2342
|
+
const cPath = join(dir, "program.c");
|
|
2343
|
+
const headerPath = join(dir, "value.h");
|
|
2344
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
2345
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
2346
|
+
try {
|
|
2347
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
2348
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
2349
|
+
await writeFile(cPath, '#include <stdio.h>\n#include "value.h"\nint main(void) { puts(VALUE); return 0; }\n');
|
|
2350
|
+
await writeFile(headerPath, '#define VALUE "one"\n');
|
|
2351
|
+
const firstOut = join(dir, "first");
|
|
2352
|
+
await compileC({ cPath, outPath: firstOut });
|
|
2353
|
+
expect(execFileSync(firstOut, { encoding: "utf8" }).trim()).toBe("one");
|
|
2354
|
+
await writeFile(headerPath, '#define VALUE "two"\n');
|
|
2355
|
+
const secondOut = join(dir, "second");
|
|
2356
|
+
await compileC({ cPath, outPath: secondOut });
|
|
2357
|
+
expect(execFileSync(secondOut, { encoding: "utf8" }).trim()).toBe("two");
|
|
2358
|
+
await expect(stat(join(cacheRoot, "bin"))).rejects.toMatchObject({ code: "ENOENT" });
|
|
2359
|
+
const archiveOut = join(dir, "program.lib.a");
|
|
2360
|
+
await compileLibArchive({ cPath, outPath: archiveOut });
|
|
2361
|
+
expect((await stat(archiveOut)).isFile()).toBe(true);
|
|
2362
|
+
await expect(stat(join(cacheRoot, "lib"))).rejects.toMatchObject({ code: "ENOENT" });
|
|
2363
|
+
}
|
|
2364
|
+
finally {
|
|
2365
|
+
if (oldCacheDir === undefined)
|
|
2366
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
2367
|
+
else
|
|
2368
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
2369
|
+
if (oldNoCache === undefined)
|
|
2370
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
2371
|
+
else
|
|
2372
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
2373
|
+
}
|
|
2374
|
+
});
|
|
2375
|
+
test("cached translation units keep the compiler-visible source path in their identity", async () => {
|
|
2376
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-source-path-"));
|
|
2377
|
+
scratch.push(dir);
|
|
2378
|
+
const cacheRoot = join(dir, "cache");
|
|
2379
|
+
const aPath = join(dir, "a.c");
|
|
2380
|
+
const bPath = join(dir, "b.c");
|
|
2381
|
+
const headerPath = join(dir, "cache_path_header.h");
|
|
2382
|
+
const source = '#include <stdio.h>\n#include "cache_path_header.h"\nint main(void) { puts(__FILE__); puts(CACHE_PATH_HEADER); return 0; }\n';
|
|
2383
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
2384
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
2385
|
+
try {
|
|
2386
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
2387
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
2388
|
+
await Promise.all([
|
|
2389
|
+
writeFile(aPath, source),
|
|
2390
|
+
writeFile(bPath, source),
|
|
2391
|
+
writeFile(headerPath, '#define CACHE_PATH_HEADER "header"\n'),
|
|
2392
|
+
]);
|
|
2393
|
+
const aOut = join(dir, "a-out", "program");
|
|
2394
|
+
const bOut = join(dir, "b-out", "program");
|
|
2395
|
+
await Promise.all([mkdir(join(dir, "a-out")), mkdir(join(dir, "b-out"))]);
|
|
2396
|
+
await compileC({ cPath: aPath, outPath: aOut, cacheIdentity: TEST_CACHE_IDENTITY });
|
|
2397
|
+
await compileC({ cPath: bPath, outPath: bOut, cacheIdentity: TEST_CACHE_IDENTITY });
|
|
2398
|
+
expect(execFileSync(aOut, { encoding: "utf8" }).trim()).toBe(`${aPath}\nheader`);
|
|
2399
|
+
expect(execFileSync(bOut, { encoding: "utf8" }).trim()).toBe(`${bPath}\nheader`);
|
|
2400
|
+
expect(await completeArtifacts(cacheRoot, "bin")).toHaveLength(2);
|
|
2401
|
+
}
|
|
2402
|
+
finally {
|
|
2403
|
+
if (oldCacheDir === undefined)
|
|
2404
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
2405
|
+
else
|
|
2406
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
2407
|
+
if (oldNoCache === undefined)
|
|
2408
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
2409
|
+
else
|
|
2410
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
2411
|
+
}
|
|
2412
|
+
});
|
|
2413
|
+
test.skipIf(process.platform === "win32")("cache publication compiles the exact translation-unit bytes used by its key", async () => {
|
|
2414
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-cache-tu-race-"));
|
|
2415
|
+
scratch.push(dir);
|
|
2416
|
+
const cacheRoot = join(dir, "cache");
|
|
2417
|
+
const binDir = join(dir, "bin");
|
|
2418
|
+
const cPath = join(dir, "program.c");
|
|
2419
|
+
const signal = join(dir, "compile-started");
|
|
2420
|
+
const release = join(dir, "compile-release");
|
|
2421
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
2422
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
2423
|
+
const oldPath = process.env["PATH"];
|
|
2424
|
+
const oldRealClang = process.env["SCRIPTC_TEST_REAL_CLANG"];
|
|
2425
|
+
const oldRaceSource = process.env["SCRIPTC_TEST_RACE_SOURCE"];
|
|
2426
|
+
const oldRaceSignal = process.env["SCRIPTC_TEST_RACE_SIGNAL"];
|
|
2427
|
+
const oldRaceRelease = process.env["SCRIPTC_TEST_RACE_RELEASE"];
|
|
2428
|
+
const originalClang = (oldPath ?? "")
|
|
2429
|
+
.split(delimiter)
|
|
2430
|
+
.map((entry) => join(entry === "" ? process.cwd() : entry, "clang"))
|
|
2431
|
+
.find((candidate) => existsSync(candidate));
|
|
2432
|
+
expect(originalClang).toBeDefined();
|
|
2433
|
+
const source = (value) => `#include <stdio.h>\nint main(void) { puts("${value}"); return 0; }\n`;
|
|
2434
|
+
const waitForSignal = async () => {
|
|
2435
|
+
const deadline = Date.now() + 10_000;
|
|
2436
|
+
while (!existsSync(signal)) {
|
|
2437
|
+
if (Date.now() > deadline)
|
|
2438
|
+
throw new Error("timed out waiting for the compiler wrapper");
|
|
2439
|
+
await new Promise((resolveWait) => setTimeout(resolveWait, 10));
|
|
2440
|
+
}
|
|
1897
2441
|
};
|
|
1898
2442
|
try {
|
|
1899
2443
|
await mkdir(binDir);
|
|
@@ -2183,7 +2727,11 @@ done
|
|
|
2183
2727
|
exec "$SCRIPTC_TEST_REAL_CLANG" "$@"
|
|
2184
2728
|
`);
|
|
2185
2729
|
await chmod(wrapper, 0o755);
|
|
2186
|
-
|
|
2730
|
+
// Keep the injected datum reachable. With executable section GC an
|
|
2731
|
+
// unreferenced test-only global is rightly removed before the binary
|
|
2732
|
+
// assertion below can observe it.
|
|
2733
|
+
await writeFile(cPath, "extern int scriptc_runtime_race_marker(void);\n" +
|
|
2734
|
+
"int main(void) { return scriptc_runtime_race_marker(); }\n");
|
|
2187
2735
|
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
2188
2736
|
process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"] = fakeRuntime;
|
|
2189
2737
|
process.env["SCRIPTC_TEST_REAL_CLANG"] = realClang;
|
|
@@ -2208,7 +2756,11 @@ exec "$SCRIPTC_TEST_REAL_CLANG" "$@"
|
|
|
2208
2756
|
throw new Error("timed out waiting for runtime compile");
|
|
2209
2757
|
await new Promise((resolveWait) => setTimeout(resolveWait, 10));
|
|
2210
2758
|
}
|
|
2211
|
-
await writeFile(raceSource, `${originalRuntimeSource}\
|
|
2759
|
+
await writeFile(raceSource, `${originalRuntimeSource}\n` +
|
|
2760
|
+
`volatile const char scriptc_runtime_race_marker_data[] = "${marker}";\n` +
|
|
2761
|
+
"int scriptc_runtime_race_marker(void) {\n" +
|
|
2762
|
+
" return scriptc_runtime_race_marker_data[0] == '\\0';\n" +
|
|
2763
|
+
"}\n");
|
|
2212
2764
|
await writeFile(release, "go");
|
|
2213
2765
|
await firstBuild;
|
|
2214
2766
|
expect((await readFile(firstOut)).includes(Buffer.from(marker))).toBe(true);
|
|
@@ -2434,7 +2986,78 @@ test("frontend-generated same-output builds no-op only while output and dependen
|
|
|
2434
2986
|
delete process.env["SCRIPTC_NO_CACHE"];
|
|
2435
2987
|
else
|
|
2436
2988
|
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
2437
|
-
process.umask(oldUmask);
|
|
2989
|
+
process.umask(oldUmask);
|
|
2990
|
+
}
|
|
2991
|
+
});
|
|
2992
|
+
test("pre-section-GC output-local stamps cannot restore an old executable", async () => {
|
|
2993
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-section-gc-local-stamp-"));
|
|
2994
|
+
scratch.push(dir);
|
|
2995
|
+
const cacheRoot = join(dir, "cache");
|
|
2996
|
+
const cPath = join(dir, "program.c");
|
|
2997
|
+
const outPath = join(dir, "program");
|
|
2998
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
2999
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
3000
|
+
try {
|
|
3001
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
3002
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
3003
|
+
await writeFile(cPath, "int main(void) { return 0; }\n");
|
|
3004
|
+
await compileC({ cPath, outPath, cacheIdentity: "scriptc-generated-v1" });
|
|
3005
|
+
const stampPath = join(cacheRoot, "local", createHash("sha256").update(outPath).digest("hex"));
|
|
3006
|
+
const current = JSON.parse(await readFile(stampPath, "utf8"));
|
|
3007
|
+
const legacy = { ...current, key: "old-non-gc-output", version: 1 };
|
|
3008
|
+
await writeFile(stampPath, `${JSON.stringify(legacy)}\n`);
|
|
3009
|
+
const pinnedTime = new Date("2001-01-01T00:00:00.000Z");
|
|
3010
|
+
await utimes(outPath, pinnedTime, pinnedTime);
|
|
3011
|
+
await compileC({ cPath, outPath, cacheIdentity: "scriptc-generated-v1" });
|
|
3012
|
+
expect((await stat(outPath)).mtimeMs).toBeGreaterThan(pinnedTime.getTime());
|
|
3013
|
+
}
|
|
3014
|
+
finally {
|
|
3015
|
+
if (oldCacheDir === undefined)
|
|
3016
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
3017
|
+
else
|
|
3018
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
3019
|
+
if (oldNoCache === undefined)
|
|
3020
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
3021
|
+
else
|
|
3022
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
3023
|
+
}
|
|
3024
|
+
});
|
|
3025
|
+
test("artifact-ready callbacks expose native dependencies on builds and validated hits", async () => {
|
|
3026
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-artifact-ready-"));
|
|
3027
|
+
scratch.push(dir);
|
|
3028
|
+
const cacheRoot = join(dir, "cache");
|
|
3029
|
+
const cPath = join(dir, "program.c");
|
|
3030
|
+
const outPath = join(dir, "program");
|
|
3031
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
3032
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
3033
|
+
try {
|
|
3034
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
3035
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
3036
|
+
await writeFile(cPath, "int main(void) { return 0; }\n");
|
|
3037
|
+
const observations = [];
|
|
3038
|
+
const build = () => compileC({
|
|
3039
|
+
cPath,
|
|
3040
|
+
outPath,
|
|
3041
|
+
cacheIdentity: "scriptc-generated-v1",
|
|
3042
|
+
onArtifactReady: async ({ dependencies }) => {
|
|
3043
|
+
observations.push(dependencies.map((dependency) => dependency.path));
|
|
3044
|
+
},
|
|
3045
|
+
});
|
|
3046
|
+
await build();
|
|
3047
|
+
await build();
|
|
3048
|
+
expect(observations).toHaveLength(2);
|
|
3049
|
+
expect(observations[0].length).toBeGreaterThan(0);
|
|
3050
|
+
expect(observations[1]).toEqual(observations[0]);
|
|
3051
|
+
}
|
|
3052
|
+
finally {
|
|
3053
|
+
if (oldCacheDir === undefined)
|
|
3054
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
3055
|
+
else
|
|
3056
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
3057
|
+
if (oldNoCache === undefined)
|
|
3058
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
3059
|
+
else
|
|
3060
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
2438
3061
|
}
|
|
2439
3062
|
});
|
|
2440
3063
|
test.skipIf(process.platform === "win32")("output-local hits follow symlinked header targets", async () => {
|
|
@@ -2515,7 +3138,7 @@ test("fresh processes preserve output-local dependency coverage after source edi
|
|
|
2515
3138
|
delete childEnv["SCRIPTC_NO_CACHE"];
|
|
2516
3139
|
delete childEnv["SCRIPTC_TEST_STABLE_TOOLCHAIN"];
|
|
2517
3140
|
delete childEnv["SCRIPTC_TEST_TRUST_COMPILER_WRAPPER"];
|
|
2518
|
-
await writeFile(helperPath, `import { compileC } from ${JSON.stringify(new URL("./
|
|
3141
|
+
await writeFile(helperPath, `import { compileC } from ${JSON.stringify(new URL("./native-toolchain.ts", import.meta.url).href)};\n` +
|
|
2519
3142
|
`await compileC({ cPath: process.argv[2], outPath: process.argv[3], cacheIdentity: "scriptc-generated-v1" });\n`);
|
|
2520
3143
|
const compileInFreshProcess = () => {
|
|
2521
3144
|
execFileSync(process.execPath, ["--import", "tsx", helperPath, cPath, outPath], {
|
|
@@ -2867,6 +3490,553 @@ test("library identity edits reuse the cached large program object", async () =>
|
|
|
2867
3490
|
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
2868
3491
|
}
|
|
2869
3492
|
});
|
|
3493
|
+
test.skipIf(!nativeShardMergeAvailable)("LLVM library shards reuse unaffected program objects after a localized edit", async () => {
|
|
3494
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-program-shards-"));
|
|
3495
|
+
scratch.push(dir);
|
|
3496
|
+
const cacheRoot = join(dir, "cache");
|
|
3497
|
+
const cPath = join(dir, "program.ll");
|
|
3498
|
+
const outPath = join(dir, "program.lib.a");
|
|
3499
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
3500
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
3501
|
+
const source = (value) => `%Pair = type { i64, i64 }
|
|
3502
|
+
@private_value = internal global i64 ${value}
|
|
3503
|
+
|
|
3504
|
+
define internal i64 @left() {
|
|
3505
|
+
entry:
|
|
3506
|
+
%v = load i64, ptr @private_value
|
|
3507
|
+
ret i64 %v
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
define internal i64 @right() {
|
|
3511
|
+
entry:
|
|
3512
|
+
%v = call i64 @left()
|
|
3513
|
+
ret i64 %v
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3516
|
+
define i64 @scriptc_public_value() {
|
|
3517
|
+
entry:
|
|
3518
|
+
%v = call i64 @right()
|
|
3519
|
+
ret i64 %v
|
|
3520
|
+
}
|
|
3521
|
+
`;
|
|
3522
|
+
try {
|
|
3523
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
3524
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
3525
|
+
await mkdir(cacheRoot, { mode: 0o700 });
|
|
3526
|
+
const build = async (value) => {
|
|
3527
|
+
const programSource = source(value);
|
|
3528
|
+
const split = splitLlvmProgram(programSource, { minimumBytes: 0, targetBytes: 64 * 1024 });
|
|
3529
|
+
expect(split?.shards).toHaveLength(3);
|
|
3530
|
+
await writeFile(cPath, programSource);
|
|
3531
|
+
await compileLibArchive({
|
|
3532
|
+
cPath,
|
|
3533
|
+
programSource,
|
|
3534
|
+
programShards: split.shards,
|
|
3535
|
+
programPublicSymbols: split.publicSymbols,
|
|
3536
|
+
outPath,
|
|
3537
|
+
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
3538
|
+
optimization: "dev",
|
|
3539
|
+
});
|
|
3540
|
+
};
|
|
3541
|
+
await build(7);
|
|
3542
|
+
const initial = (await readdir(join(cacheRoot, "program-shard")))
|
|
3543
|
+
.filter((name) => !name.endsWith(".sha256"));
|
|
3544
|
+
expect(initial).toHaveLength(3);
|
|
3545
|
+
await build(9);
|
|
3546
|
+
const after = (await readdir(join(cacheRoot, "program-shard")))
|
|
3547
|
+
.filter((name) => !name.endsWith(".sha256"));
|
|
3548
|
+
expect(after).toHaveLength(4);
|
|
3549
|
+
expect(initial.filter((name) => after.includes(name))).toHaveLength(3);
|
|
3550
|
+
const old = new Date("2000-01-01T00:00:00.000Z");
|
|
3551
|
+
await Promise.all(after.map((name) => utimes(join(cacheRoot, "program-shard", name), old, old)));
|
|
3552
|
+
await build(9);
|
|
3553
|
+
for (const name of after) {
|
|
3554
|
+
// The completed archive hit returns before shard lookup/merge; exact
|
|
3555
|
+
// repeats do not even touch the per-bucket object cache.
|
|
3556
|
+
expect((await stat(join(cacheRoot, "program-shard", name))).mtimeMs).toBe(old.getTime());
|
|
3557
|
+
}
|
|
3558
|
+
const probeSource = join(dir, "probe.c");
|
|
3559
|
+
const probe = join(dir, "probe");
|
|
3560
|
+
await writeFile(probeSource, "#include <stdio.h>\nlong long scriptc_public_value(void);\nint main(void) { printf(\"%lld\\n\", scriptc_public_value()); }\n");
|
|
3561
|
+
execFileSync("clang", [probeSource, outPath, "-lm", "-o", probe]);
|
|
3562
|
+
expect(execFileSync(probe, { encoding: "utf8" })).toBe("9\n");
|
|
3563
|
+
}
|
|
3564
|
+
finally {
|
|
3565
|
+
if (oldCacheDir === undefined)
|
|
3566
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
3567
|
+
else
|
|
3568
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
3569
|
+
if (oldNoCache === undefined)
|
|
3570
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
3571
|
+
else
|
|
3572
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
3573
|
+
}
|
|
3574
|
+
});
|
|
3575
|
+
test.skipIf(!nativeShardMergeAvailable)("LLVM executable dev shards reuse unaffected program objects after a localized edit", async () => {
|
|
3576
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-exe-program-shards-"));
|
|
3577
|
+
scratch.push(dir);
|
|
3578
|
+
const cacheRoot = join(dir, "cache");
|
|
3579
|
+
const cPath = join(dir, "program.ll");
|
|
3580
|
+
const outPath = join(dir, "program");
|
|
3581
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
3582
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
3583
|
+
const source = (value) => `@private_value = internal global i64 ${value}
|
|
3584
|
+
|
|
3585
|
+
define internal i64 @left() {
|
|
3586
|
+
entry:
|
|
3587
|
+
%v = load i64, ptr @private_value
|
|
3588
|
+
ret i64 %v
|
|
3589
|
+
}
|
|
3590
|
+
|
|
3591
|
+
define internal i64 @right() {
|
|
3592
|
+
entry:
|
|
3593
|
+
%v = call i64 @left()
|
|
3594
|
+
ret i64 %v
|
|
3595
|
+
}
|
|
3596
|
+
|
|
3597
|
+
define i32 @main() {
|
|
3598
|
+
entry:
|
|
3599
|
+
%v = call i64 @right()
|
|
3600
|
+
%exit = trunc i64 %v to i32
|
|
3601
|
+
ret i32 %exit
|
|
3602
|
+
}
|
|
3603
|
+
`;
|
|
3604
|
+
try {
|
|
3605
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
3606
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
3607
|
+
await mkdir(cacheRoot, { mode: 0o700 });
|
|
3608
|
+
const build = async (value) => {
|
|
3609
|
+
const programSource = source(value);
|
|
3610
|
+
const split = splitLlvmProgram(programSource, { minimumBytes: 0, targetBytes: 64 * 1024 });
|
|
3611
|
+
expect(split?.shards).toHaveLength(3);
|
|
3612
|
+
await writeFile(cPath, programSource);
|
|
3613
|
+
await compileC({
|
|
3614
|
+
cPath,
|
|
3615
|
+
programShards: split.shards,
|
|
3616
|
+
programPublicSymbols: split.publicSymbols,
|
|
3617
|
+
outPath,
|
|
3618
|
+
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
3619
|
+
optimization: "dev",
|
|
3620
|
+
});
|
|
3621
|
+
};
|
|
3622
|
+
await build(7);
|
|
3623
|
+
const initial = (await readdir(join(cacheRoot, "program-shard")))
|
|
3624
|
+
.filter((name) => !name.endsWith(".sha256"));
|
|
3625
|
+
expect(initial).toHaveLength(3);
|
|
3626
|
+
expect(spawnSync(outPath).status).toBe(7);
|
|
3627
|
+
await build(9);
|
|
3628
|
+
const after = (await readdir(join(cacheRoot, "program-shard")))
|
|
3629
|
+
.filter((name) => !name.endsWith(".sha256"));
|
|
3630
|
+
expect(after).toHaveLength(4);
|
|
3631
|
+
expect(initial.filter((name) => after.includes(name))).toHaveLength(3);
|
|
3632
|
+
expect(spawnSync(outPath).status).toBe(9);
|
|
3633
|
+
const old = new Date("2000-01-01T00:00:00.000Z");
|
|
3634
|
+
await Promise.all(after.map((name) => utimes(join(cacheRoot, "program-shard", name), old, old)));
|
|
3635
|
+
await build(9);
|
|
3636
|
+
for (const name of after) {
|
|
3637
|
+
// The completed executable hit returns before shard lookup/merge.
|
|
3638
|
+
expect((await stat(join(cacheRoot, "program-shard", name))).mtimeMs).toBe(old.getTime());
|
|
3639
|
+
}
|
|
3640
|
+
}
|
|
3641
|
+
finally {
|
|
3642
|
+
if (oldCacheDir === undefined)
|
|
3643
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
3644
|
+
else
|
|
3645
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
3646
|
+
if (oldNoCache === undefined)
|
|
3647
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
3648
|
+
else
|
|
3649
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
3650
|
+
}
|
|
3651
|
+
});
|
|
3652
|
+
test.skipIf(process.platform === "win32" || clangExecutable === undefined ||
|
|
3653
|
+
ldExecutable === undefined || (process.platform === "linux" && objcopyExecutable === undefined))("LLVM executable shards fall back without publishing a shard-derived binary", async () => {
|
|
3654
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-exe-program-shard-fallback-"));
|
|
3655
|
+
scratch.push(dir);
|
|
3656
|
+
const binDir = join(dir, "bin");
|
|
3657
|
+
const cacheRoot = join(dir, "cache");
|
|
3658
|
+
const cPath = join(dir, "program.ll");
|
|
3659
|
+
const outPath = join(dir, "program");
|
|
3660
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
3661
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
3662
|
+
const oldPath = process.env["PATH"];
|
|
3663
|
+
const oldRealLd = process.env["SCRIPTC_TEST_REAL_LD"];
|
|
3664
|
+
const programSource = [
|
|
3665
|
+
"define i32 @main() {",
|
|
3666
|
+
"entry:",
|
|
3667
|
+
" ret i32 7",
|
|
3668
|
+
"}",
|
|
3669
|
+
"",
|
|
3670
|
+
"define internal i64 @other_value() {",
|
|
3671
|
+
"entry:",
|
|
3672
|
+
" ret i64 9",
|
|
3673
|
+
"}",
|
|
3674
|
+
"",
|
|
3675
|
+
].join("\n");
|
|
3676
|
+
try {
|
|
3677
|
+
await Promise.all([mkdir(binDir), mkdir(cacheRoot, { mode: 0o700 })]);
|
|
3678
|
+
await Promise.all([
|
|
3679
|
+
symlink(clangExecutable, join(binDir, "clang")),
|
|
3680
|
+
...(process.platform === "linux"
|
|
3681
|
+
? [symlink(objcopyExecutable, join(binDir, "objcopy"))]
|
|
3682
|
+
: []),
|
|
3683
|
+
writeFile(join(binDir, "ld"), "#!/bin/sh\nfor arg in \"$@\"; do if [ \"$arg\" = -r ]; then exit 1; fi; done\nexec \"$SCRIPTC_TEST_REAL_LD\" \"$@\"\n"),
|
|
3684
|
+
writeFile(cPath, programSource),
|
|
3685
|
+
]);
|
|
3686
|
+
await chmod(join(binDir, "ld"), 0o755);
|
|
3687
|
+
process.env["PATH"] = binDir;
|
|
3688
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
3689
|
+
process.env["SCRIPTC_TEST_REAL_LD"] = ldExecutable;
|
|
3690
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
3691
|
+
await compileC({
|
|
3692
|
+
cPath,
|
|
3693
|
+
programShards: [
|
|
3694
|
+
{
|
|
3695
|
+
name: "program-f000.ll",
|
|
3696
|
+
source: "define i32 @main() {\nentry:\n ret i32 7\n}\ndeclare hidden i64 @other_value()\n",
|
|
3697
|
+
},
|
|
3698
|
+
{
|
|
3699
|
+
name: "program-f001.ll",
|
|
3700
|
+
source: "declare i32 @main()\ndefine hidden i64 @other_value() {\nentry:\n ret i64 9\n}\n",
|
|
3701
|
+
},
|
|
3702
|
+
],
|
|
3703
|
+
programPublicSymbols: ["main"],
|
|
3704
|
+
outPath,
|
|
3705
|
+
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
3706
|
+
optimization: "dev",
|
|
3707
|
+
});
|
|
3708
|
+
expect(spawnSync(outPath).status).toBe(7);
|
|
3709
|
+
// The canonical retry is valid output, but cannot occupy a key describing
|
|
3710
|
+
// a successful shard merge. Repairing the merge tool must retry the mode.
|
|
3711
|
+
expect(await readdir(join(cacheRoot, "bin")).catch(() => [])).toEqual([]);
|
|
3712
|
+
expect(await readdir(join(cacheRoot, "program-shard")).catch(() => [])).toEqual([]);
|
|
3713
|
+
}
|
|
3714
|
+
finally {
|
|
3715
|
+
if (oldCacheDir === undefined)
|
|
3716
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
3717
|
+
else
|
|
3718
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
3719
|
+
if (oldNoCache === undefined)
|
|
3720
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
3721
|
+
else
|
|
3722
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
3723
|
+
if (oldPath === undefined)
|
|
3724
|
+
delete process.env["PATH"];
|
|
3725
|
+
else
|
|
3726
|
+
process.env["PATH"] = oldPath;
|
|
3727
|
+
if (oldRealLd === undefined)
|
|
3728
|
+
delete process.env["SCRIPTC_TEST_REAL_LD"];
|
|
3729
|
+
else
|
|
3730
|
+
process.env["SCRIPTC_TEST_REAL_LD"] = oldRealLd;
|
|
3731
|
+
}
|
|
3732
|
+
});
|
|
3733
|
+
test.skipIf(!nativeShardMergeAvailable)("LLVM merged-object and archive caches separate shard projections and public-symbol keep sets", async () => {
|
|
3734
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-program-shard-abi-"));
|
|
3735
|
+
scratch.push(dir);
|
|
3736
|
+
const cacheRoot = join(dir, "cache");
|
|
3737
|
+
const cPath = join(dir, "program.ll");
|
|
3738
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
3739
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
3740
|
+
const programSource = [
|
|
3741
|
+
"define i64 @foo() {",
|
|
3742
|
+
"entry:",
|
|
3743
|
+
" ret i64 1",
|
|
3744
|
+
"}",
|
|
3745
|
+
"",
|
|
3746
|
+
"define i64 @bar() {",
|
|
3747
|
+
"entry:",
|
|
3748
|
+
" ret i64 2",
|
|
3749
|
+
"}",
|
|
3750
|
+
"",
|
|
3751
|
+
].join("\n");
|
|
3752
|
+
const programShards = [
|
|
3753
|
+
{
|
|
3754
|
+
name: "program-f000.ll",
|
|
3755
|
+
source: "define i64 @foo() {\nentry:\n ret i64 1\n}\ndeclare i64 @bar()\n",
|
|
3756
|
+
},
|
|
3757
|
+
{
|
|
3758
|
+
name: "program-f001.ll",
|
|
3759
|
+
source: "declare i64 @foo()\ndefine i64 @bar() {\nentry:\n ret i64 2\n}\n",
|
|
3760
|
+
},
|
|
3761
|
+
];
|
|
3762
|
+
try {
|
|
3763
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
3764
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
3765
|
+
await mkdir(cacheRoot, { mode: 0o700 });
|
|
3766
|
+
await writeFile(cPath, programSource);
|
|
3767
|
+
const build = async (symbol, outPath, shards = programShards) => {
|
|
3768
|
+
await compileLibArchive({
|
|
3769
|
+
cPath,
|
|
3770
|
+
programSource,
|
|
3771
|
+
programShards: shards,
|
|
3772
|
+
programPublicSymbols: [symbol],
|
|
3773
|
+
outPath,
|
|
3774
|
+
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
3775
|
+
optimization: "dev",
|
|
3776
|
+
});
|
|
3777
|
+
};
|
|
3778
|
+
await build("foo", join(dir, "foo.lib.a"));
|
|
3779
|
+
const barArchive = join(dir, "bar.lib.a");
|
|
3780
|
+
await build("bar", barArchive);
|
|
3781
|
+
expect(await completeArtifacts(cacheRoot, "lib")).toHaveLength(2);
|
|
3782
|
+
expect((await readdir(join(cacheRoot, "program-obj"))).filter((name) => !name.endsWith(".sha256"))).toHaveLength(2);
|
|
3783
|
+
const probeSource = join(dir, "probe.c");
|
|
3784
|
+
const probe = join(dir, "probe");
|
|
3785
|
+
await writeFile(probeSource, "long long bar(void);\nint main(void) { return bar() != 2; }\n");
|
|
3786
|
+
execFileSync("clang", [probeSource, barArchive, "-lm", "-o", probe]);
|
|
3787
|
+
expect(execFileSync(probe, { encoding: "utf8" })).toBe("");
|
|
3788
|
+
// The canonical TU and ABI stay fixed, but the exact shard projection
|
|
3789
|
+
// changes. The merged-object tier must not reuse the previous layout.
|
|
3790
|
+
await build("bar", join(dir, "bar-commented.lib.a"), [
|
|
3791
|
+
{ ...programShards[0], source: `; projection edit\n${programShards[0].source}` },
|
|
3792
|
+
programShards[1],
|
|
3793
|
+
]);
|
|
3794
|
+
expect(await completeArtifacts(cacheRoot, "lib")).toHaveLength(3);
|
|
3795
|
+
expect((await readdir(join(cacheRoot, "program-obj"))).filter((name) => !name.endsWith(".sha256"))).toHaveLength(3);
|
|
3796
|
+
}
|
|
3797
|
+
finally {
|
|
3798
|
+
if (oldCacheDir === undefined)
|
|
3799
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
3800
|
+
else
|
|
3801
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
3802
|
+
if (oldNoCache === undefined)
|
|
3803
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
3804
|
+
else
|
|
3805
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
3806
|
+
}
|
|
3807
|
+
});
|
|
3808
|
+
test.skipIf(process.platform === "win32" || clangExecutable === undefined || arExecutable === undefined ||
|
|
3809
|
+
ldExecutable === undefined || (process.platform === "linux" && objcopyExecutable === undefined))("LLVM merged-object caches follow in-place shard merge-tool replacements", async () => {
|
|
3810
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-program-shard-merge-tool-"));
|
|
3811
|
+
scratch.push(dir);
|
|
3812
|
+
const binDir = join(dir, "bin");
|
|
3813
|
+
const cacheRoot = join(dir, "cache");
|
|
3814
|
+
const mergeLog = join(dir, "merge.log");
|
|
3815
|
+
const cPath = join(dir, "program.ll");
|
|
3816
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
3817
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
3818
|
+
const oldPath = process.env["PATH"];
|
|
3819
|
+
const oldRealLd = process.env["SCRIPTC_TEST_REAL_LD"];
|
|
3820
|
+
const oldMergeLog = process.env["SCRIPTC_TEST_MERGE_LOG"];
|
|
3821
|
+
const programSource = [
|
|
3822
|
+
"define i64 @foo() {",
|
|
3823
|
+
"entry:",
|
|
3824
|
+
" ret i64 1",
|
|
3825
|
+
"}",
|
|
3826
|
+
"",
|
|
3827
|
+
"define i64 @bar() {",
|
|
3828
|
+
"entry:",
|
|
3829
|
+
" ret i64 2",
|
|
3830
|
+
"}",
|
|
3831
|
+
"",
|
|
3832
|
+
].join("\n");
|
|
3833
|
+
const programShards = [
|
|
3834
|
+
{
|
|
3835
|
+
name: "program-f000.ll",
|
|
3836
|
+
source: "define i64 @foo() {\nentry:\n ret i64 1\n}\ndeclare i64 @bar()\n",
|
|
3837
|
+
},
|
|
3838
|
+
{
|
|
3839
|
+
name: "program-f001.ll",
|
|
3840
|
+
source: "declare i64 @foo()\ndefine i64 @bar() {\nentry:\n ret i64 2\n}\n",
|
|
3841
|
+
},
|
|
3842
|
+
];
|
|
3843
|
+
const ldWrapper = (generation) => `#!/bin/sh
|
|
3844
|
+
printf '${generation}\\n' >> "$SCRIPTC_TEST_MERGE_LOG"
|
|
3845
|
+
exec "$SCRIPTC_TEST_REAL_LD" "$@"
|
|
3846
|
+
`;
|
|
3847
|
+
try {
|
|
3848
|
+
await Promise.all([mkdir(binDir), mkdir(cacheRoot, { mode: 0o700 })]);
|
|
3849
|
+
await Promise.all([
|
|
3850
|
+
symlink(clangExecutable, join(binDir, "clang")),
|
|
3851
|
+
symlink(arExecutable, join(binDir, "ar")),
|
|
3852
|
+
...(process.platform === "linux"
|
|
3853
|
+
? [symlink(objcopyExecutable, join(binDir, "objcopy"))]
|
|
3854
|
+
: []),
|
|
3855
|
+
writeFile(join(binDir, "ld"), ldWrapper("first")),
|
|
3856
|
+
writeFile(cPath, programSource),
|
|
3857
|
+
]);
|
|
3858
|
+
await chmod(join(binDir, "ld"), 0o755);
|
|
3859
|
+
process.env["PATH"] = binDir;
|
|
3860
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
3861
|
+
process.env["SCRIPTC_TEST_REAL_LD"] = ldExecutable;
|
|
3862
|
+
process.env["SCRIPTC_TEST_MERGE_LOG"] = mergeLog;
|
|
3863
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
3864
|
+
const build = (outPath) => compileLibArchive({
|
|
3865
|
+
cPath,
|
|
3866
|
+
programSource,
|
|
3867
|
+
programShards,
|
|
3868
|
+
programPublicSymbols: ["foo", "bar"],
|
|
3869
|
+
outPath,
|
|
3870
|
+
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
3871
|
+
optimization: "dev",
|
|
3872
|
+
});
|
|
3873
|
+
await build(join(dir, "first.lib.a"));
|
|
3874
|
+
await writeFile(join(binDir, "ld"), ldWrapper("second"));
|
|
3875
|
+
await chmod(join(binDir, "ld"), 0o755);
|
|
3876
|
+
await build(join(dir, "second.lib.a"));
|
|
3877
|
+
expect((await readFile(mergeLog, "utf8")).trim().split("\n")).toEqual(["first", "second"]);
|
|
3878
|
+
expect(await completeArtifacts(cacheRoot, "lib")).toHaveLength(2);
|
|
3879
|
+
expect((await readdir(join(cacheRoot, "program-obj"))).filter((name) => !name.endsWith(".sha256"))).toHaveLength(2);
|
|
3880
|
+
// The compiler and shard IR did not change; only merged outputs split by
|
|
3881
|
+
// localization-tool identity.
|
|
3882
|
+
expect((await readdir(join(cacheRoot, "program-shard"))).filter((name) => !name.endsWith(".sha256"))).toHaveLength(2);
|
|
3883
|
+
}
|
|
3884
|
+
finally {
|
|
3885
|
+
if (oldCacheDir === undefined)
|
|
3886
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
3887
|
+
else
|
|
3888
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
3889
|
+
if (oldNoCache === undefined)
|
|
3890
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
3891
|
+
else
|
|
3892
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
3893
|
+
if (oldPath === undefined)
|
|
3894
|
+
delete process.env["PATH"];
|
|
3895
|
+
else
|
|
3896
|
+
process.env["PATH"] = oldPath;
|
|
3897
|
+
if (oldRealLd === undefined)
|
|
3898
|
+
delete process.env["SCRIPTC_TEST_REAL_LD"];
|
|
3899
|
+
else
|
|
3900
|
+
process.env["SCRIPTC_TEST_REAL_LD"] = oldRealLd;
|
|
3901
|
+
if (oldMergeLog === undefined)
|
|
3902
|
+
delete process.env["SCRIPTC_TEST_MERGE_LOG"];
|
|
3903
|
+
else
|
|
3904
|
+
process.env["SCRIPTC_TEST_MERGE_LOG"] = oldMergeLog;
|
|
3905
|
+
}
|
|
3906
|
+
});
|
|
3907
|
+
test.skipIf(process.platform === "win32" || clangExecutable === undefined || arExecutable === undefined)("LLVM library shards fall back to the canonical TU without host merge tools", async () => {
|
|
3908
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-program-shard-fallback-"));
|
|
3909
|
+
scratch.push(dir);
|
|
3910
|
+
const binDir = join(dir, "bin");
|
|
3911
|
+
const cPath = join(dir, "program.ll");
|
|
3912
|
+
const outPath = join(dir, "program.lib.a");
|
|
3913
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
3914
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
3915
|
+
const oldPath = process.env["PATH"];
|
|
3916
|
+
const programSource = "define i64 @public_value() {\nentry:\n ret i64 7\n}\n";
|
|
3917
|
+
try {
|
|
3918
|
+
await mkdir(binDir);
|
|
3919
|
+
await Promise.all([
|
|
3920
|
+
symlink(clangExecutable, join(binDir, "clang")),
|
|
3921
|
+
symlink(arExecutable, join(binDir, "ar")),
|
|
3922
|
+
]);
|
|
3923
|
+
process.env["PATH"] = binDir;
|
|
3924
|
+
process.env["SCRIPTC_NO_CACHE"] = "1";
|
|
3925
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
3926
|
+
await writeFile(cPath, programSource);
|
|
3927
|
+
await compileLibArchive({
|
|
3928
|
+
cPath,
|
|
3929
|
+
programSource,
|
|
3930
|
+
// If compilation reaches either shard, this build fails. Missing ld
|
|
3931
|
+
// (and objcopy on Linux) must select the valid canonical TU instead.
|
|
3932
|
+
programShards: [
|
|
3933
|
+
{ name: "program-f000.ll", source: "not valid LLVM IR\n" },
|
|
3934
|
+
{ name: "program-f001.ll", source: "also not valid LLVM IR\n" },
|
|
3935
|
+
],
|
|
3936
|
+
programPublicSymbols: ["public_value"],
|
|
3937
|
+
outPath,
|
|
3938
|
+
optimization: "dev",
|
|
3939
|
+
});
|
|
3940
|
+
expect(await readFile(outPath)).not.toHaveLength(0);
|
|
3941
|
+
}
|
|
3942
|
+
finally {
|
|
3943
|
+
if (oldCacheDir === undefined)
|
|
3944
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
3945
|
+
else
|
|
3946
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
3947
|
+
if (oldNoCache === undefined)
|
|
3948
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
3949
|
+
else
|
|
3950
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
3951
|
+
if (oldPath === undefined)
|
|
3952
|
+
delete process.env["PATH"];
|
|
3953
|
+
else
|
|
3954
|
+
process.env["PATH"] = oldPath;
|
|
3955
|
+
}
|
|
3956
|
+
});
|
|
3957
|
+
test.skipIf(process.platform === "win32" || clangExecutable === undefined || arExecutable === undefined ||
|
|
3958
|
+
(process.platform === "linux" && objcopyExecutable === undefined))("LLVM library shards fall back when an available host merge tool fails", async () => {
|
|
3959
|
+
const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-program-shard-merge-failure-"));
|
|
3960
|
+
scratch.push(dir);
|
|
3961
|
+
const binDir = join(dir, "bin");
|
|
3962
|
+
const cacheRoot = join(dir, "cache");
|
|
3963
|
+
const cPath = join(dir, "program.ll");
|
|
3964
|
+
const outPath = join(dir, "program.lib.a");
|
|
3965
|
+
const oldCacheDir = process.env["SCRIPTC_CACHE_DIR"];
|
|
3966
|
+
const oldNoCache = process.env["SCRIPTC_NO_CACHE"];
|
|
3967
|
+
const oldPath = process.env["PATH"];
|
|
3968
|
+
const programSource = [
|
|
3969
|
+
"define i64 @public_value() {",
|
|
3970
|
+
"entry:",
|
|
3971
|
+
" ret i64 7",
|
|
3972
|
+
"}",
|
|
3973
|
+
"",
|
|
3974
|
+
"define i64 @other_value() {",
|
|
3975
|
+
"entry:",
|
|
3976
|
+
" ret i64 9",
|
|
3977
|
+
"}",
|
|
3978
|
+
"",
|
|
3979
|
+
].join("\n");
|
|
3980
|
+
try {
|
|
3981
|
+
await Promise.all([mkdir(binDir), mkdir(cacheRoot, { mode: 0o700 })]);
|
|
3982
|
+
await Promise.all([
|
|
3983
|
+
symlink(clangExecutable, join(binDir, "clang")),
|
|
3984
|
+
symlink(arExecutable, join(binDir, "ar")),
|
|
3985
|
+
...(process.platform === "linux"
|
|
3986
|
+
? [symlink(objcopyExecutable, join(binDir, "objcopy"))]
|
|
3987
|
+
: []),
|
|
3988
|
+
writeFile(join(binDir, "ld"), "#!/bin/sh\nexit 1\n"),
|
|
3989
|
+
writeFile(cPath, programSource),
|
|
3990
|
+
]);
|
|
3991
|
+
await chmod(join(binDir, "ld"), 0o755);
|
|
3992
|
+
process.env["PATH"] = binDir;
|
|
3993
|
+
process.env["SCRIPTC_CACHE_DIR"] = cacheRoot;
|
|
3994
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
3995
|
+
await compileLibArchive({
|
|
3996
|
+
cPath,
|
|
3997
|
+
programSource,
|
|
3998
|
+
programShards: [
|
|
3999
|
+
{
|
|
4000
|
+
name: "program-f000.ll",
|
|
4001
|
+
source: "define i64 @public_value() {\nentry:\n ret i64 7\n}\ndeclare i64 @other_value()\n",
|
|
4002
|
+
},
|
|
4003
|
+
{
|
|
4004
|
+
name: "program-f001.ll",
|
|
4005
|
+
source: "declare i64 @public_value()\ndefine i64 @other_value() {\nentry:\n ret i64 9\n}\n",
|
|
4006
|
+
},
|
|
4007
|
+
],
|
|
4008
|
+
programPublicSymbols: ["public_value", "other_value"],
|
|
4009
|
+
outPath,
|
|
4010
|
+
cacheIdentity: TEST_CACHE_IDENTITY,
|
|
4011
|
+
optimization: "dev",
|
|
4012
|
+
});
|
|
4013
|
+
const probeSource = join(dir, "probe.c");
|
|
4014
|
+
const probe = join(dir, "probe");
|
|
4015
|
+
await writeFile(probeSource, "long long public_value(void);\nint main(void) { return public_value() != 7; }\n");
|
|
4016
|
+
execFileSync(clangExecutable, [probeSource, outPath, "-lm", "-o", probe]);
|
|
4017
|
+
expect(execFileSync(probe, { encoding: "utf8" })).toBe("");
|
|
4018
|
+
// The canonical retry is valid for this invocation, but must not occupy a
|
|
4019
|
+
// key describing merged shard bytes. A repaired merge tool retries the
|
|
4020
|
+
// optimization instead of receiving a poisoned completed/object hit.
|
|
4021
|
+
expect(await readdir(join(cacheRoot, "lib")).catch(() => [])).toEqual([]);
|
|
4022
|
+
expect(await readdir(join(cacheRoot, "program-obj")).catch(() => [])).toEqual([]);
|
|
4023
|
+
expect(await readdir(join(cacheRoot, "program-shard")).catch(() => [])).toEqual([]);
|
|
4024
|
+
}
|
|
4025
|
+
finally {
|
|
4026
|
+
if (oldCacheDir === undefined)
|
|
4027
|
+
delete process.env["SCRIPTC_CACHE_DIR"];
|
|
4028
|
+
else
|
|
4029
|
+
process.env["SCRIPTC_CACHE_DIR"] = oldCacheDir;
|
|
4030
|
+
if (oldNoCache === undefined)
|
|
4031
|
+
delete process.env["SCRIPTC_NO_CACHE"];
|
|
4032
|
+
else
|
|
4033
|
+
process.env["SCRIPTC_NO_CACHE"] = oldNoCache;
|
|
4034
|
+
if (oldPath === undefined)
|
|
4035
|
+
delete process.env["PATH"];
|
|
4036
|
+
else
|
|
4037
|
+
process.env["PATH"] = oldPath;
|
|
4038
|
+
}
|
|
4039
|
+
});
|
|
2870
4040
|
test.skipIf(process.platform === "win32" || zigExecutable === undefined)("cross-ELF localized archives retain unreferenced identity roots", async () => {
|
|
2871
4041
|
const dir = await mkdtemp(join(tmpdir(), "scriptc-lib-localized-identity-"));
|
|
2872
4042
|
scratch.push(dir);
|
|
@@ -2933,4 +4103,4 @@ test.skipIf(process.platform === "win32" || zigExecutable === undefined)("cross-
|
|
|
2933
4103
|
process.env["SCRIPTC_TARGET"] = oldTarget;
|
|
2934
4104
|
}
|
|
2935
4105
|
});
|
|
2936
|
-
//# sourceMappingURL=
|
|
4106
|
+
//# sourceMappingURL=native-toolchain.test.js.map
|