@scriptc/compiler 0.2.4 → 0.2.6
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 +972 -245
- package/ambient/scriptc-overrides.d.ts +5 -1
- package/ambient/scriptc.d.ts +22 -2
- package/dist/backend/build-cache.d.ts +4 -0
- package/dist/backend/build-cache.js +16 -2
- package/dist/backend/build-cache.js.map +1 -1
- package/dist/backend/cycle-analysis.js +17 -5
- package/dist/backend/cycle-analysis.js.map +1 -1
- package/dist/backend/cycle-analysis.test.js +50 -11
- package/dist/backend/cycle-analysis.test.js.map +1 -1
- package/dist/backend/debug-symbols.js.map +1 -1
- package/dist/backend/executable-features.js +3 -1
- package/dist/backend/executable-features.js.map +1 -1
- package/dist/backend/external-c.d.ts +13 -6
- package/dist/backend/external-c.js +15 -3
- package/dist/backend/external-c.js.map +1 -1
- package/dist/backend/ffi-callbacks.js +6 -9
- package/dist/backend/ffi-callbacks.js.map +1 -1
- package/dist/backend/library-identity-markers.js.map +1 -1
- package/dist/backend/library-pack.d.ts +1 -1
- package/dist/backend/library-pack.js +81 -24
- package/dist/backend/library-pack.js.map +1 -1
- package/dist/backend/link-plan-core.d.ts +2 -0
- package/dist/backend/link-plan-core.js +10 -2
- package/dist/backend/link-plan-core.js.map +1 -1
- package/dist/backend/link-plan.d.ts +2 -1
- package/dist/backend/link-plan.js +7 -5
- package/dist/backend/link-plan.js.map +1 -1
- package/dist/backend/linker.d.ts +1 -1
- package/dist/backend/linker.js +42 -23
- package/dist/backend/linker.js.map +1 -1
- package/dist/backend/literals.js +5 -4
- package/dist/backend/literals.js.map +1 -1
- package/dist/backend/literals.test.js +30 -5
- package/dist/backend/literals.test.js.map +1 -1
- package/dist/backend/llvm/blocks.test.js +5 -2
- package/dist/backend/llvm/blocks.test.js.map +1 -1
- package/dist/backend/llvm/borrowed-inputs.js.map +1 -1
- package/dist/backend/llvm/borrowed-receivers.js +43 -19
- package/dist/backend/llvm/borrowed-receivers.js.map +1 -1
- package/dist/backend/llvm/byte-numbers.js +38 -5
- package/dist/backend/llvm/byte-numbers.js.map +1 -1
- package/dist/backend/llvm/byte-windows.d.ts +31 -0
- package/dist/backend/llvm/byte-windows.js +334 -0
- package/dist/backend/llvm/byte-windows.js.map +1 -0
- package/dist/backend/llvm/byte-windows.test.d.ts +1 -0
- package/dist/backend/llvm/byte-windows.test.js +244 -0
- package/dist/backend/llvm/byte-windows.test.js.map +1 -0
- package/dist/backend/llvm/bytes-bounds.js +73 -22
- package/dist/backend/llvm/bytes-bounds.js.map +1 -1
- package/dist/backend/llvm/call-lifetimes.d.ts +5 -2
- package/dist/backend/llvm/call-lifetimes.js +72 -19
- package/dist/backend/llvm/call-lifetimes.js.map +1 -1
- package/dist/backend/llvm/call-lifetimes.test.js +159 -28
- package/dist/backend/llvm/call-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/checked-value-lifetimes.d.ts +4 -0
- package/dist/backend/llvm/checked-value-lifetimes.js +14 -4
- package/dist/backend/llvm/checked-value-lifetimes.js.map +1 -1
- package/dist/backend/llvm/classes.d.ts +14 -1
- package/dist/backend/llvm/classes.js +94 -19
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/classes.test.d.ts +1 -0
- package/dist/backend/llvm/classes.test.js +99 -0
- package/dist/backend/llvm/classes.test.js.map +1 -0
- package/dist/backend/llvm/common.js +4 -1
- package/dist/backend/llvm/common.js.map +1 -1
- package/dist/backend/llvm/constant-callbacks.d.ts +11 -0
- package/dist/backend/llvm/constant-callbacks.js +213 -0
- package/dist/backend/llvm/constant-callbacks.js.map +1 -0
- package/dist/backend/llvm/constant-callbacks.test.d.ts +1 -0
- package/dist/backend/llvm/constant-callbacks.test.js +124 -0
- package/dist/backend/llvm/constant-callbacks.test.js.map +1 -0
- package/dist/backend/llvm/counted-loops.js +10 -3
- package/dist/backend/llvm/counted-loops.js.map +1 -1
- package/dist/backend/llvm/debug-info.js +33 -13
- package/dist/backend/llvm/debug-info.js.map +1 -1
- package/dist/backend/llvm/debug-info.test.js +28 -6
- package/dist/backend/llvm/debug-info.test.js.map +1 -1
- package/dist/backend/llvm/dyn.d.ts +1 -1
- package/dist/backend/llvm/dyn.js +75 -56
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +28 -3
- package/dist/backend/llvm/emitter.js +506 -145
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +247 -50
- package/dist/backend/llvm/emitter.test.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +25 -6
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.js +24 -4
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-callbacks.js +10 -4
- package/dist/backend/llvm/expr-callbacks.js.map +1 -1
- package/dist/backend/llvm/expr-calls.js +110 -31
- package/dist/backend/llvm/expr-calls.js.map +1 -1
- package/dist/backend/llvm/expr-containers.d.ts +2 -0
- package/dist/backend/llvm/expr-containers.js +112 -41
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-control.js +1 -3
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dispatch.js +3 -1
- package/dist/backend/llvm/expr-dispatch.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +55 -7
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-island.js +26 -6
- package/dist/backend/llvm/expr-island.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.js +95 -19
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.test.js +44 -2
- package/dist/backend/llvm/expr-primitives.test.js.map +1 -1
- package/dist/backend/llvm/expr-records.js +10 -9
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +44 -26
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.test.js +51 -14
- package/dist/backend/llvm/expr-stream-bridges.test.js.map +1 -1
- package/dist/backend/llvm/expr-stream-callbacks.js +34 -14
- package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
- package/dist/backend/llvm/integer-remainder.d.ts +7 -0
- package/dist/backend/llvm/integer-remainder.js +26 -0
- package/dist/backend/llvm/integer-remainder.js.map +1 -0
- package/dist/backend/llvm/integer-values.js +15 -4
- package/dist/backend/llvm/integer-values.js.map +1 -1
- package/dist/backend/llvm/integer-views.js +23 -7
- package/dist/backend/llvm/integer-views.js.map +1 -1
- package/dist/backend/llvm/integer-views.test.js +42 -7
- package/dist/backend/llvm/integer-views.test.js.map +1 -1
- package/dist/backend/llvm/layouts.js +1 -1
- package/dist/backend/llvm/layouts.js.map +1 -1
- package/dist/backend/llvm/lib-abi.js +20 -4
- package/dist/backend/llvm/lib-abi.js.map +1 -1
- package/dist/backend/llvm/lib-connect.js +7 -1
- package/dist/backend/llvm/lib-connect.js.map +1 -1
- package/dist/backend/llvm/lib-datagram.js +35 -10
- package/dist/backend/llvm/lib-datagram.js.map +1 -1
- package/dist/backend/llvm/lib-dispatch.js +42 -38
- package/dist/backend/llvm/lib-dispatch.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +33 -18
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-http2.js +60 -15
- package/dist/backend/llvm/lib-http2.js.map +1 -1
- package/dist/backend/llvm/lib-lookup.js +12 -5
- package/dist/backend/llvm/lib-lookup.js.map +1 -1
- package/dist/backend/llvm/lib-network.js +97 -31
- package/dist/backend/llvm/lib-network.js.map +1 -1
- package/dist/backend/llvm/lib-process.js +57 -14
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +122 -31
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/lib-stream.js +33 -14
- package/dist/backend/llvm/lib-stream.js.map +1 -1
- package/dist/backend/llvm/lib-test.js +21 -7
- package/dist/backend/llvm/lib-test.js.map +1 -1
- package/dist/backend/llvm/lib-tls.js +69 -20
- package/dist/backend/llvm/lib-tls.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.d.ts +9 -2
- package/dist/backend/llvm/local-array-reads.js +176 -89
- package/dist/backend/llvm/local-array-reads.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.test.js +394 -62
- package/dist/backend/llvm/local-array-reads.test.js.map +1 -1
- package/dist/backend/llvm/local-union-storage.d.ts +25 -0
- package/dist/backend/llvm/local-union-storage.js +109 -0
- package/dist/backend/llvm/local-union-storage.js.map +1 -0
- package/dist/backend/llvm/local-union-storage.test.d.ts +1 -0
- package/dist/backend/llvm/local-union-storage.test.js +210 -0
- package/dist/backend/llvm/local-union-storage.test.js.map +1 -0
- package/dist/backend/llvm/loop-array-borrows.d.ts +11 -0
- package/dist/backend/llvm/loop-array-borrows.js +70 -0
- package/dist/backend/llvm/loop-array-borrows.js.map +1 -0
- package/dist/backend/llvm/loop-array-borrows.test.d.ts +1 -0
- package/dist/backend/llvm/loop-array-borrows.test.js +145 -0
- package/dist/backend/llvm/loop-array-borrows.test.js.map +1 -0
- package/dist/backend/llvm/map-read-lifetimes.d.ts +11 -2
- package/dist/backend/llvm/map-read-lifetimes.js +64 -19
- package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
- package/dist/backend/llvm/map-read-lifetimes.test.js +89 -17
- package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/reference-effects.d.ts +5 -3
- package/dist/backend/llvm/reference-effects.js +74 -36
- package/dist/backend/llvm/reference-effects.js.map +1 -1
- package/dist/backend/llvm/reference-effects.test.js +92 -9
- package/dist/backend/llvm/reference-effects.test.js.map +1 -1
- package/dist/backend/llvm/shapes.d.ts +4 -1
- package/dist/backend/llvm/shapes.js +82 -24
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/shapes.test.js +3 -1
- package/dist/backend/llvm/shapes.test.js.map +1 -1
- package/dist/backend/llvm/split-loops.d.ts +48 -0
- package/dist/backend/llvm/split-loops.js +183 -0
- package/dist/backend/llvm/split-loops.js.map +1 -0
- package/dist/backend/llvm/split.js +4 -11
- package/dist/backend/llvm/split.js.map +1 -1
- package/dist/backend/llvm/split.test.js +80 -8
- package/dist/backend/llvm/split.test.js.map +1 -1
- package/dist/backend/llvm/stack-callbacks.d.ts +14 -0
- package/dist/backend/llvm/stack-callbacks.js +73 -0
- package/dist/backend/llvm/stack-callbacks.js.map +1 -0
- package/dist/backend/llvm/stack-captures.d.ts +25 -0
- package/dist/backend/llvm/stack-captures.js +150 -0
- package/dist/backend/llvm/stack-captures.js.map +1 -0
- package/dist/backend/llvm/stack-unions.js +14 -5
- package/dist/backend/llvm/stack-unions.js.map +1 -1
- package/dist/backend/llvm/stack-unions.test.js +151 -32
- package/dist/backend/llvm/stack-unions.test.js.map +1 -1
- package/dist/backend/llvm/string-construction.js.map +1 -1
- package/dist/backend/llvm/string-construction.test.js +32 -7
- package/dist/backend/llvm/string-construction.test.js.map +1 -1
- package/dist/backend/llvm/string-lifetimes.js +14 -8
- package/dist/backend/llvm/string-lifetimes.js.map +1 -1
- package/dist/backend/llvm/string-lifetimes.test.js +119 -29
- package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/string-slices.d.ts +20 -0
- package/dist/backend/llvm/string-slices.js +87 -0
- package/dist/backend/llvm/string-slices.js.map +1 -0
- package/dist/backend/llvm/string-slices.test.d.ts +1 -0
- package/dist/backend/llvm/string-slices.test.js +98 -0
- package/dist/backend/llvm/string-slices.test.js.map +1 -0
- package/dist/backend/llvm/switch-dispatch.d.ts +9 -0
- package/dist/backend/llvm/switch-dispatch.js +114 -0
- package/dist/backend/llvm/switch-dispatch.js.map +1 -0
- package/dist/backend/llvm/switch-dispatch.test.d.ts +1 -0
- package/dist/backend/llvm/switch-dispatch.test.js +83 -0
- package/dist/backend/llvm/switch-dispatch.test.js.map +1 -0
- package/dist/backend/llvm/unsupported.js.map +1 -1
- package/dist/backend/llvm/walkers.d.ts +4 -14
- package/dist/backend/llvm/walkers.js +58 -313
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/backend/mangle.js +9 -3
- package/dist/backend/mangle.js.map +1 -1
- package/dist/backend/may-throw.js +18 -8
- package/dist/backend/may-throw.js.map +1 -1
- package/dist/backend/may-throw.test.js +119 -23
- package/dist/backend/may-throw.test.js.map +1 -1
- package/dist/backend/native/artifact-stamps.d.ts +39 -0
- package/dist/backend/native/artifact-stamps.js +371 -0
- package/dist/backend/native/artifact-stamps.js.map +1 -0
- package/dist/backend/native/cache-warm.d.ts +24 -0
- package/dist/backend/native/cache-warm.js +80 -0
- package/dist/backend/native/cache-warm.js.map +1 -0
- package/dist/backend/native/compiler-fingerprint.d.ts +19 -0
- package/dist/backend/native/compiler-fingerprint.js +357 -0
- package/dist/backend/native/compiler-fingerprint.js.map +1 -0
- package/dist/backend/native/contracts.d.ts +316 -0
- package/dist/backend/native/contracts.js +2 -0
- package/dist/backend/native/contracts.js.map +1 -0
- package/dist/backend/native/dependency-files.d.ts +5 -0
- package/dist/backend/native/dependency-files.js +83 -0
- package/dist/backend/native/dependency-files.js.map +1 -0
- package/dist/backend/native/driver.d.ts +50 -0
- package/dist/backend/native/driver.js +294 -0
- package/dist/backend/native/driver.js.map +1 -0
- package/dist/backend/native/executable.d.ts +18 -0
- package/dist/backend/native/executable.js +1275 -0
- package/dist/backend/native/executable.js.map +1 -0
- package/dist/backend/native/library.d.ts +2 -0
- package/dist/backend/native/library.js +614 -0
- package/dist/backend/native/library.js.map +1 -0
- package/dist/backend/native/linker-fingerprint.d.ts +8 -0
- package/dist/backend/native/linker-fingerprint.js +174 -0
- package/dist/backend/native/linker-fingerprint.js.map +1 -0
- package/dist/backend/native/object-merge.d.ts +2 -0
- package/dist/backend/native/object-merge.js +154 -0
- package/dist/backend/native/object-merge.js.map +1 -0
- package/dist/backend/native/process.d.ts +17 -0
- package/dist/backend/native/process.js +42 -0
- package/dist/backend/native/process.js.map +1 -0
- package/dist/backend/native/program-shards.d.ts +21 -0
- package/dist/backend/native/program-shards.js +99 -0
- package/dist/backend/native/program-shards.js.map +1 -0
- package/dist/backend/native/runtime-inputs.d.ts +12 -0
- package/dist/backend/native/runtime-inputs.js +165 -0
- package/dist/backend/native/runtime-inputs.js.map +1 -0
- package/dist/backend/native/runtime-inputs.test.d.ts +1 -0
- package/dist/backend/native/runtime-inputs.test.js +64 -0
- package/dist/backend/native/runtime-inputs.test.js.map +1 -0
- package/dist/backend/native/runtime-objects.d.ts +16 -0
- package/dist/backend/native/runtime-objects.js +122 -0
- package/dist/backend/native/runtime-objects.js.map +1 -0
- package/dist/backend/native/session.d.ts +12 -0
- package/dist/backend/native/session.js +31 -0
- package/dist/backend/native/session.js.map +1 -0
- package/dist/backend/native/tool-identity.d.ts +37 -0
- package/dist/backend/native/tool-identity.js +310 -0
- package/dist/backend/native/tool-identity.js.map +1 -0
- package/dist/backend/native/trace-paths.d.ts +12 -0
- package/dist/backend/native/trace-paths.js +73 -0
- package/dist/backend/native/trace-paths.js.map +1 -0
- package/dist/backend/native/vendor.d.ts +1 -0
- package/dist/backend/native/vendor.js +12 -0
- package/dist/backend/native/vendor.js.map +1 -0
- package/dist/backend/native-codegen-core.d.ts +6 -0
- package/dist/backend/native-codegen-core.js +23 -1
- package/dist/backend/native-codegen-core.js.map +1 -1
- package/dist/backend/native-codegen.d.ts +5 -1
- package/dist/backend/native-codegen.js +52 -25
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/native-codegen.test.js +59 -11
- package/dist/backend/native-codegen.test.js.map +1 -1
- package/dist/backend/native-link-info-core.d.ts +1 -0
- package/dist/backend/native-link-info-core.js +9 -5
- package/dist/backend/native-link-info-core.js.map +1 -1
- package/dist/backend/native-link-info.js +14 -5
- package/dist/backend/native-link-info.js.map +1 -1
- package/dist/backend/native-link-info.test.js +58 -13
- package/dist/backend/native-link-info.test.js.map +1 -1
- package/dist/backend/native-toolchain.d.ts +17 -450
- package/dist/backend/native-toolchain.js +17 -3971
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/native-toolchain.test.js +103 -72
- package/dist/backend/native-toolchain.test.js.map +1 -1
- package/dist/backend/native-tools.d.ts +3 -1
- package/dist/backend/native-tools.js +30 -9
- package/dist/backend/native-tools.js.map +1 -1
- package/dist/backend/object-localize.js +17 -14
- package/dist/backend/object-localize.js.map +1 -1
- package/dist/backend/object-localize.test.js +59 -12
- package/dist/backend/object-localize.test.js.map +1 -1
- package/dist/backend/runtime-abi.d.ts +2 -2
- package/dist/backend/runtime-abi.js +2 -2
- package/dist/backend/runtime-pack-core.d.ts +1 -0
- package/dist/backend/runtime-pack-core.js +104 -50
- package/dist/backend/runtime-pack-core.js.map +1 -1
- package/dist/backend/runtime-pack-native.js +5 -3
- package/dist/backend/runtime-pack-native.js.map +1 -1
- package/dist/backend/runtime-pack-targets.test.js +126 -17
- package/dist/backend/runtime-pack-targets.test.js.map +1 -1
- package/dist/backend/runtime-pack.d.ts +1 -1
- package/dist/backend/runtime-pack.js +11 -12
- package/dist/backend/runtime-pack.js.map +1 -1
- package/dist/backend/runtime-pack.test.js +51 -25
- package/dist/backend/runtime-pack.test.js.map +1 -1
- package/dist/backend/source-locations.js.map +1 -1
- package/dist/backend/source-locations.test.js +7 -2
- package/dist/backend/source-locations.test.js.map +1 -1
- package/dist/backend/target-diagnostics.js +44 -14
- package/dist/backend/target-diagnostics.js.map +1 -1
- package/dist/backend/target-diagnostics.test.js +93 -20
- package/dist/backend/target-diagnostics.test.js.map +1 -1
- package/dist/backend/targets.js +230 -86
- package/dist/backend/targets.js.map +1 -1
- package/dist/backend/targets.test.js +32 -18
- package/dist/backend/targets.test.js.map +1 -1
- package/dist/backend/toolchain-environment.js +5 -1
- package/dist/backend/toolchain-environment.js.map +1 -1
- package/dist/backend/vendor-archives.d.ts +2 -2
- package/dist/backend/vendor-archives.js +84 -35
- package/dist/backend/vendor-archives.js.map +1 -1
- package/dist/backend/vendor-inputs.js +13 -1
- package/dist/backend/vendor-inputs.js.map +1 -1
- package/dist/backend/warm-cache.d.ts +1 -1
- package/dist/backend/warm-cache.js +33 -9
- package/dist/backend/warm-cache.js.map +1 -1
- package/dist/cli/command.js +39 -9
- package/dist/cli/command.js.map +1 -1
- package/dist/cli/output-options.js +14 -4
- package/dist/cli/output-options.js.map +1 -1
- package/dist/cli/paths.js.map +1 -1
- package/dist/cli/wasi-runner.js.map +1 -1
- package/dist/compat/fetch-profile.js +36 -35
- package/dist/compat/fetch-profile.js.map +1 -1
- package/dist/coverage/report.js +16 -17
- package/dist/coverage/report.js.map +1 -1
- package/dist/coverage/surface-manifest.js +112 -26
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.js +55 -16
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/executable/executable-cache.d.ts +2 -1
- package/dist/executable/executable-cache.js +79 -48
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +90 -26
- package/dist/executable/executable-cache.test.js.map +1 -1
- package/dist/executable/prepare.js +2 -2
- package/dist/executable/prepare.js.map +1 -1
- package/dist/ffi/ffi-manifest.js +78 -20
- package/dist/ffi/ffi-manifest.js.map +1 -1
- package/dist/ffi/ffi-manifest.test.js +96 -16
- package/dist/ffi/ffi-manifest.test.js.map +1 -1
- package/dist/format-integer.js.map +1 -1
- package/dist/frontend/analysis.js +12 -2
- package/dist/frontend/analysis.js.map +1 -1
- package/dist/frontend/arguments-usage.js.map +1 -1
- package/dist/frontend/binding-source.js +9 -3
- package/dist/frontend/binding-source.js.map +1 -1
- package/dist/frontend/builtin-modules.js +46 -2
- package/dist/frontend/builtin-modules.js.map +1 -1
- package/dist/frontend/cjs-resolve.js +40 -17
- package/dist/frontend/cjs-resolve.js.map +1 -1
- package/dist/frontend/cjs-syntax.js +31 -10
- package/dist/frontend/cjs-syntax.js.map +1 -1
- package/dist/frontend/comptime-node.test.js +10 -11
- package/dist/frontend/comptime-node.test.js.map +1 -1
- package/dist/frontend/dts-paths.js +7 -3
- package/dist/frontend/dts-paths.js.map +1 -1
- package/dist/frontend/fork-target.d.ts +5 -0
- package/dist/frontend/fork-target.js +55 -18
- package/dist/frontend/fork-target.js.map +1 -1
- package/dist/frontend/fork-target.test.d.ts +1 -0
- package/dist/frontend/fork-target.test.js +59 -0
- package/dist/frontend/fork-target.test.js.map +1 -0
- package/dist/frontend/function-completion.js +11 -6
- package/dist/frontend/function-completion.js.map +1 -1
- package/dist/frontend/helper-tokens.js +53 -6
- package/dist/frontend/helper-tokens.js.map +1 -1
- package/dist/frontend/helper-tokens.test.js +18 -2
- package/dist/frontend/helper-tokens.test.js.map +1 -1
- package/dist/frontend/inferred-js-diagnostics.js +32 -12
- package/dist/frontend/inferred-js-diagnostics.js.map +1 -1
- package/dist/frontend/inferred-js-diagnostics.test.js +11 -15
- package/dist/frontend/inferred-js-diagnostics.test.js.map +1 -1
- package/dist/frontend/input-tracker.js +11 -11
- package/dist/frontend/input-tracker.js.map +1 -1
- package/dist/frontend/input-tracker.test.js +4 -1
- package/dist/frontend/input-tracker.test.js.map +1 -1
- package/dist/frontend/lib-contract.js +38 -9
- package/dist/frontend/lib-contract.js.map +1 -1
- package/dist/frontend/literal-values.js.map +1 -1
- package/dist/frontend/literal-values.test.js +28 -8
- package/dist/frontend/literal-values.test.js.map +1 -1
- package/dist/frontend/lowering/abstract-equality.js +53 -10
- package/dist/frontend/lowering/abstract-equality.js.map +1 -1
- package/dist/frontend/lowering/array-indexed-mutation.d.ts +1 -1
- package/dist/frontend/lowering/array-indexed-mutation.js +108 -106
- package/dist/frontend/lowering/array-indexed-mutation.js.map +1 -1
- package/dist/frontend/lowering/array-values.js +37 -14
- package/dist/frontend/lowering/array-values.js.map +1 -1
- package/dist/frontend/lowering/binding-analysis.d.ts +50 -0
- package/dist/frontend/lowering/binding-analysis.js +376 -0
- package/dist/frontend/lowering/binding-analysis.js.map +1 -0
- package/dist/frontend/lowering/builtin-calls.d.ts +9 -0
- package/dist/frontend/lowering/builtin-calls.js +60 -0
- package/dist/frontend/lowering/builtin-calls.js.map +1 -0
- package/dist/frontend/lowering/builtins/arguments.d.ts +43 -0
- package/dist/frontend/lowering/builtins/arguments.js +247 -0
- package/dist/frontend/lowering/builtins/arguments.js.map +1 -0
- package/dist/frontend/lowering/builtins/async-context.d.ts +52 -0
- package/dist/frontend/lowering/builtins/async-context.js +436 -0
- package/dist/frontend/lowering/builtins/async-context.js.map +1 -0
- package/dist/frontend/lowering/builtins/atomics.d.ts +6 -0
- package/dist/frontend/lowering/builtins/atomics.js +82 -0
- package/dist/frontend/lowering/builtins/atomics.js.map +1 -0
- package/dist/frontend/lowering/builtins/callable-aliases.d.ts +13 -0
- package/dist/frontend/lowering/builtins/callable-aliases.js +87 -0
- package/dist/frontend/lowering/builtins/callable-aliases.js.map +1 -0
- package/dist/frontend/lowering/builtins/child-process-async.d.ts +18 -0
- package/dist/frontend/lowering/builtins/child-process-async.js +245 -0
- package/dist/frontend/lowering/builtins/child-process-async.js.map +1 -0
- package/dist/frontend/lowering/builtins/child-process-methods.d.ts +21 -0
- package/dist/frontend/lowering/builtins/child-process-methods.js +362 -0
- package/dist/frontend/lowering/builtins/child-process-methods.js.map +1 -0
- package/dist/frontend/lowering/builtins/child-process-sync.d.ts +4 -0
- package/dist/frontend/lowering/builtins/child-process-sync.js +639 -0
- package/dist/frontend/lowering/builtins/child-process-sync.js.map +1 -0
- package/dist/frontend/lowering/builtins/child-process.d.ts +55 -0
- package/dist/frontend/lowering/builtins/child-process.js +669 -0
- package/dist/frontend/lowering/builtins/child-process.js.map +1 -0
- package/dist/frontend/lowering/builtins/compression.d.ts +7 -0
- package/dist/frontend/lowering/builtins/compression.js +115 -0
- package/dist/frontend/lowering/builtins/compression.js.map +1 -0
- package/dist/frontend/lowering/builtins/console.d.ts +10 -0
- package/dist/frontend/lowering/builtins/console.js +38 -0
- package/dist/frontend/lowering/builtins/console.js.map +1 -0
- package/dist/frontend/lowering/builtins/constants.d.ts +24 -0
- package/dist/frontend/lowering/builtins/constants.js +141 -0
- package/dist/frontend/lowering/builtins/constants.js.map +1 -0
- package/dist/frontend/lowering/builtins/crypto.d.ts +22 -0
- package/dist/frontend/lowering/builtins/crypto.js +517 -0
- package/dist/frontend/lowering/builtins/crypto.js.map +1 -0
- package/dist/frontend/lowering/builtins/dates.d.ts +7 -0
- package/dist/frontend/lowering/builtins/dates.js +101 -0
- package/dist/frontend/lowering/builtins/dates.js.map +1 -0
- package/dist/frontend/lowering/builtins/environment.d.ts +34 -0
- package/dist/frontend/lowering/builtins/environment.js +95 -0
- package/dist/frontend/lowering/builtins/environment.js.map +1 -0
- package/dist/frontend/lowering/builtins/errors.d.ts +14 -0
- package/dist/frontend/lowering/builtins/errors.js +83 -0
- package/dist/frontend/lowering/builtins/errors.js.map +1 -0
- package/dist/frontend/lowering/builtins/filesystem-calls.d.ts +35 -0
- package/dist/frontend/lowering/builtins/filesystem-calls.js +725 -0
- package/dist/frontend/lowering/builtins/filesystem-calls.js.map +1 -0
- package/dist/frontend/lowering/builtins/filesystem.d.ts +108 -0
- package/dist/frontend/lowering/builtins/filesystem.js +1061 -0
- package/dist/frontend/lowering/builtins/filesystem.js.map +1 -0
- package/dist/frontend/lowering/builtins/ipc.d.ts +8 -0
- package/dist/frontend/lowering/builtins/ipc.js +76 -0
- package/dist/frontend/lowering/builtins/ipc.js.map +1 -0
- package/dist/frontend/lowering/builtins/json.d.ts +12 -0
- package/dist/frontend/lowering/builtins/json.js +387 -0
- package/dist/frontend/lowering/builtins/json.js.map +1 -0
- package/dist/frontend/lowering/builtins/module-bindings.d.ts +85 -0
- package/dist/frontend/lowering/builtins/module-bindings.js +346 -0
- package/dist/frontend/lowering/builtins/module-bindings.js.map +1 -0
- package/dist/frontend/lowering/builtins/module-calls.d.ts +8 -0
- package/dist/frontend/lowering/builtins/module-calls.js +308 -0
- package/dist/frontend/lowering/builtins/module-calls.js.map +1 -0
- package/dist/frontend/lowering/builtins/module-resolution.d.ts +38 -0
- package/dist/frontend/lowering/builtins/module-resolution.js +344 -0
- package/dist/frontend/lowering/builtins/module-resolution.js.map +1 -0
- package/dist/frontend/lowering/builtins/numbers.d.ts +15 -0
- package/dist/frontend/lowering/builtins/numbers.js +124 -0
- package/dist/frontend/lowering/builtins/numbers.js.map +1 -0
- package/dist/frontend/lowering/builtins/operating-system.d.ts +13 -0
- package/dist/frontend/lowering/builtins/operating-system.js +148 -0
- package/dist/frontend/lowering/builtins/operating-system.js.map +1 -0
- package/dist/frontend/lowering/builtins/performance.d.ts +13 -0
- package/dist/frontend/lowering/builtins/performance.js +100 -0
- package/dist/frontend/lowering/builtins/performance.js.map +1 -0
- package/dist/frontend/lowering/builtins/process.d.ts +50 -0
- package/dist/frontend/lowering/builtins/process.js +1138 -0
- package/dist/frontend/lowering/builtins/process.js.map +1 -0
- package/dist/frontend/lowering/builtins/promises.d.ts +14 -0
- package/dist/frontend/lowering/builtins/promises.js +356 -0
- package/dist/frontend/lowering/builtins/promises.js.map +1 -0
- package/dist/frontend/lowering/builtins/properties.d.ts +11 -0
- package/dist/frontend/lowering/builtins/properties.js +186 -0
- package/dist/frontend/lowering/builtins/properties.js.map +1 -0
- package/dist/frontend/lowering/builtins/querystring.d.ts +19 -0
- package/dist/frontend/lowering/builtins/querystring.js +150 -0
- package/dist/frontend/lowering/builtins/querystring.js.map +1 -0
- package/dist/frontend/lowering/builtins/readline.d.ts +13 -0
- package/dist/frontend/lowering/builtins/readline.js +138 -0
- package/dist/frontend/lowering/builtins/readline.js.map +1 -0
- package/dist/frontend/lowering/builtins/receiver-types.d.ts +5 -0
- package/dist/frontend/lowering/builtins/receiver-types.js +15 -0
- package/dist/frontend/lowering/builtins/receiver-types.js.map +1 -0
- package/dist/frontend/lowering/builtins/reflection.d.ts +13 -0
- package/dist/frontend/lowering/builtins/reflection.js +95 -0
- package/dist/frontend/lowering/builtins/reflection.js.map +1 -0
- package/dist/frontend/lowering/builtins/strings.d.ts +14 -0
- package/dist/frontend/lowering/builtins/strings.js +121 -0
- package/dist/frontend/lowering/builtins/strings.js.map +1 -0
- package/dist/frontend/lowering/builtins/text-codecs.d.ts +29 -0
- package/dist/frontend/lowering/builtins/text-codecs.js +590 -0
- package/dist/frontend/lowering/builtins/text-codecs.js.map +1 -0
- package/dist/frontend/lowering/builtins/timers.d.ts +22 -0
- package/dist/frontend/lowering/builtins/timers.js +180 -0
- package/dist/frontend/lowering/builtins/timers.js.map +1 -0
- package/dist/frontend/lowering/builtins/url.d.ts +37 -0
- package/dist/frontend/lowering/builtins/url.js +596 -0
- package/dist/frontend/lowering/builtins/url.js.map +1 -0
- package/dist/frontend/lowering/builtins/util-types.d.ts +7 -0
- package/dist/frontend/lowering/builtins/util-types.js +31 -0
- package/dist/frontend/lowering/builtins/util-types.js.map +1 -0
- package/dist/frontend/lowering/builtins/workers.d.ts +5 -0
- package/dist/frontend/lowering/builtins/workers.js +65 -0
- package/dist/frontend/lowering/builtins/workers.js.map +1 -0
- package/dist/frontend/lowering/call-arguments.d.ts +72 -0
- package/dist/frontend/lowering/call-arguments.js +618 -0
- package/dist/frontend/lowering/call-arguments.js.map +1 -0
- package/dist/frontend/lowering/call-signatures.d.ts +115 -0
- package/dist/frontend/lowering/call-signatures.js +719 -0
- package/dist/frontend/lowering/call-signatures.js.map +1 -0
- package/dist/frontend/lowering/callback-arg.js.map +1 -1
- package/dist/frontend/lowering/checked-iterable-spread.js +53 -10
- package/dist/frontend/lowering/checked-iterable-spread.js.map +1 -1
- package/dist/frontend/lowering/checked-promise-all.js +41 -9
- package/dist/frontend/lowering/checked-promise-all.js.map +1 -1
- package/dist/frontend/lowering/class-assertions.d.ts +7 -0
- package/dist/frontend/lowering/class-assertions.js +96 -0
- package/dist/frontend/lowering/class-assertions.js.map +1 -0
- package/dist/frontend/lowering/class-callbacks.d.ts +1 -1
- package/dist/frontend/lowering/class-callbacks.js +130 -27
- package/dist/frontend/lowering/class-callbacks.js.map +1 -1
- package/dist/frontend/lowering/class-construction.d.ts +1 -1
- package/dist/frontend/lowering/class-construction.js +247 -50
- package/dist/frontend/lowering/class-construction.js.map +1 -1
- package/dist/frontend/lowering/class-descriptors.js +109 -29
- package/dist/frontend/lowering/class-descriptors.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +6 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.js +2471 -465
- package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.test.js +284 -44
- package/dist/frontend/lowering/class-dynamic-dispatch.test.js.map +1 -1
- package/dist/frontend/lowering/class-instance-constructor.js +17 -5
- package/dist/frontend/lowering/class-instance-constructor.js.map +1 -1
- package/dist/frontend/lowering/class-method-values.js +199 -51
- package/dist/frontend/lowering/class-method-values.js.map +1 -1
- package/dist/frontend/lowering/class-protocols.d.ts +17 -0
- package/dist/frontend/lowering/class-protocols.js +126 -0
- package/dist/frontend/lowering/class-protocols.js.map +1 -0
- package/dist/frontend/lowering/class-prototypes.js +345 -86
- package/dist/frontend/lowering/class-prototypes.js.map +1 -1
- package/dist/frontend/lowering/class-runtime-statics.d.ts +1 -1
- package/dist/frontend/lowering/class-runtime-statics.js +80 -17
- package/dist/frontend/lowering/class-runtime-statics.js.map +1 -1
- package/dist/frontend/lowering/class-static-data.js +76 -16
- package/dist/frontend/lowering/class-static-data.js.map +1 -1
- package/dist/frontend/lowering/coercions/builders.d.ts +13 -0
- package/dist/frontend/lowering/coercions/builders.js +77 -0
- package/dist/frontend/lowering/coercions/builders.js.map +1 -0
- package/dist/frontend/lowering/coercions/function-adapters.d.ts +58 -0
- package/dist/frontend/lowering/coercions/function-adapters.js +430 -0
- package/dist/frontend/lowering/coercions/function-adapters.js.map +1 -0
- package/dist/frontend/lowering/coercions/island.d.ts +37 -0
- package/dist/frontend/lowering/coercions/island.js +350 -0
- package/dist/frontend/lowering/coercions/island.js.map +1 -0
- package/dist/frontend/lowering/coercions/state.d.ts +14 -0
- package/dist/frontend/lowering/coercions/state.js +16 -0
- package/dist/frontend/lowering/coercions/state.js.map +1 -0
- package/dist/frontend/lowering/coercions/structural-plans.d.ts +40 -0
- package/dist/frontend/lowering/coercions/structural-plans.js +447 -0
- package/dist/frontend/lowering/coercions/structural-plans.js.map +1 -0
- package/dist/frontend/lowering/coercions/structural.d.ts +63 -0
- package/dist/frontend/lowering/coercions/structural.js +616 -0
- package/dist/frontend/lowering/coercions/structural.js.map +1 -0
- package/dist/frontend/lowering/coercions/unions.d.ts +65 -0
- package/dist/frontend/lowering/coercions/unions.js +318 -0
- package/dist/frontend/lowering/coercions/unions.js.map +1 -0
- package/dist/frontend/lowering/collection-ingestion.js +355 -72
- package/dist/frontend/lowering/collection-ingestion.js.map +1 -1
- package/dist/frontend/lowering/containers/array-callbacks.d.ts +88 -0
- package/dist/frontend/lowering/containers/array-callbacks.js +1611 -0
- package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -0
- package/dist/frontend/lowering/containers/array-construction.d.ts +42 -0
- package/dist/frontend/lowering/containers/array-construction.js +1044 -0
- package/dist/frontend/lowering/containers/array-construction.js.map +1 -0
- package/dist/frontend/lowering/containers/array-iteration.d.ts +16 -0
- package/dist/frontend/lowering/containers/array-iteration.js +95 -0
- package/dist/frontend/lowering/containers/array-iteration.js.map +1 -0
- package/dist/frontend/lowering/containers/array-methods.d.ts +27 -0
- package/dist/frontend/lowering/containers/array-methods.js +1854 -0
- package/dist/frontend/lowering/containers/array-methods.js.map +1 -0
- package/dist/frontend/lowering/containers/bytes.d.ts +17 -17
- package/dist/frontend/lowering/containers/bytes.js +430 -138
- package/dist/frontend/lowering/containers/bytes.js.map +1 -1
- package/dist/frontend/lowering/containers/callback-arguments.d.ts +26 -0
- package/dist/frontend/lowering/containers/callback-arguments.js +179 -0
- package/dist/frontend/lowering/containers/callback-arguments.js.map +1 -0
- package/dist/frontend/lowering/containers/collection-grouping.d.ts +19 -0
- package/dist/frontend/lowering/containers/collection-grouping.js +297 -0
- package/dist/frontend/lowering/containers/collection-grouping.js.map +1 -0
- package/dist/frontend/lowering/containers/collection-methods.d.ts +57 -0
- package/dist/frontend/lowering/containers/collection-methods.js +1314 -0
- package/dist/frontend/lowering/containers/collection-methods.js.map +1 -0
- package/dist/frontend/lowering/containers/dynamic-receivers.d.ts +18 -0
- package/dist/frontend/lowering/containers/dynamic-receivers.js +114 -0
- package/dist/frontend/lowering/containers/dynamic-receivers.js.map +1 -0
- package/dist/frontend/lowering/containers/for-of.d.ts +60 -0
- package/dist/frontend/lowering/containers/for-of.js +643 -0
- package/dist/frontend/lowering/containers/for-of.js.map +1 -0
- package/dist/frontend/lowering/containers/indexed-objects.d.ts +132 -0
- package/dist/frontend/lowering/containers/indexed-objects.js +1743 -0
- package/dist/frontend/lowering/containers/indexed-objects.js.map +1 -0
- package/dist/frontend/lowering/containers/iterator-helpers.d.ts +20 -0
- package/dist/frontend/lowering/containers/iterator-helpers.js +809 -0
- package/dist/frontend/lowering/containers/iterator-helpers.js.map +1 -0
- package/dist/frontend/lowering/containers/string-and-regexp.js +601 -160
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
- package/dist/frontend/lowering/containers/string-replacement.js +213 -63
- package/dist/frontend/lowering/containers/string-replacement.js.map +1 -1
- package/dist/frontend/lowering/crypto-tables.js +263 -64
- package/dist/frontend/lowering/crypto-tables.js.map +1 -1
- package/dist/frontend/lowering/deferred-module-initializers.js +172 -68
- package/dist/frontend/lowering/deferred-module-initializers.js.map +1 -1
- package/dist/frontend/lowering/deferred-module-initializers.test.js +41 -8
- package/dist/frontend/lowering/deferred-module-initializers.test.js.map +1 -1
- package/dist/frontend/lowering/error-methods.js +339 -65
- package/dist/frontend/lowering/error-methods.js.map +1 -1
- package/dist/frontend/lowering/expressions/evaluation-safety.js +3 -2
- package/dist/frontend/lowering/expressions/evaluation-safety.js.map +1 -1
- package/dist/frontend/lowering/expressions/global-symbols.js +2 -1
- package/dist/frontend/lowering/expressions/global-symbols.js.map +1 -1
- package/dist/frontend/lowering/expressions/native-proxy.js +11 -5
- package/dist/frontend/lowering/expressions/native-proxy.js.map +1 -1
- package/dist/frontend/lowering/expressions/nullish-assignment.js +67 -13
- package/dist/frontend/lowering/expressions/nullish-assignment.js.map +1 -1
- package/dist/frontend/lowering/expressions/object-literals.js +588 -165
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/optional-chains.d.ts +1 -1
- package/dist/frontend/lowering/expressions/optional-chains.js +68 -17
- package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
- package/dist/frontend/lowering/expressions/union-field-write.js +45 -18
- package/dist/frontend/lowering/expressions/union-field-write.js.map +1 -1
- package/dist/frontend/lowering/function-context.js +5 -1
- package/dist/frontend/lowering/function-context.js.map +1 -1
- package/dist/frontend/lowering/function-returns.d.ts +67 -0
- package/dist/frontend/lowering/function-returns.js +392 -0
- package/dist/frontend/lowering/function-returns.js.map +1 -0
- package/dist/frontend/lowering/generator-delegation.d.ts +1 -1
- package/dist/frontend/lowering/generator-delegation.js +171 -39
- package/dist/frontend/lowering/generator-delegation.js.map +1 -1
- package/dist/frontend/lowering/generator-dynamic-dispatch.d.ts +1 -1
- package/dist/frontend/lowering/generator-dynamic-dispatch.js +176 -36
- package/dist/frontend/lowering/generator-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/generic-functions.d.ts +271 -0
- package/dist/frontend/lowering/generic-functions.js +1730 -0
- package/dist/frontend/lowering/generic-functions.js.map +1 -0
- package/dist/frontend/lowering/import-meta.d.ts +1 -1
- package/dist/frontend/lowering/import-meta.js +37 -14
- package/dist/frontend/lowering/import-meta.js.map +1 -1
- package/dist/frontend/lowering/indexed-comparison.js +6 -2
- package/dist/frontend/lowering/indexed-comparison.js.map +1 -1
- package/dist/frontend/lowering/instantiation-path.d.ts +8 -0
- package/dist/frontend/lowering/instantiation-path.js +10 -0
- package/dist/frontend/lowering/instantiation-path.js.map +1 -0
- package/dist/frontend/lowering/instantiation-path.test.d.ts +1 -0
- package/dist/frontend/lowering/instantiation-path.test.js +23 -0
- package/dist/frontend/lowering/instantiation-path.test.js.map +1 -0
- package/dist/frontend/lowering/iterator-consumption.d.ts +9 -0
- package/dist/frontend/lowering/iterator-consumption.js +91 -0
- package/dist/frontend/lowering/iterator-consumption.js.map +1 -0
- package/dist/frontend/lowering/lib-boundary.js +40 -17
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lib-boundary.test.js +49 -11
- package/dist/frontend/lowering/lib-boundary.test.js.map +1 -1
- package/dist/frontend/lowering/lower-array-sort.d.ts +0 -19
- package/dist/frontend/lowering/lower-array-sort.js +331 -566
- package/dist/frontend/lowering/lower-array-sort.js.map +1 -1
- package/dist/frontend/lowering/lower-assert.js +248 -43
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-async-iteration.js +71 -16
- package/dist/frontend/lowering/lower-async-iteration.js.map +1 -1
- package/dist/frontend/lowering/lower-builtin-values.js +430 -77
- package/dist/frontend/lowering/lower-builtin-values.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +39 -558
- package/dist/frontend/lowering/lower-calls.js +2628 -5669
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +248 -242
- package/dist/frontend/lowering/lower-classes.js +1820 -775
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-comptime.d.ts +35 -35
- package/dist/frontend/lowering/lower-comptime.js +39 -37
- package/dist/frontend/lowering/lower-comptime.js.map +1 -1
- package/dist/frontend/lowering/lower-dgram.js +59 -31
- package/dist/frontend/lowering/lower-dgram.js.map +1 -1
- package/dist/frontend/lowering/lower-enums.js +5 -2
- package/dist/frontend/lowering/lower-enums.js.map +1 -1
- package/dist/frontend/lowering/lower-event-emitter.js +323 -86
- package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-expando.js +19 -7
- package/dist/frontend/lowering/lower-expando.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +194 -194
- package/dist/frontend/lowering/lower-exprs.js +3473 -1160
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-functions.d.ts +23 -0
- package/dist/frontend/lowering/lower-functions.js +449 -0
- package/dist/frontend/lowering/lower-functions.js.map +1 -0
- package/dist/frontend/lowering/lower-generators.js +121 -32
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-global-value.js +15 -4
- package/dist/frontend/lowering/lower-global-value.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +479 -89
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +79 -79
- package/dist/frontend/lowering/lower-island.js +366 -274
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-island.test.js +7 -2
- package/dist/frontend/lowering/lower-island.test.js.map +1 -1
- package/dist/frontend/lowering/lower-mixins.js +17 -8
- package/dist/frontend/lowering/lower-mixins.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.d.ts +95 -94
- package/dist/frontend/lowering/lower-modules.js +655 -329
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.test.js +91 -20
- package/dist/frontend/lowering/lower-modules.test.js.map +1 -1
- package/dist/frontend/lowering/lower-namespaces.js +25 -8
- package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
- package/dist/frontend/lowering/lower-node-module.js +115 -34
- package/dist/frontend/lowering/lower-node-module.js.map +1 -1
- package/dist/frontend/lowering/lower-server.js +1210 -290
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +148 -148
- package/dist/frontend/lowering/lower-stmts.js +2324 -994
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.test.js +13 -5
- package/dist/frontend/lowering/lower-stmts.test.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +363 -91
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lower-system-errors.js +30 -8
- package/dist/frontend/lowering/lower-system-errors.js.map +1 -1
- package/dist/frontend/lowering/lower-test.js +40 -13
- package/dist/frontend/lowering/lower-test.js.map +1 -1
- package/dist/frontend/lowering/lower-timers.d.ts +31 -0
- package/dist/frontend/lowering/lower-timers.js +336 -0
- package/dist/frontend/lowering/lower-timers.js.map +1 -0
- package/dist/frontend/lowering/lower-url.d.ts +1 -1
- package/dist/frontend/lowering/lower-url.js +46 -13
- package/dist/frontend/lowering/lower-url.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +34 -352
- package/dist/frontend/lowering/lowerer.js +1074 -2712
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/lowerer.test.js +85 -17
- package/dist/frontend/lowering/lowerer.test.js.map +1 -1
- package/dist/frontend/lowering/module-namespace-elements.js +43 -8
- package/dist/frontend/lowering/module-namespace-elements.js.map +1 -1
- package/dist/frontend/lowering/native-ffi.d.ts +21 -1
- package/dist/frontend/lowering/native-ffi.js +487 -18
- package/dist/frontend/lowering/native-ffi.js.map +1 -1
- package/dist/frontend/lowering/object-enumeration-receiver.js +55 -10
- package/dist/frontend/lowering/object-enumeration-receiver.js.map +1 -1
- package/dist/frontend/lowering/object-factory-new.d.ts +1 -1
- package/dist/frontend/lowering/object-factory-new.js +36 -11
- package/dist/frontend/lowering/object-factory-new.js.map +1 -1
- package/dist/frontend/lowering/optional-arguments.js +49 -20
- package/dist/frontend/lowering/optional-arguments.js.map +1 -1
- package/dist/frontend/lowering/pairs-snapshot.js +66 -14
- package/dist/frontend/lowering/pairs-snapshot.js.map +1 -1
- package/dist/frontend/lowering/runtime-optional-locals.js +3 -1
- package/dist/frontend/lowering/runtime-optional-locals.js.map +1 -1
- package/dist/frontend/lowering/runtime-optional-locals.test.js.map +1 -1
- package/dist/frontend/lowering/sanitize-class-types.js +4 -1
- package/dist/frontend/lowering/sanitize-class-types.js.map +1 -1
- package/dist/frontend/lowering/sanitize-class-types.test.js +86 -18
- package/dist/frontend/lowering/sanitize-class-types.test.js.map +1 -1
- package/dist/frontend/lowering/split-consumers.d.ts +7 -0
- package/dist/frontend/lowering/split-consumers.js +64 -0
- package/dist/frontend/lowering/split-consumers.js.map +1 -0
- package/dist/frontend/lowering/static-this.js +13 -6
- package/dist/frontend/lowering/static-this.js.map +1 -1
- package/dist/frontend/lowering/string-index.js +32 -10
- package/dist/frontend/lowering/string-index.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +45 -46
- package/dist/frontend/lowering/surfaces.js +1129 -256
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/lowering/symbol-fields.js +40 -15
- package/dist/frontend/lowering/symbol-fields.js.map +1 -1
- package/dist/frontend/lowering/symbol-methods.js.map +1 -1
- package/dist/frontend/lowering/text-decoder-encoding.js +236 -24
- package/dist/frontend/lowering/text-decoder-encoding.js.map +1 -1
- package/dist/frontend/lowering/text-decoder-encoding.test.js +84 -12
- package/dist/frontend/lowering/text-decoder-encoding.test.js.map +1 -1
- package/dist/frontend/lowering/union-narrow.js +59 -16
- package/dist/frontend/lowering/union-narrow.js.map +1 -1
- package/dist/frontend/lowering/union-narrow.test.js +24 -7
- package/dist/frontend/lowering/union-narrow.test.js.map +1 -1
- package/dist/frontend/lowering/union-retag.js +155 -40
- package/dist/frontend/lowering/union-retag.js.map +1 -1
- package/dist/frontend/lowering/union-retag.test.js +131 -33
- package/dist/frontend/lowering/union-retag.test.js.map +1 -1
- package/dist/frontend/lowering/unregistered-class-types.js.map +1 -1
- package/dist/frontend/lowering/unregistered-class-types.test.js +34 -8
- package/dist/frontend/lowering/unregistered-class-types.test.js.map +1 -1
- package/dist/frontend/module-source-candidates.d.ts +10 -0
- package/dist/frontend/module-source-candidates.js +40 -0
- package/dist/frontend/module-source-candidates.js.map +1 -0
- package/dist/frontend/module-source-candidates.test.d.ts +1 -0
- package/dist/frontend/module-source-candidates.test.js +53 -0
- package/dist/frontend/module-source-candidates.test.js.map +1 -0
- package/dist/frontend/module-syntax.js +21 -8
- package/dist/frontend/module-syntax.js.map +1 -1
- package/dist/frontend/module-syntax.test.js +37 -13
- package/dist/frontend/module-syntax.test.js.map +1 -1
- package/dist/frontend/npm-fetch-analysis.js +13 -4
- package/dist/frontend/npm-fetch-analysis.js.map +1 -1
- package/dist/frontend/npm-fetch-analysis.test.js +33 -10
- package/dist/frontend/npm-fetch-analysis.test.js.map +1 -1
- package/dist/frontend/npm-fetch-node.js.map +1 -1
- package/dist/frontend/npm-fetch-syntax.js +4 -9
- package/dist/frontend/npm-fetch-syntax.js.map +1 -1
- package/dist/frontend/npm-node.js +3 -1
- package/dist/frontend/npm-node.js.map +1 -1
- package/dist/frontend/npm-static-bundled-cjs.js +88 -29
- package/dist/frontend/npm-static-bundled-cjs.js.map +1 -1
- package/dist/frontend/npm-static-bundled-cjs.test.js +29 -10
- package/dist/frontend/npm-static-bundled-cjs.test.js.map +1 -1
- package/dist/frontend/npm-static-declaration-syntax.js +148 -56
- package/dist/frontend/npm-static-declaration-syntax.js.map +1 -1
- package/dist/frontend/npm-static-declarations.d.ts +1 -1
- package/dist/frontend/npm-static-declarations.js.map +1 -1
- package/dist/frontend/npm-static-declarations.test.js +33 -7
- package/dist/frontend/npm-static-declarations.test.js.map +1 -1
- package/dist/frontend/npm-static-prune.js +26 -12
- package/dist/frontend/npm-static-prune.js.map +1 -1
- package/dist/frontend/npm-static-prune.test.js +17 -3
- package/dist/frontend/npm-static-prune.test.js.map +1 -1
- package/dist/frontend/npm-static-rewrite-host.js +2 -3
- package/dist/frontend/npm-static-rewrite-host.js.map +1 -1
- package/dist/frontend/npm-static-rewrite-syntax.js +64 -21
- package/dist/frontend/npm-static-rewrite-syntax.js.map +1 -1
- package/dist/frontend/npm-static-rewrite.js.map +1 -1
- package/dist/frontend/npm-static-subtyping.js +9 -4
- package/dist/frontend/npm-static-subtyping.js.map +1 -1
- package/dist/frontend/npm-static-subtyping.test.js +30 -6
- package/dist/frontend/npm-static-subtyping.test.js.map +1 -1
- package/dist/frontend/npm-static.js +15 -4
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/npm.js +122 -36
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/npm.test.js +30 -10
- package/dist/frontend/npm.test.js.map +1 -1
- package/dist/frontend/pipeline-native.js.map +1 -1
- package/dist/frontend/pipeline.js +25 -8
- package/dist/frontend/pipeline.js.map +1 -1
- package/dist/frontend/pipeline.test.js +21 -7
- package/dist/frontend/pipeline.test.js.map +1 -1
- package/dist/frontend/program-analysis.d.ts +2 -0
- package/dist/frontend/program-analysis.js +2 -0
- package/dist/frontend/program-analysis.js.map +1 -1
- package/dist/frontend/program-node.js +5 -3
- package/dist/frontend/program-node.js.map +1 -1
- package/dist/frontend/program.d.ts +2 -2
- package/dist/frontend/program.js +248 -127
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/program.test.js +27 -27
- package/dist/frontend/program.test.js.map +1 -1
- package/dist/frontend/provenance-core.js +8 -4
- package/dist/frontend/provenance-core.js.map +1 -1
- package/dist/frontend/provenance-registry.js +1 -4
- package/dist/frontend/provenance-registry.js.map +1 -1
- package/dist/frontend/resolve.js +32 -20
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/resolve.test.js +1 -1
- package/dist/frontend/resolve.test.js.map +1 -1
- package/dist/frontend/runtime-resolve.js +13 -4
- package/dist/frontend/runtime-resolve.js.map +1 -1
- package/dist/frontend/runtime-resolve.test.js +25 -9
- package/dist/frontend/runtime-resolve.test.js.map +1 -1
- package/dist/frontend/services-node.js +6 -2
- package/dist/frontend/services-node.js.map +1 -1
- package/dist/frontend/services-node.test.js +18 -6
- package/dist/frontend/services-node.test.js.map +1 -1
- package/dist/frontend/services.js +8 -4
- package/dist/frontend/services.js.map +1 -1
- package/dist/frontend/services.test.js +6 -2
- package/dist/frontend/services.test.js.map +1 -1
- package/dist/frontend/ts7/api.js +12 -4
- package/dist/frontend/ts7/api.js.map +1 -1
- package/dist/frontend/ts7/ast-bytes.js +10 -3
- package/dist/frontend/ts7/ast-bytes.js.map +1 -1
- package/dist/frontend/ts7/ast-bytes.test.js +23 -4
- package/dist/frontend/ts7/ast-bytes.test.js.map +1 -1
- package/dist/frontend/ts7/ast-guards.test.js +14 -5
- package/dist/frontend/ts7/ast-guards.test.js.map +1 -1
- package/dist/frontend/ts7/ast-node.d.ts +6 -1
- package/dist/frontend/ts7/ast-node.js +490 -172
- package/dist/frontend/ts7/ast-node.js.map +1 -1
- package/dist/frontend/ts7/ast-node.test.js +150 -36
- package/dist/frontend/ts7/ast-node.test.js.map +1 -1
- package/dist/frontend/ts7/ast-text.js +13 -3
- package/dist/frontend/ts7/ast-text.js.map +1 -1
- package/dist/frontend/ts7/ast-text.test.js.map +1 -1
- package/dist/frontend/ts7/ast-types.js.map +1 -1
- package/dist/frontend/ts7/ast-wire.d.ts +5 -1
- package/dist/frontend/ts7/ast-wire.js +83 -20
- package/dist/frontend/ts7/ast-wire.js.map +1 -1
- package/dist/frontend/ts7/ast-wire.test.js +53 -8
- package/dist/frontend/ts7/ast-wire.test.js.map +1 -1
- package/dist/frontend/ts7/ast.d.ts +2 -1
- package/dist/frontend/ts7/ast.js +53 -30
- package/dist/frontend/ts7/ast.js.map +1 -1
- package/dist/frontend/ts7/census-check.js +1 -1
- package/dist/frontend/ts7/census-check.js.map +1 -1
- package/dist/frontend/ts7/checker-cache.js.map +1 -1
- package/dist/frontend/ts7/checker.d.ts +8 -2
- package/dist/frontend/ts7/checker.js +46 -8
- package/dist/frontend/ts7/checker.js.map +1 -1
- package/dist/frontend/ts7/checker.test.js +96 -17
- package/dist/frontend/ts7/checker.test.js.map +1 -1
- package/dist/frontend/ts7/enums.test.js +9 -4
- package/dist/frontend/ts7/enums.test.js.map +1 -1
- package/dist/frontend/ts7/native-api.js.map +1 -1
- package/dist/frontend/ts7/native-host.js.map +1 -1
- package/dist/frontend/ts7/native-process.js +9 -3
- package/dist/frontend/ts7/native-process.js.map +1 -1
- package/dist/frontend/ts7/program-adapter.js +1 -1
- package/dist/frontend/ts7/program-adapter.js.map +1 -1
- package/dist/frontend/ts7/program-host.d.ts +7 -0
- package/dist/frontend/ts7/program-host.js +33 -6
- package/dist/frontend/ts7/program-host.js.map +1 -1
- package/dist/frontend/ts7/program-host.test.js +72 -11
- package/dist/frontend/ts7/program-host.test.js.map +1 -1
- package/dist/frontend/ts7/rpc-api.js +3 -1
- package/dist/frontend/ts7/rpc-api.js.map +1 -1
- package/dist/frontend/ts7/rpc-client.js +15 -5
- package/dist/frontend/ts7/rpc-client.js.map +1 -1
- package/dist/frontend/ts7/rpc-client.test.js +26 -8
- package/dist/frontend/ts7/rpc-client.test.js.map +1 -1
- package/dist/frontend/ts7/rpc-filesystem.js.map +1 -1
- package/dist/frontend/ts7/rpc-filesystem.test.js +43 -10
- package/dist/frontend/ts7/rpc-filesystem.test.js.map +1 -1
- package/dist/frontend/ts7/rpc-process.js +16 -6
- package/dist/frontend/ts7/rpc-process.js.map +1 -1
- package/dist/frontend/ts7/rpc-process.test.js.map +1 -1
- package/dist/frontend/ts7/rpc-wire.d.ts +1 -0
- package/dist/frontend/ts7/rpc-wire.js +41 -14
- package/dist/frontend/ts7/rpc-wire.js.map +1 -1
- package/dist/frontend/ts7/rpc-wire.test.js +36 -6
- package/dist/frontend/ts7/rpc-wire.test.js.map +1 -1
- package/dist/frontend/ts7/semantic-checker.js +209 -68
- package/dist/frontend/ts7/semantic-checker.js.map +1 -1
- package/dist/frontend/ts7/semantic-checker.test.js +29 -14
- package/dist/frontend/ts7/semantic-checker.test.js.map +1 -1
- package/dist/frontend/ts7/semantic-json.js +7 -1
- package/dist/frontend/ts7/semantic-json.js.map +1 -1
- package/dist/frontend/ts7/semantic-json.test.js +17 -3
- package/dist/frontend/ts7/semantic-json.test.js.map +1 -1
- package/dist/frontend/ts7/semantic-model.js +147 -51
- package/dist/frontend/ts7/semantic-model.js.map +1 -1
- package/dist/frontend/ts7/semantic-model.test.js +72 -19
- package/dist/frontend/ts7/semantic-model.test.js.map +1 -1
- package/dist/frontend/ts7/semantic-types.d.ts +2 -2
- package/dist/frontend/ts7/session-cache.js +10 -3
- package/dist/frontend/ts7/session-cache.js.map +1 -1
- package/dist/frontend/ts7/session-cache.test.js +13 -4
- package/dist/frontend/ts7/session-cache.test.js.map +1 -1
- package/dist/frontend/ts7/session-path.js +25 -10
- package/dist/frontend/ts7/session-path.js.map +1 -1
- package/dist/frontend/ts7/session-path.test.js +47 -4
- package/dist/frontend/ts7/session-path.test.js.map +1 -1
- package/dist/frontend/ts7/session-timing.js +22 -3
- package/dist/frontend/ts7/session-timing.js.map +1 -1
- package/dist/frontend/ts7/session-timing.test.js +12 -3
- package/dist/frontend/ts7/session-timing.test.js.map +1 -1
- package/dist/frontend/ts7/session.d.ts +1 -0
- package/dist/frontend/ts7/session.js +109 -35
- package/dist/frontend/ts7/session.js.map +1 -1
- package/dist/frontend/ts7/session.test.js +69 -15
- package/dist/frontend/ts7/session.test.js.map +1 -1
- package/dist/frontend/ts7/source-parser.js +17 -6
- package/dist/frontend/ts7/source-parser.js.map +1 -1
- package/dist/frontend/ts7/source-parser.test.js +32 -9
- package/dist/frontend/ts7/source-parser.test.js.map +1 -1
- package/dist/frontend/tsc-codes.js +56 -11
- package/dist/frontend/tsc-codes.js.map +1 -1
- package/dist/frontend/tsc-codes.test.d.ts +1 -0
- package/dist/frontend/tsc-codes.test.js +16 -0
- package/dist/frontend/tsc-codes.test.js.map +1 -0
- package/dist/frontend/type-mapper.d.ts +3 -0
- package/dist/frontend/type-mapper.js +589 -206
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/frontend/type-mapper.test.js +108 -33
- package/dist/frontend/type-mapper.test.js.map +1 -1
- package/dist/frontend/union-discriminants.js +8 -2
- package/dist/frontend/union-discriminants.js.map +1 -1
- package/dist/frontend/union-discriminants.test.js +77 -20
- package/dist/frontend/union-discriminants.test.js.map +1 -1
- package/dist/frontend/worker-target.d.ts +6 -0
- package/dist/frontend/worker-target.js +108 -0
- package/dist/frontend/worker-target.js.map +1 -0
- package/dist/frontend/worker-target.test.d.ts +1 -0
- package/dist/frontend/worker-target.test.js +71 -0
- package/dist/frontend/worker-target.test.js.map +1 -0
- package/dist/index.d.ts +9 -8
- package/dist/index.js +174 -88
- package/dist/index.js.map +1 -1
- package/dist/ir/analysis.js +95 -38
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/analysis.test.js +91 -15
- package/dist/ir/analysis.test.js.map +1 -1
- package/dist/ir/api.d.ts +4 -0
- package/dist/ir/api.js +6 -0
- package/dist/ir/api.js.map +1 -0
- package/dist/ir/build.d.ts +7 -0
- package/dist/ir/build.js +27 -1
- package/dist/ir/build.js.map +1 -1
- package/dist/ir/builtin-effects.d.ts +9 -0
- package/dist/ir/builtin-effects.js +643 -0
- package/dist/ir/builtin-effects.js.map +1 -0
- package/dist/ir/builtin-signatures.d.ts +15 -0
- package/dist/ir/builtin-signatures.js +3397 -0
- package/dist/ir/builtin-signatures.js.map +1 -0
- package/dist/ir/byte-numbers.js +67 -16
- package/dist/ir/byte-numbers.js.map +1 -1
- package/dist/ir/byte-numbers.test.js +17 -4
- package/dist/ir/byte-numbers.test.js.map +1 -1
- package/dist/ir/constant-tables.js +13 -5
- package/dist/ir/constant-tables.js.map +1 -1
- package/dist/ir/constant-tables.test.js +47 -7
- package/dist/ir/constant-tables.test.js.map +1 -1
- package/dist/ir/control-flow.js +2 -1
- package/dist/ir/control-flow.js.map +1 -1
- package/dist/ir/control-flow.test.js +35 -11
- package/dist/ir/control-flow.test.js.map +1 -1
- package/dist/ir/initializer-bindings.d.ts +9 -0
- package/dist/ir/initializer-bindings.js +115 -0
- package/dist/ir/initializer-bindings.js.map +1 -0
- package/dist/ir/initializer-bindings.test.d.ts +1 -0
- package/dist/ir/initializer-bindings.test.js +118 -0
- package/dist/ir/initializer-bindings.test.js.map +1 -0
- package/dist/ir/integer-loop-facts.d.ts +12 -0
- package/dist/ir/integer-loop-facts.js +137 -0
- package/dist/ir/integer-loop-facts.js.map +1 -0
- package/dist/ir/integer-loops.js +85 -30
- package/dist/ir/integer-loops.js.map +1 -1
- package/dist/ir/integer-loops.test.js +76 -13
- package/dist/ir/integer-loops.test.js.map +1 -1
- package/dist/ir/integer-ranges.js +93 -21
- package/dist/ir/integer-ranges.js.map +1 -1
- package/dist/ir/integer-ranges.test.js +199 -21
- package/dist/ir/integer-ranges.test.js.map +1 -1
- package/dist/ir/intrinsic-signatures.d.ts +20 -0
- package/dist/ir/intrinsic-signatures.js +57 -0
- package/dist/ir/intrinsic-signatures.js.map +1 -0
- package/dist/ir/ir.d.ts +15 -2160
- package/dist/ir/ir.js +160 -1396
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/ir.test.js +98 -77
- package/dist/ir/ir.test.js.map +1 -1
- package/dist/ir/numeric-call-specialization.d.ts +8 -0
- package/dist/ir/numeric-call-specialization.js +198 -0
- package/dist/ir/numeric-call-specialization.js.map +1 -0
- package/dist/ir/runtime-features.d.ts +221 -0
- package/dist/ir/runtime-features.js +552 -0
- package/dist/ir/runtime-features.js.map +1 -0
- package/dist/ir/runtime-features.test.d.ts +1 -0
- package/dist/ir/runtime-features.test.js +98 -0
- package/dist/ir/runtime-features.test.js.map +1 -0
- package/dist/ir/scalar-records.js +130 -40
- package/dist/ir/scalar-records.js.map +1 -1
- package/dist/ir/scalar-records.test.js +146 -27
- package/dist/ir/scalar-records.test.js.map +1 -1
- package/dist/ir/serialize.d.ts +1 -1
- package/dist/ir/serialize.js +7 -5
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/serialize.test.js +44 -9
- package/dist/ir/serialize.test.js.map +1 -1
- package/dist/ir/traverse.js +156 -43
- package/dist/ir/traverse.js.map +1 -1
- package/dist/ir/traverse.test.js +99 -24
- package/dist/ir/traverse.test.js.map +1 -1
- package/dist/ir/validate.d.ts +1 -31
- package/dist/ir/validate.js +869 -1910
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +697 -114
- package/dist/ir/validate.test.js.map +1 -1
- package/dist/library/cache-primitives.js +9 -10
- package/dist/library/cache-primitives.js.map +1 -1
- package/dist/library/cache-reset.test.js +2 -1
- package/dist/library/cache-reset.test.js.map +1 -1
- package/dist/library/compiler-self-identity.js +11 -5
- package/dist/library/compiler-self-identity.js.map +1 -1
- package/dist/library/compiler-self-identity.test.js +10 -3
- package/dist/library/compiler-self-identity.test.js.map +1 -1
- package/dist/library/fence-eval.js +24 -7
- package/dist/library/fence-eval.js.map +1 -1
- package/dist/library/fence-eval.test.js +31 -7
- package/dist/library/fence-eval.test.js.map +1 -1
- package/dist/library/int-infer.js +199 -59
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/int-infer.test.js +137 -61
- package/dist/library/int-infer.test.js.map +1 -1
- package/dist/library/ir-policy.d.ts +27 -0
- package/dist/library/ir-policy.js +234 -0
- package/dist/library/ir-policy.js.map +1 -0
- package/dist/library/library-cache.js +40 -17
- package/dist/library/library-cache.js.map +1 -1
- package/dist/library/library-cache.test.js +29 -18
- package/dist/library/library-cache.test.js.map +1 -1
- package/dist/library/library-profile.js +68 -13
- package/dist/library/library-profile.js.map +1 -1
- package/dist/library/prepare.js +48 -20
- package/dist/library/prepare.js.map +1 -1
- package/dist/library/semantic-source.js +16 -14
- package/dist/library/semantic-source.js.map +1 -1
- package/dist/library/semantic-source.test.js +9 -14
- package/dist/library/semantic-source.test.js.map +1 -1
- package/dist/library/sidecar-validate.js +53 -12
- package/dist/library/sidecar-validate.js.map +1 -1
- package/dist/library/sidecar.js +85 -24
- package/dist/library/sidecar.js.map +1 -1
- package/dist/library/trap-teaching.js +11 -2
- package/dist/library/trap-teaching.js.map +1 -1
- package/dist/native/cache.js +10 -4
- package/dist/native/cache.js.map +1 -1
- package/dist/native/cli.js +34 -9
- package/dist/native/cli.js.map +1 -1
- package/dist/native/compiler.js +191 -54
- package/dist/native/compiler.js.map +1 -1
- package/dist/native/compiler.test.js +134 -33
- package/dist/native/compiler.test.js.map +1 -1
- package/dist/native/comptime.js +23 -7
- package/dist/native/comptime.js.map +1 -1
- package/dist/native/comptime.test.js +26 -6
- package/dist/native/comptime.test.js.map +1 -1
- package/dist/native/executable-cache.js +77 -27
- package/dist/native/executable-cache.js.map +1 -1
- package/dist/native/executable-cache.test.js +41 -6
- package/dist/native/executable-cache.test.js.map +1 -1
- package/dist/native/file-identity.js +9 -2
- package/dist/native/file-identity.js.map +1 -1
- package/dist/native/file-identity.test.js.map +1 -1
- package/dist/native/library.js +50 -12
- package/dist/native/library.js.map +1 -1
- package/dist/native/prepare.js +121 -34
- package/dist/native/prepare.js.map +1 -1
- package/dist/native/prepare.test.js +22 -6
- package/dist/native/prepare.test.js.map +1 -1
- package/dist/native/runtime-typescript.js +24 -11
- package/dist/native/runtime-typescript.js.map +1 -1
- package/dist/native/runtime-typescript.test.js.map +1 -1
- package/dist/native/sanitizer.js +71 -18
- package/dist/native/sanitizer.js.map +1 -1
- package/dist/native/sanitizer.test.js +78 -20
- package/dist/native/sanitizer.test.js.map +1 -1
- package/dist/native/task.js.map +1 -1
- package/dist/native/task.test.js +7 -2
- package/dist/native/task.test.js.map +1 -1
- package/dist/native/toolchain.js +68 -26
- package/dist/native/toolchain.js.map +1 -1
- package/dist/native/toolchain.test.js +59 -20
- package/dist/native/toolchain.test.js.map +1 -1
- package/dist/startup-cache.d.ts +1 -1
- package/dist/startup-cache.js +1 -1
- package/dist/startup-cache.js.map +1 -1
- package/dist/timing.js.map +1 -1
- package/package.json +20 -20
- package/surface-manifest.json +30 -2
- package/dist/frontend/lowering/lower-builtins.d.ts +0 -601
- package/dist/frontend/lowering/lower-builtins.js +0 -8986
- package/dist/frontend/lowering/lower-builtins.js.map +0 -1
- package/dist/frontend/lowering/lower-containers.d.ts +0 -382
- package/dist/frontend/lowering/lower-containers.js +0 -7344
- package/dist/frontend/lowering/lower-containers.js.map +0 -1
|
@@ -1,3973 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export {
|
|
4
|
-
|
|
5
|
-
export {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
import { localizeElfObject, mergeAndLocalizeCoffObjects } from "./object-localize.js";
|
|
18
|
-
import { executableOptimizationLinkerArgs, executableStripLinkerArgs, windowsSubsystemLinkerArgs } from "./targets.js";
|
|
19
|
-
import { createDarwinDebugSymbols, installDarwinDebugSymbols, needsDarwinDebugSymbols, readDarwinDebugSymbols } from "./debug-symbols.js";
|
|
20
|
-
import { createVendorArchives, MBEDTLS_VERSION, QJS_COMMIT, ZLIB_VERSION, } from "./vendor-archives.js";
|
|
21
|
-
import { cacheDigestPath, cacheRootDir, copyValidCachedFile, ensurePrivateCacheRoot, fileDigest, fileExists, installArtifact, privateSiblingPath, protectCachedArtifact, pruneCache, publishCachedFile, validCachedFile, } from "./build-cache.js";
|
|
1
|
+
/** Native compiler facade. Recipe implementation lives with its semantic owner. */
|
|
2
|
+
export { EXECUTABLE_RUNTIME_SOURCES, runtimeSrcDir, runtimeFingerprint, } from "./native/runtime-inputs.js";
|
|
3
|
+
export { executableSectionEliminationFlags, isZigDriver, resolveCc, targetPlatform, cacheTargetIdentity, } from "./native/driver.js";
|
|
4
|
+
export { executableNativeEnvironmentFingerprint, compilerDriverSupportsPersistentCache, ccVersion, } from "./native/tool-identity.js";
|
|
5
|
+
export {} from "./native/contracts.js";
|
|
6
|
+
export { CcCompileError, subprocessFailureDetail } from "./native/process.js";
|
|
7
|
+
export { compileLibArchive } from "./native/library.js";
|
|
8
|
+
export { localizeLibraryObjects } from "./native/object-merge.js";
|
|
9
|
+
export { implicitDependencyProbeIncludes } from "./native/compiler-fingerprint.js";
|
|
10
|
+
export { parseLinkTraceFiles, nativeLinkerDependencyPaths } from "./native/linker-fingerprint.js";
|
|
11
|
+
export { clearCcCaches, stageRuntimeObjects } from "./native/runtime-objects.js";
|
|
12
|
+
export { snapshotNativeArtifactDependencies, nativeArtifactDependenciesStillMatch, } from "./native/artifact-stamps.js";
|
|
13
|
+
export { compileC } from "./native/executable.js";
|
|
14
|
+
export { supportedNativeCacheWarmProfiles, warmNativeCaches, } from "./native/cache-warm.js";
|
|
15
|
+
export { toolchainEnvironmentCachePolicy, toolchainEnvironmentFingerprint, } from "./toolchain-environment.js";
|
|
16
|
+
export { IPHONEOS_MIN_VERSION, ANDROID_MIN_API, isIosTarget, isAndroidTarget, isMobileTarget, mobileLibraryTarget, mobileTargetRefusal, configuredTargetPlatform, } from "./target-platform.js";
|
|
22
17
|
export { buildCacheRoot, prepareBuildCacheRoot, pruneBuildCache, resolveBuildCacheRoot, } from "./build-cache.js";
|
|
23
|
-
|
|
24
|
-
const NATIVE_TOOLCHAIN_IMPLEMENTATION_PATH = fileURLToPath(import.meta.url);
|
|
25
|
-
const NATIVE_RECIPE_IMPLEMENTATION_PATHS = [
|
|
26
|
-
NATIVE_TOOLCHAIN_IMPLEMENTATION_PATH,
|
|
27
|
-
join(dirname(NATIVE_TOOLCHAIN_IMPLEMENTATION_PATH), `build-cache${extname(NATIVE_TOOLCHAIN_IMPLEMENTATION_PATH)}`),
|
|
28
|
-
join(dirname(NATIVE_TOOLCHAIN_IMPLEMENTATION_PATH), `vendor-archives${extname(NATIVE_TOOLCHAIN_IMPLEMENTATION_PATH)}`),
|
|
29
|
-
];
|
|
30
|
-
/** Test lanes run against one immutable checkout and one immutable toolchain
|
|
31
|
-
* for the lifetime of each Vitest worker. Production deliberately rediscovers
|
|
32
|
-
* compiler/linker inputs on every invocation, but doing that thousands of
|
|
33
|
-
* times in the differential corpus costs far more than the compile itself.
|
|
34
|
-
* This test-only opt-in lets those workers reuse metadata probes for their
|
|
35
|
-
* session; the cache-correctness suite removes the flag and exercises the
|
|
36
|
-
* strict production path. */
|
|
37
|
-
function stableTestToolchainSession() {
|
|
38
|
-
return process.env["SCRIPTC_TEST_STABLE_TOOLCHAIN"] === "1";
|
|
39
|
-
}
|
|
40
|
-
function stableTestMemo(cache, key, probe) {
|
|
41
|
-
if (!stableTestToolchainSession())
|
|
42
|
-
return probe();
|
|
43
|
-
const existing = cache.get(key);
|
|
44
|
-
if (existing !== undefined)
|
|
45
|
-
return existing;
|
|
46
|
-
const pending = probe();
|
|
47
|
-
cache.set(key, pending);
|
|
48
|
-
void pending.catch(() => {
|
|
49
|
-
if (cache.get(key) === pending)
|
|
50
|
-
cache.delete(key);
|
|
51
|
-
});
|
|
52
|
-
return pending;
|
|
53
|
-
}
|
|
54
|
-
export const EXECUTABLE_RUNTIME_SOURCES = ["scr_number.c", "scr_bigint.c", "scr_string.c", "scr_grapheme.c", "scr_array.c", "scr_bytes.c", "scr_bytes_io.c", "scr_map.c", "scr_closure.c", "scr_ffi.c", "scr_object.c", "scr_union.c", "scr_exception.c", "scr_error.c", "scr_console.c", "scr_lib.c", "scr_path.c", "scr_url.c", "scr_url_params.c", "scr_json.c", "scr_node_builtin.c", "scr_async.c", "scr_crypto_async.c", "scr_child.c", "scr_cycle.c"];
|
|
55
|
-
/**
|
|
56
|
-
* Per-target section-elimination recipe. This belongs beside the native
|
|
57
|
-
* driver rather than a particular build path: one-shot compilation, cached
|
|
58
|
-
* runtime objects, external object recipes, and runtime packs must all agree
|
|
59
|
-
* on what their final linker is allowed to discard.
|
|
60
|
-
*
|
|
61
|
-
* Executable recipes use both halves. Archive and compile-only recipes never
|
|
62
|
-
* use the link half, but `--lib` uses the compile half so its consumers retain
|
|
63
|
-
* the choice to eliminate unused sections when they perform the final link.
|
|
64
|
-
*/
|
|
65
|
-
export function executableSectionEliminationFlags(platform) {
|
|
66
|
-
switch (platform) {
|
|
67
|
-
case "darwin":
|
|
68
|
-
// ld64's symbol subsections make this sufficient for ordinary C/LLVM
|
|
69
|
-
// objects. Do not attach it only to --dynamic: static programs have the
|
|
70
|
-
// same unreachable runtime sections.
|
|
71
|
-
return { compile: [], link: ["-Wl,-dead_strip"] };
|
|
72
|
-
case "linux":
|
|
73
|
-
return {
|
|
74
|
-
compile: ["-ffunction-sections", "-fdata-sections"],
|
|
75
|
-
link: ["-Wl,--gc-sections"],
|
|
76
|
-
};
|
|
77
|
-
case "win32":
|
|
78
|
-
// clang's MinGW driver forwards this to GNU-flavor ld/lld. A direct
|
|
79
|
-
// local lld-link invocation instead uses /OPT:REF, but scriptc only
|
|
80
|
-
// drives the compiler's GNU-flavor route here.
|
|
81
|
-
return {
|
|
82
|
-
compile: ["-ffunction-sections", "-fdata-sections"],
|
|
83
|
-
link: ["-Wl,--gc-sections"],
|
|
84
|
-
};
|
|
85
|
-
// WASI has a distinct linker/runtime contract. Keep its existing object
|
|
86
|
-
// layout until its linker invocation is validated separately.
|
|
87
|
-
default:
|
|
88
|
-
return { compile: [], link: [] };
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
/** Inputs that can change which native tool/runtime implementation an
|
|
92
|
-
* executable build selects before compileC has a chance to rediscover it.
|
|
93
|
-
* The early whole-program cache keys this exact posture before restoring a
|
|
94
|
-
* final binary; compileC retains its deeper inode/content validation. */
|
|
95
|
-
export async function executableNativeEnvironmentFingerprint(env = process.env) {
|
|
96
|
-
const configuredCompiler = env["SCRIPTC_CC"] ?? "";
|
|
97
|
-
let compilerIdentity;
|
|
98
|
-
try {
|
|
99
|
-
compilerIdentity = await effectiveCompilerEnvironmentIdentity(resolveCc(env), env);
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
// A failed trace cannot safely describe a reusable native posture. Keep
|
|
103
|
-
// the build working, but make this invocation miss every persistent early
|
|
104
|
-
// entry so compileC performs its full discovery and validation.
|
|
105
|
-
compilerIdentity = `<unavailable:${configuredCompiler}:${randomUUID()}>`;
|
|
106
|
-
}
|
|
107
|
-
const hash = createHash("sha256")
|
|
108
|
-
.update("executable-native-environment-v2\0")
|
|
109
|
-
.update(toolchainEnvironmentFingerprint(env)).update("\0")
|
|
110
|
-
// PATH text alone is not a resolution proof, and on Darwin /usr/bin/clang
|
|
111
|
-
// is a stable shim whose selected Xcode compiler can change underneath it.
|
|
112
|
-
// Re-resolve and trace the effective driver on every early lookup.
|
|
113
|
-
.update(compilerIdentity).update("\0");
|
|
114
|
-
for (const name of [
|
|
115
|
-
"PATH",
|
|
116
|
-
"SCRIPTC_FETCH_CURL",
|
|
117
|
-
"SCRIPTC_TEST_RUNTIME_SRC_DIR",
|
|
118
|
-
"SCRIPTC_TEST_VENDOR_CACHE_DIR",
|
|
119
|
-
"SCRIPTC_TEST_TRUST_COMPILER_WRAPPER",
|
|
120
|
-
]) {
|
|
121
|
-
const value = env[name];
|
|
122
|
-
hash.update(name).update(value === undefined ? "\0unset\0" : "\0set\0").update(value ?? "").update("\0");
|
|
123
|
-
}
|
|
124
|
-
return hash.digest("hex");
|
|
125
|
-
}
|
|
126
|
-
/** Structured compiler-driver failure. Most callers still let this surface
|
|
127
|
-
* as an internal build error; the TypeScript compiler pipeline recognizes it
|
|
128
|
-
* when an outbound FFI profile is active and turns user-controlled native
|
|
129
|
-
* link failures into SC5004. */
|
|
130
|
-
export class CcCompileError extends Error {
|
|
131
|
-
driver;
|
|
132
|
-
stderr;
|
|
133
|
-
constructor(driver, stderr, message) {
|
|
134
|
-
super(message);
|
|
135
|
-
this.driver = driver;
|
|
136
|
-
this.stderr = stderr;
|
|
137
|
-
this.name = "CcCompileError";
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
/** Preserve the useful output from a failed compiler/tool invocation. Node's
|
|
141
|
-
* execFile error exposes stderr and stdout independently, but either stream
|
|
142
|
-
* may be present as an empty string. Prefer compiler diagnostics, retain a
|
|
143
|
-
* non-standard stdout diagnostic when that is all the tool emitted, and only
|
|
144
|
-
* then fall back to the process error itself. */
|
|
145
|
-
export function subprocessFailureDetail(err) {
|
|
146
|
-
const failure = err;
|
|
147
|
-
const output = (value) => {
|
|
148
|
-
const text = Buffer.isBuffer(value) ? value.toString("utf8") : value ?? "";
|
|
149
|
-
return text.trim().length > 0 ? text.trimEnd() : "";
|
|
150
|
-
};
|
|
151
|
-
const stderr = output(failure.stderr);
|
|
152
|
-
const stdout = output(failure.stdout);
|
|
153
|
-
if (stderr !== "" && stdout !== "")
|
|
154
|
-
return `${stderr}\n\ncompiler stdout:\n${stdout}`;
|
|
155
|
-
if (stderr !== "")
|
|
156
|
-
return stderr;
|
|
157
|
-
if (stdout !== "")
|
|
158
|
-
return `compiler stdout:\n${stdout}`;
|
|
159
|
-
if (typeof failure.message === "string" && failure.message.trim() !== "") {
|
|
160
|
-
return failure.message;
|
|
161
|
-
}
|
|
162
|
-
return String(err);
|
|
163
|
-
}
|
|
164
|
-
export function runtimeSrcDir() {
|
|
165
|
-
const testRoot = process.env["SCRIPTC_TEST_RUNTIME_SRC_DIR"];
|
|
166
|
-
if (testRoot !== undefined)
|
|
167
|
-
return resolve(testRoot);
|
|
168
|
-
const require = createRequire(import.meta.url);
|
|
169
|
-
return join(dirname(require.resolve("@scriptc/runtime/package.json")), "src");
|
|
170
|
-
}
|
|
171
|
-
/** Whether the selected compiler driver is Zig. This is deliberately based on
|
|
172
|
-
* the executable prefix rather than target presence: targetless Zig is still
|
|
173
|
-
* a Zig build, while target presence is only the platform/target contract. */
|
|
174
|
-
export function isZigDriver(driver) {
|
|
175
|
-
return driver.argv[0] === "zig";
|
|
176
|
-
}
|
|
177
|
-
/* ── mobile targets (library mode) ─────────────────────────────────────────
|
|
178
|
-
* Three mobile triples are admitted, and only for LIBRARY-MODE archive
|
|
179
|
-
* builds — the consuming pattern is an embedding app linking the archive,
|
|
180
|
-
* never a standalone executable (compile()/compileC refuse the executable
|
|
181
|
-
* lane with the pointer to --lib):
|
|
182
|
-
*
|
|
183
|
-
* aarch64-apple-ios device archives; zig `aarch64-ios.15.0`
|
|
184
|
-
* aarch64-apple-ios-simulator simulator archives; zig
|
|
185
|
-
* `aarch64-ios.15.0-simulator`
|
|
186
|
-
* aarch64-linux-android zig `aarch64-linux-android.26`
|
|
187
|
-
*
|
|
188
|
-
* The minimum-version floors are part of the target contract: iOS archives
|
|
189
|
-
* build for iOS 15.0 (IPHONEOS_MIN_VERSION), Android archives for API level
|
|
190
|
-
* 26 (ANDROID_MIN_API) — LC_BUILD_VERSION minos and the bionic stub level
|
|
191
|
-
* both come from the pinned zig spelling above, so an embedder's deployment
|
|
192
|
-
* target at or above the floor links cleanly.
|
|
193
|
-
*
|
|
194
|
-
* Zig bundles no Apple or bionic libc, so both families compile against an
|
|
195
|
-
* explicit sysroot discovered here and spelled into targetArgs (where every
|
|
196
|
-
* cache tier already keys it):
|
|
197
|
-
*
|
|
198
|
-
* iOS darwin hosts only — `xcrun --show-sdk-path` selects the
|
|
199
|
-
* iPhoneOS/iPhoneSimulator SDK; compiles add `-isysroot <sdk>`
|
|
200
|
-
* plus `-isystem <sdk>/usr/include` (zig's driver manages libc
|
|
201
|
-
* header search itself and would otherwise find no headers).
|
|
202
|
-
* Android any host with an NDK — ANDROID_NDK_ROOT/ANDROID_NDK_HOME, or
|
|
203
|
-
* the newest ndk/<version> under ANDROID_HOME/ANDROID_SDK_ROOT
|
|
204
|
-
* or the platform-default SDK location; compiles add the NDK
|
|
205
|
-
* sysroot's generic and per-triple include directories.
|
|
206
|
-
*
|
|
207
|
-
* Library archives never link, so the sysroot's LIBRARIES are the
|
|
208
|
-
* embedder's side of the contract: Xcode links iOS archives against the
|
|
209
|
-
* selected SDK, and Gradle/NDK builds link Android archives against the
|
|
210
|
-
* API-26+ bionic stubs. */
|
|
211
|
-
/** The Apple SDK root for one mobile platform, discovered through xcrun the
|
|
212
|
-
* way Xcode's own build system selects it. Memoized per SDK name and
|
|
213
|
-
* selection environment: production rediscovers per process, and the two
|
|
214
|
-
* selection variables (DEVELOPER_DIR/SDKROOT) are already mutable-input
|
|
215
|
-
* keys that disable persistent caching. */
|
|
216
|
-
const appleSdkMemos = new Map();
|
|
217
|
-
function appleSdkRoot(sdk, env) {
|
|
218
|
-
const key = [sdk, env["PATH"] ?? "", env["DEVELOPER_DIR"] ?? "", env["SDKROOT"] ?? ""].join("\0");
|
|
219
|
-
const memo = appleSdkMemos.get(key);
|
|
220
|
-
if (memo !== undefined)
|
|
221
|
-
return memo;
|
|
222
|
-
const probe = spawnSync("xcrun", ["--sdk", sdk, "--show-sdk-path"], {
|
|
223
|
-
encoding: "utf8",
|
|
224
|
-
env,
|
|
225
|
-
});
|
|
226
|
-
const path = probe.status === 0 ? (probe.stdout ?? "").trim() : "";
|
|
227
|
-
if (path === "" || !existsSync(join(path, "usr", "include"))) {
|
|
228
|
-
throw new Error(`the ${sdk} SDK was not found (xcrun --sdk ${sdk} --show-sdk-path failed) — ` +
|
|
229
|
-
`iOS targets need Xcode with the ${sdk === "iphoneos" ? "iPhoneOS" : "iPhoneSimulator"} SDK installed`);
|
|
230
|
-
}
|
|
231
|
-
appleSdkMemos.set(key, path);
|
|
232
|
-
return path;
|
|
233
|
-
}
|
|
234
|
-
/** Compare dotted-numeric NDK version directory names, newest first. */
|
|
235
|
-
function compareNdkVersionsDesc(a, b) {
|
|
236
|
-
const as = a.split(".").map((s) => Number.parseInt(s, 10));
|
|
237
|
-
const bs = b.split(".").map((s) => Number.parseInt(s, 10));
|
|
238
|
-
for (let i = 0; i < Math.max(as.length, bs.length); i++) {
|
|
239
|
-
const d = (bs[i] ?? 0) - (as[i] ?? 0);
|
|
240
|
-
if (d !== 0 && Number.isFinite(d))
|
|
241
|
-
return d;
|
|
242
|
-
}
|
|
243
|
-
return a < b ? 1 : a > b ? -1 : 0;
|
|
244
|
-
}
|
|
245
|
-
/** The Android NDK sysroot for the selected environment: an explicit
|
|
246
|
-
* ANDROID_NDK_ROOT/ANDROID_NDK_HOME wins; otherwise the newest ndk/<version>
|
|
247
|
-
* under ANDROID_HOME, ANDROID_SDK_ROOT, or the platform-default SDK
|
|
248
|
-
* location. The prebuilt host directory is discovered rather than guessed —
|
|
249
|
-
* the NDK ships exactly one per host OS. Memoized per selection
|
|
250
|
-
* environment. */
|
|
251
|
-
const ndkSysrootMemos = new Map();
|
|
252
|
-
function androidNdkSysroot(env) {
|
|
253
|
-
const key = ["ANDROID_NDK_ROOT", "ANDROID_NDK_HOME", "ANDROID_HOME", "ANDROID_SDK_ROOT"]
|
|
254
|
-
.map((name) => env[name] ?? "")
|
|
255
|
-
.join("\0");
|
|
256
|
-
const memo = ndkSysrootMemos.get(key);
|
|
257
|
-
if (memo !== undefined)
|
|
258
|
-
return memo;
|
|
259
|
-
const ndkRoots = [];
|
|
260
|
-
const explicit = [env["ANDROID_NDK_ROOT"], env["ANDROID_NDK_HOME"]]
|
|
261
|
-
.find((root) => root !== undefined && root !== "");
|
|
262
|
-
if (explicit !== undefined) {
|
|
263
|
-
ndkRoots.push(explicit);
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
const sdkRoots = [
|
|
267
|
-
env["ANDROID_HOME"],
|
|
268
|
-
env["ANDROID_SDK_ROOT"],
|
|
269
|
-
process.platform === "darwin"
|
|
270
|
-
? join(homedir(), "Library", "Android", "sdk")
|
|
271
|
-
: process.platform === "win32"
|
|
272
|
-
? join(env["LOCALAPPDATA"] ?? join(homedir(), "AppData", "Local"), "Android", "Sdk")
|
|
273
|
-
: join(homedir(), "Android", "Sdk"),
|
|
274
|
-
].filter((root) => root !== undefined && root !== "");
|
|
275
|
-
for (const root of sdkRoots) {
|
|
276
|
-
let versions = [];
|
|
277
|
-
try {
|
|
278
|
-
versions = readdirSync(join(root, "ndk")).filter((name) => /^\d/.test(name));
|
|
279
|
-
}
|
|
280
|
-
catch {
|
|
281
|
-
continue;
|
|
282
|
-
}
|
|
283
|
-
versions.sort(compareNdkVersionsDesc);
|
|
284
|
-
ndkRoots.push(...versions.map((version) => join(root, "ndk", version)));
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
for (const ndk of ndkRoots) {
|
|
288
|
-
const prebuilt = join(ndk, "toolchains", "llvm", "prebuilt");
|
|
289
|
-
let hosts = [];
|
|
290
|
-
try {
|
|
291
|
-
hosts = readdirSync(prebuilt).sort();
|
|
292
|
-
}
|
|
293
|
-
catch {
|
|
294
|
-
continue;
|
|
295
|
-
}
|
|
296
|
-
for (const host of hosts) {
|
|
297
|
-
const sysroot = join(prebuilt, host, "sysroot");
|
|
298
|
-
if (existsSync(join(sysroot, "usr", "include", "aarch64-linux-android"))) {
|
|
299
|
-
ndkSysrootMemos.set(key, sysroot);
|
|
300
|
-
return sysroot;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
throw new Error("no Android NDK sysroot was found — install an NDK (sdkmanager 'ndk;<version>') and/or set " +
|
|
305
|
-
"ANDROID_NDK_ROOT to it (ANDROID_HOME with an ndk/ directory also works). " +
|
|
306
|
-
"aarch64-linux-android compiles against the NDK's bionic headers.");
|
|
307
|
-
}
|
|
308
|
-
/** Resolve native platform flags independently of the machine running tests,
|
|
309
|
-
* so the host-Linux contract remains pinned on every development host. */
|
|
310
|
-
function nativePlatformArgs(platform) {
|
|
311
|
-
return platform === "linux"
|
|
312
|
-
? { targetArgs: ["-D_GNU_SOURCE"], linkArgs: ["-lm"] }
|
|
313
|
-
: { targetArgs: [], linkArgs: [] };
|
|
314
|
-
}
|
|
315
|
-
export function resolveCc(env = process.env, hostPlatform = process.platform) {
|
|
316
|
-
const cc = env["SCRIPTC_CC"] ?? "";
|
|
317
|
-
const target = env["SCRIPTC_TARGET"] ?? "";
|
|
318
|
-
const hostArgs = nativePlatformArgs(hostPlatform);
|
|
319
|
-
if (cc === "" || cc === "clang") {
|
|
320
|
-
if (target !== "") {
|
|
321
|
-
throw new Error(`SCRIPTC_TARGET=${target} requires SCRIPTC_CC=zigcc — the default clang path has no cross-target sysroots.`);
|
|
322
|
-
}
|
|
323
|
-
return { argv: ["clang"], target: null, zigTarget: null, ...hostArgs };
|
|
324
|
-
}
|
|
325
|
-
if (cc !== "zigcc") {
|
|
326
|
-
throw new Error(`unknown SCRIPTC_CC '${cc}' (supported: clang, zigcc)`);
|
|
327
|
-
}
|
|
328
|
-
if (target === "")
|
|
329
|
-
return { argv: ["zig", "cc"], target: null, zigTarget: null, ...hostArgs };
|
|
330
|
-
// Validate the target spelling before any SDK/sysroot discovery. Source
|
|
331
|
-
// emission uses the same pure classifier without resolving this driver.
|
|
332
|
-
configuredTargetPlatform(env, hostPlatform);
|
|
333
|
-
const mobileRefusal = mobileTargetRefusal(target, hostPlatform);
|
|
334
|
-
if (mobileRefusal !== null)
|
|
335
|
-
throw new Error(mobileRefusal);
|
|
336
|
-
if (isIosTarget(target)) {
|
|
337
|
-
// Library-mode-only target (compile()/compileC own the executable-lane
|
|
338
|
-
// refusal). Zig bundles no Apple libc: the compile rides the selected
|
|
339
|
-
// SDK sysroot, with the libc header directory spelled explicitly
|
|
340
|
-
// because zig's driver manages libc search itself and consults no
|
|
341
|
-
// -isysroot for it. The zig spelling pins the iOS 15.0 floor into
|
|
342
|
-
// every object's LC_BUILD_VERSION minos.
|
|
343
|
-
const simulator = target === "aarch64-apple-ios-simulator";
|
|
344
|
-
const sdk = appleSdkRoot(simulator ? "iphonesimulator" : "iphoneos", env);
|
|
345
|
-
const zigTarget = `aarch64-ios.${IPHONEOS_MIN_VERSION}${simulator ? "-simulator" : ""}`;
|
|
346
|
-
return {
|
|
347
|
-
argv: ["zig", "cc"],
|
|
348
|
-
target,
|
|
349
|
-
zigTarget,
|
|
350
|
-
targetArgs: ["-target", zigTarget, "-isysroot", sdk, "-isystem", join(sdk, "usr", "include")],
|
|
351
|
-
linkArgs: [],
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
if (isAndroidTarget(target)) {
|
|
355
|
-
// Library-mode-only target. Zig bundles no bionic: compiles ride the
|
|
356
|
-
// NDK sysroot's generic and per-triple include directories. Bionic
|
|
357
|
-
// supports _GNU_SOURCE like glibc (and hides some POSIX declarations
|
|
358
|
-
// without it); the zig spelling pins the API 26 floor, which the NDK's
|
|
359
|
-
// versioned stub libraries enforce at the embedder's link. API 26
|
|
360
|
-
// bionic carries everything the library-lane units call (arc4random_buf
|
|
361
|
-
// included), so no shim TU joins the archive.
|
|
362
|
-
const sysroot = androidNdkSysroot(env);
|
|
363
|
-
const zigTarget = `aarch64-linux-android.${ANDROID_MIN_API}`;
|
|
364
|
-
return {
|
|
365
|
-
argv: ["zig", "cc"],
|
|
366
|
-
target,
|
|
367
|
-
zigTarget,
|
|
368
|
-
targetArgs: [
|
|
369
|
-
"-target",
|
|
370
|
-
zigTarget,
|
|
371
|
-
"-D_GNU_SOURCE",
|
|
372
|
-
"-isystem",
|
|
373
|
-
join(sysroot, "usr", "include"),
|
|
374
|
-
"-isystem",
|
|
375
|
-
join(sysroot, "usr", "include", "aarch64-linux-android"),
|
|
376
|
-
],
|
|
377
|
-
linkArgs: ["-lm"],
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
const linux = target.includes("linux");
|
|
381
|
-
const wasi = target.includes("wasi");
|
|
382
|
-
const musl = target.includes("linux-musl");
|
|
383
|
-
return {
|
|
384
|
-
argv: ["zig", "cc"],
|
|
385
|
-
target,
|
|
386
|
-
zigTarget: target,
|
|
387
|
-
targetArgs: [
|
|
388
|
-
"-target",
|
|
389
|
-
target,
|
|
390
|
-
...(linux || wasi ? ["-D_GNU_SOURCE"] : []),
|
|
391
|
-
...(musl ? ["-DSCR_MUSL"] : []),
|
|
392
|
-
...(wasi ? ["-D_WASI_EMULATED_SIGNAL", "-D_WASI_EMULATED_PROCESS_CLOCKS"] : []),
|
|
393
|
-
],
|
|
394
|
-
linkArgs: linux
|
|
395
|
-
? ["-lm"]
|
|
396
|
-
: wasi
|
|
397
|
-
? ["-lwasi-emulated-signal", "-lwasi-emulated-process-clocks"]
|
|
398
|
-
: [],
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
/** Musl intentionally has no predefined libc macro. The explicit Zig target
|
|
402
|
-
* is therefore the source of truth for selecting its small runtime shim. */
|
|
403
|
-
function isMuslTarget(driver) {
|
|
404
|
-
return driver.target?.includes("linux-musl") ?? false;
|
|
405
|
-
}
|
|
406
|
-
/** The OS the produced binary runs on: the triple's OS under SCRIPTC_TARGET,
|
|
407
|
-
* the host's otherwise — so platform-conditional link flags follow the
|
|
408
|
-
* TARGET, not the machine running the compiler. Exported for compile()/
|
|
409
|
-
* analyze(): the FRONTEND consults it too (path.sep / os.EOL literals and
|
|
410
|
-
* the path-module binding follow the target — a win32 triple compiles
|
|
411
|
-
* Node-on-Windows semantics, path.win32 backing the bare module). */
|
|
412
|
-
export function targetPlatform(driver) {
|
|
413
|
-
if (driver.target === null)
|
|
414
|
-
return process.platform;
|
|
415
|
-
return configuredTargetPlatform({ SCRIPTC_TARGET: driver.target });
|
|
416
|
-
}
|
|
417
|
-
/** Architecture identity for host-native cache entries. Explicit cross targets
|
|
418
|
-
* already name their complete target triple; native builds need the process
|
|
419
|
-
* architecture too because one per-user cache can serve both native and
|
|
420
|
-
* emulated processes (arm64 and Rosetta on macOS, for example). */
|
|
421
|
-
export function cacheTargetIdentity(driver, hostPlatform = process.platform, hostArch = process.arch) {
|
|
422
|
-
return driver.target === null ? `native:${hostPlatform}:${hostArch}` : `cross:${driver.target}`;
|
|
423
|
-
}
|
|
424
|
-
/* ── library mode: the static-archive artifact ────────────────────────────
|
|
425
|
-
* One `scriptc build --lib` invocation produces <name>.lib.a: the program TU
|
|
426
|
-
* object plus exactly the runtime objects the program's IR gates in, every
|
|
427
|
-
* TU compiled with -DSCR_LIB (the per-flavor discipline that keeps library
|
|
428
|
-
* objects apart from executable-lane objects). Persistent builds cache the
|
|
429
|
-
* completed archive by program-TU content and cache runtime objects separately,
|
|
430
|
-
* so an edit recompiles only the changed program object before re-archiving.
|
|
431
|
-
* Large LLVM dev TUs split further into stable symbol-hash shards: compile
|
|
432
|
-
* those in parallel, cache each object independently, then relocatably merge
|
|
433
|
-
* them back into the archive's one canonical program member. A localized edit
|
|
434
|
-
* recompiles only the changed buckets; exact repeats use the merged-object or
|
|
435
|
-
* completed-archive tiers and never repeat the merge.
|
|
436
|
-
* The base set narrows from the executable lane's unconditional sources:
|
|
437
|
-
* scr_async.c (fibers, timers, the loop) and scr_child.c drop — the
|
|
438
|
-
* async_free refusal already guarantees nothing references them — and
|
|
439
|
-
* scr_library.c (sink, arena, reset registry, library funnel) joins. The gated
|
|
440
|
-
* units a library may reach are the pure-data ones (regex + the vendored matcher, assert,
|
|
441
|
-
* inspect, symbol, searchParams, emitter+dyn_handle, zlib); every
|
|
442
|
-
* loop-hooked or ambient unit was refused at SC4005 before emission.
|
|
443
|
-
* External-symbol contract: undefined references only to the target's C/math
|
|
444
|
-
* runtime and system APIs. Windows embedders additionally link advapi32,
|
|
445
|
-
* iphlpapi, and ws2_32; the platform driver supplies its ordinary CRT and
|
|
446
|
-
* kernel imports. Zlib rides the VENDORED per-flavor objects here even on
|
|
447
|
-
* hosts because the executable lane's system `-lz` cannot ride inside an
|
|
448
|
-
* archive. */
|
|
449
|
-
/** The library base: the executable lane's unconditional sources minus the
|
|
450
|
-
* fiber/loop and child-process units, plus the library-mode TU. */
|
|
451
|
-
const LIB_RUNTIME_SOURCES = [
|
|
452
|
-
...EXECUTABLE_RUNTIME_SOURCES.filter((f) => f !== "scr_async.c" && f !== "scr_crypto_async.c" && f !== "scr_child.c" && f !== "scr_ffi.c"),
|
|
453
|
-
"scr_library.c",
|
|
454
|
-
];
|
|
455
|
-
function updateProgramShardCacheIdentity(hash, shards, publicSymbols, mergeIdentity) {
|
|
456
|
-
hash.update("\0program-shards\0");
|
|
457
|
-
if (shards === undefined) {
|
|
458
|
-
hash.update("<none>\0");
|
|
459
|
-
}
|
|
460
|
-
else {
|
|
461
|
-
hash.update("<present>\0");
|
|
462
|
-
for (const shard of shards) {
|
|
463
|
-
hash.update(shard.name).update("\0").update(shard.source).update("\0");
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
hash.update("\0program-public-symbols\0");
|
|
467
|
-
if (publicSymbols === undefined) {
|
|
468
|
-
hash.update("<none>\0");
|
|
469
|
-
}
|
|
470
|
-
else {
|
|
471
|
-
hash.update("<present>\0");
|
|
472
|
-
for (const symbol of publicSymbols)
|
|
473
|
-
hash.update(symbol).update("\0");
|
|
474
|
-
}
|
|
475
|
-
hash.update("\0program-shard-merge\0");
|
|
476
|
-
hash.update(mergeIdentity === undefined ? "<none>\0" : `<present>\0${mergeIdentity}\0`);
|
|
477
|
-
}
|
|
478
|
-
/** Identity of the machinery that turns LLVM shard objects back into the
|
|
479
|
-
* canonical program member, or null when the target cannot do so. Sharding is
|
|
480
|
-
* only a build optimization: a missing host tool or unsupported object class
|
|
481
|
-
* retains the ordinary single-TU compile. The identity joins every cache tier
|
|
482
|
-
* that contains merged bytes; raw shard-object keys deliberately omit it. */
|
|
483
|
-
async function resolveProgramShardMergeIdentity(driver) {
|
|
484
|
-
const platform = targetPlatform(driver);
|
|
485
|
-
// Mach-O merging uses the host's ld64. A Darwin target produced from a
|
|
486
|
-
// non-Darwin host can still compile as one canonical Zig TU, but the host's
|
|
487
|
-
// ELF/COFF linker cannot combine those objects.
|
|
488
|
-
if (platform === "darwin") {
|
|
489
|
-
if (process.platform !== "darwin")
|
|
490
|
-
return null;
|
|
491
|
-
const ld = await resolvedTool("ld");
|
|
492
|
-
return ld === null
|
|
493
|
-
? null
|
|
494
|
-
: rememberFingerprintDependencies(`program-shard-merge-darwin-v1\0${ld.cacheIdentity}`, [ld.canonicalPath]);
|
|
495
|
-
}
|
|
496
|
-
if (platform === "linux") {
|
|
497
|
-
if (driver.target === null) {
|
|
498
|
-
const [ld, objcopy] = await Promise.all([
|
|
499
|
-
resolvedTool("ld"),
|
|
500
|
-
resolvedTool("objcopy"),
|
|
501
|
-
]);
|
|
502
|
-
return ld === null || objcopy === null
|
|
503
|
-
? null
|
|
504
|
-
: rememberFingerprintDependencies(`program-shard-merge-linux-v1\0${ld.cacheIdentity}\0${objcopy.cacheIdentity}`, [ld.canonicalPath, objcopy.canonicalPath]);
|
|
505
|
-
}
|
|
506
|
-
const arch = driver.target.split("-", 1)[0];
|
|
507
|
-
if (arch !== "x86_64" && arch !== "aarch64")
|
|
508
|
-
return null;
|
|
509
|
-
const compiler = await resolvedTool(driver.argv[0] ?? "zig");
|
|
510
|
-
return compiler === null
|
|
511
|
-
? null
|
|
512
|
-
: rememberFingerprintDependencies(`program-shard-merge-cross-elf-v1\0${arch}\0${compiler.cacheIdentity}`, [compiler.canonicalPath]);
|
|
513
|
-
}
|
|
514
|
-
if (platform === "win32") {
|
|
515
|
-
const arch = driver.target?.split("-", 1)[0] ?? process.arch;
|
|
516
|
-
return arch === "x86_64" || arch === "x64"
|
|
517
|
-
? `program-shard-merge-coff-v1\0${arch}`
|
|
518
|
-
: null;
|
|
519
|
-
}
|
|
520
|
-
return null;
|
|
521
|
-
}
|
|
522
|
-
export async function compileLibArchive(opts) {
|
|
523
|
-
clearCcCaches();
|
|
524
|
-
const rtDir = runtimeSrcDir();
|
|
525
|
-
const driver = resolveCc();
|
|
526
|
-
const shardNames = new Set();
|
|
527
|
-
const programShardsValid = opts.programShards?.every((shard) => {
|
|
528
|
-
if (basename(shard.name) !== shard.name || !shard.name.endsWith(".ll") ||
|
|
529
|
-
shardNames.has(shard.name))
|
|
530
|
-
return false;
|
|
531
|
-
shardNames.add(shard.name);
|
|
532
|
-
return true;
|
|
533
|
-
}) === true;
|
|
534
|
-
const programShardsRequested = opts.cPath.endsWith(".ll") &&
|
|
535
|
-
programShardsValid && opts.programShards !== undefined && opts.programShards.length > 1 &&
|
|
536
|
-
opts.programPublicSymbols !== undefined
|
|
537
|
-
? opts.programShards
|
|
538
|
-
: null;
|
|
539
|
-
const programShardMergeIdentity = programShardsRequested === null
|
|
540
|
-
? null
|
|
541
|
-
: await resolveProgramShardMergeIdentity(driver);
|
|
542
|
-
const programShards = programShardMergeIdentity === null ? null : programShardsRequested;
|
|
543
|
-
const programPublicSymbols = programShards === null ? undefined : opts.programPublicSymbols;
|
|
544
|
-
const sanitize = opts.sanitize ?? false;
|
|
545
|
-
const optimization = opts.optimization ?? "release";
|
|
546
|
-
const regex = opts.regex ?? false;
|
|
547
|
-
const sources = [
|
|
548
|
-
...LIB_RUNTIME_SOURCES,
|
|
549
|
-
// win32 targets compile the libc-shim TU into the archive (stpcpy,
|
|
550
|
-
// arc4random_buf — scr_number.c/scr_lib.c/scr_bytes_io.c call them and
|
|
551
|
-
// mingw's CRT has neither), exactly like compileC's unconditional win32
|
|
552
|
-
// arm. The system-DLL imports the shim and scr_lib.c reference
|
|
553
|
-
// (advapi32's CSPRNG/GetUserNameA, iphlpapi's GetAdaptersAddresses,
|
|
554
|
-
// ws2_32's inet_ntop/htonl) stay the EMBEDDER's link line — an archive
|
|
555
|
-
// carries no -l flags. Never present off win32, so host archives
|
|
556
|
-
// cannot change by a byte.
|
|
557
|
-
...(targetPlatform(driver) === "win32" ? ["scr_win.c"] : []),
|
|
558
|
-
// Zig's musl sysroot does not provide arc4random_buf. Keep the fallback
|
|
559
|
-
// inside the archive so library embedders need no extra system library.
|
|
560
|
-
...(isMuslTarget(driver) ? ["scr_musl.c"] : []),
|
|
561
|
-
...(regex ? ["scr_regex.c"] : []),
|
|
562
|
-
...(opts.assert || regex || opts.symbol ? ["scr_assert.c"] : []),
|
|
563
|
-
...(opts.inspect ? ["scr_inspect.c", "scr_console_native.c"] : []),
|
|
564
|
-
...(opts.symbol ? ["scr_symbol.c"] : []),
|
|
565
|
-
...(opts.assert && opts.bigint ? ["scr_bigint_assert.c"] : []),
|
|
566
|
-
...(opts.emitter ? ["scr_events_emitter.c", "scr_dyn_handle.c"] : []),
|
|
567
|
-
...(opts.dynInvoke ? ["scr_dyn_invoke.c"] : []),
|
|
568
|
-
...(opts.zlib ? ["scr_zlib.c"] : []),
|
|
569
|
-
...(opts.copying ? ["scr_copying.c"] : []),
|
|
570
|
-
];
|
|
571
|
-
const cflags = [
|
|
572
|
-
"-std=c11",
|
|
573
|
-
...driver.targetArgs,
|
|
574
|
-
...(sanitize
|
|
575
|
-
? ["-O1", "-fsanitize=address", "-DSCR_RC_AUDIT"]
|
|
576
|
-
: [optimization === "dev" ? "-O0" : "-O2"]),
|
|
577
|
-
...executableSectionEliminationFlags(targetPlatform(driver)).compile,
|
|
578
|
-
"-fno-math-errno",
|
|
579
|
-
"-fno-strict-aliasing", // the emitted object model type-puns — see compileC's buildArgs
|
|
580
|
-
"-Wno-deprecated-declarations",
|
|
581
|
-
"-DSCR_LIB",
|
|
582
|
-
...(opts.threadInstances ? ["-DSCR_THREAD_INSTANCES"] : []),
|
|
583
|
-
...(opts.textDecoderLegacy ? ["-DSCR_TEXT_DECODER_LEGACY"] : []),
|
|
584
|
-
"-I", rtDir,
|
|
585
|
-
...(regex ? ["-I", vendorEngineDir()] : []),
|
|
586
|
-
...(opts.zlib ? ["-I", vendorZlibDir()] : []),
|
|
587
|
-
];
|
|
588
|
-
const programCompilerArgs = opts.cPath.endsWith(".ll")
|
|
589
|
-
? [...cflags, "-Wno-override-module"]
|
|
590
|
-
: cflags;
|
|
591
|
-
const programSourceExtension = opts.cPath.endsWith(".ll") ? ".ll" : ".c";
|
|
592
|
-
const arArgv = isZigDriver(driver) ? [driver.argv[0], "ar"] : ["ar"];
|
|
593
|
-
const cachePolicy = toolchainEnvironmentCachePolicy();
|
|
594
|
-
const configuredCacheRoot = cacheRootDir();
|
|
595
|
-
const toolchainEnv = toolchainEnvironmentFingerprint();
|
|
596
|
-
const persistentDriverCache = cachePolicy.runtimeObjects &&
|
|
597
|
-
configuredCacheRoot !== null &&
|
|
598
|
-
await compilerDriverSupportsPersistentCache(driver, toolchainEnv);
|
|
599
|
-
// A library archive is compile-only from clang's perspective. Link-only
|
|
600
|
-
// search variables cannot affect it, but any mutable compilation input or
|
|
601
|
-
// opaque compiler wrapper makes every persistent tier unsafe to reuse. An
|
|
602
|
-
// opaque archiver narrows only the completed-archive tier below.
|
|
603
|
-
const cacheIdentity = opts.cacheIdentity;
|
|
604
|
-
let persistentCache = cacheIdentity === undefined || configuredCacheRoot === null || !persistentDriverCache
|
|
605
|
-
? null
|
|
606
|
-
: { root: configuredCacheRoot, identity: cacheIdentity };
|
|
607
|
-
if (persistentCache !== null) {
|
|
608
|
-
try {
|
|
609
|
-
await ensurePrivateCacheRoot(persistentCache.root, process.env["SCRIPTC_CACHE_DIR"] === undefined);
|
|
610
|
-
}
|
|
611
|
-
catch {
|
|
612
|
-
persistentCache = null;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
let implicitToolchain = null;
|
|
616
|
-
let implicitCompileToolchain = null;
|
|
617
|
-
let runtimeCompilerInvocation = null;
|
|
618
|
-
let programCompilerInvocation = null;
|
|
619
|
-
if (persistentDriverCache) {
|
|
620
|
-
try {
|
|
621
|
-
const fingerprints = await implicitToolchainFingerprints(driver, toolchainEnv);
|
|
622
|
-
implicitToolchain = fingerprints.complete;
|
|
623
|
-
implicitCompileToolchain = fingerprints.compile;
|
|
624
|
-
}
|
|
625
|
-
catch {
|
|
626
|
-
// An identity probe is cache machinery, never a reason a valid native
|
|
627
|
-
// compile should fail. Disable every persistent tier for this invocation.
|
|
628
|
-
persistentCache = null;
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
if (persistentCache !== null) {
|
|
632
|
-
try {
|
|
633
|
-
runtimeCompilerInvocation = await effectiveCompilerInvocationFingerprint(driver, toolchainEnv, cflags);
|
|
634
|
-
programCompilerInvocation = programSourceExtension === ".ll"
|
|
635
|
-
? await effectiveCompilerInvocationFingerprint(driver, toolchainEnv, programCompilerArgs, programSourceExtension)
|
|
636
|
-
: runtimeCompilerInvocation;
|
|
637
|
-
}
|
|
638
|
-
catch {
|
|
639
|
-
// A wrapper that cannot expose its effective invocation can still build,
|
|
640
|
-
// but its outputs cannot safely participate in a persistent cache.
|
|
641
|
-
persistentCache = null;
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
const vendorBuildIdentity = await currentVendorCacheBuildIdentity(driver, `${toolchainEnv}\0${implicitToolchain ?? "<uncached>"}`);
|
|
645
|
-
// Runtime-localized archives skip the completed-archive tier: their bytes
|
|
646
|
-
// additionally depend on the localization toolchain's identity (host ld/
|
|
647
|
-
// objcopy or the cross driver's lld), which the archive key does not
|
|
648
|
-
// fingerprint. The runtime-object tier still serves them (localization
|
|
649
|
-
// consumes the same per-flavor objects).
|
|
650
|
-
const cacheCompleteArchive = opts.localizeSymbols === undefined &&
|
|
651
|
-
persistentCache !== null && await archiverSupportsPersistentCache(arArgv, driver);
|
|
652
|
-
let cachedArchive = null;
|
|
653
|
-
let compilerVersion = "";
|
|
654
|
-
let archiverVersion = "";
|
|
655
|
-
let runtimeHash = "";
|
|
656
|
-
let programDependencyHash = "";
|
|
657
|
-
let cachedProgramBytes = opts.programSource === undefined
|
|
658
|
-
? null
|
|
659
|
-
: Buffer.from(opts.programSource, "utf8");
|
|
660
|
-
const identityBytes = opts.identityLlvmSource === undefined
|
|
661
|
-
? null
|
|
662
|
-
: Buffer.from(opts.identityLlvmSource, "utf8");
|
|
663
|
-
if (persistentCache !== null) {
|
|
664
|
-
try {
|
|
665
|
-
const [cv, fingerprint, programBytes] = await Promise.all([
|
|
666
|
-
ccVersion(driver.argv, toolchainEnv, true),
|
|
667
|
-
runtimeFingerprint(rtDir),
|
|
668
|
-
cachedProgramBytes === null ? readFile(opts.cPath) : Promise.resolve(cachedProgramBytes),
|
|
669
|
-
]);
|
|
670
|
-
compilerVersion = cv;
|
|
671
|
-
runtimeHash = fingerprint;
|
|
672
|
-
cachedProgramBytes = programBytes;
|
|
673
|
-
programDependencyHash = await translationUnitDependencyFingerprint(driver, cflags, opts.cPath, programBytes, toolchainEnv);
|
|
674
|
-
if (cacheCompleteArchive) {
|
|
675
|
-
const av = await toolVersionOnce(arArgv, toolchainEnv, true);
|
|
676
|
-
archiverVersion = av;
|
|
677
|
-
const key = createHash("sha256")
|
|
678
|
-
// v10 adds the shard-merge implementation/tool identity. The
|
|
679
|
-
// canonical TU still keys source semantics; shard, keep, and merge
|
|
680
|
-
// bytes key the exact merged program object so ABI projections,
|
|
681
|
-
// tool replacements, and single-/multi-TU producers never collide.
|
|
682
|
-
.update("lib-v10\0")
|
|
683
|
-
.update(cacheTargetIdentity(driver)).update("\0")
|
|
684
|
-
.update(toolchainEnv).update("\0")
|
|
685
|
-
.update(implicitToolchain).update("\0")
|
|
686
|
-
.update(runtimeCompilerInvocation).update("\0")
|
|
687
|
-
.update(programCompilerInvocation).update("\0")
|
|
688
|
-
.update(programDependencyHash).update("\0")
|
|
689
|
-
.update(persistentCache.identity).update("\0")
|
|
690
|
-
.update(driver.argv.join("\x1f")).update("\0")
|
|
691
|
-
.update(cv).update("\0")
|
|
692
|
-
.update(fingerprint).update("\0")
|
|
693
|
-
.update(arArgv.join("\x1f")).update("\0")
|
|
694
|
-
.update(av).update("\0")
|
|
695
|
-
.update(cflags.join("\x1f")).update("\0")
|
|
696
|
-
.update(sources.join("\x1f")).update("\0")
|
|
697
|
-
// The compiler-visible spelling and resolved location are both inputs:
|
|
698
|
-
// __FILE__ observes the former, while relative includes follow the
|
|
699
|
-
// latter. Archive members also inherit the TU's basename.
|
|
700
|
-
.update(opts.cPath).update("\0")
|
|
701
|
-
.update(resolve(opts.cPath)).update("\0")
|
|
702
|
-
.update(programBytes);
|
|
703
|
-
updateProgramShardCacheIdentity(key, programShards ?? undefined, programPublicSymbols, programShardMergeIdentity ?? undefined);
|
|
704
|
-
const keyHex = key
|
|
705
|
-
.update("\0identity\0")
|
|
706
|
-
.update(identityBytes === null ? "<none>" : "<generated>").update("\0")
|
|
707
|
-
.update(identityBytes ?? Buffer.alloc(0))
|
|
708
|
-
.digest("hex");
|
|
709
|
-
cachedArchive = join(persistentCache.root, "lib", keyHex);
|
|
710
|
-
const tmpOut = privateSiblingPath(opts.outPath, "lib-hit");
|
|
711
|
-
try {
|
|
712
|
-
await mkdir(dirname(opts.outPath), { recursive: true });
|
|
713
|
-
if (!(await copyValidCachedFile(cachedArchive, tmpOut))) {
|
|
714
|
-
throw new Error("invalid cached library archive");
|
|
715
|
-
}
|
|
716
|
-
// Match a fresh `ar` output under the caller's current umask. Cache
|
|
717
|
-
// entries may have been populated by a less restrictive shell.
|
|
718
|
-
await chmod(tmpOut, 0o666 & ~process.umask());
|
|
719
|
-
await rename(tmpOut, opts.outPath);
|
|
720
|
-
return;
|
|
721
|
-
}
|
|
722
|
-
catch {
|
|
723
|
-
await rm(tmpOut, { force: true }).catch(() => undefined);
|
|
724
|
-
// Miss (or unreadable cache): compile below and publish best-effort.
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
catch {
|
|
729
|
-
// Cache identity trouble is never a build failure. The fresh path below
|
|
730
|
-
// retains the historical compile-everything behavior.
|
|
731
|
-
cachedArchive = null;
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
const transientVendorRoot = persistentCache !== null && implicitToolchain !== null
|
|
735
|
-
? null
|
|
736
|
-
: join(tmpdir(), `scriptc-lib-vendor-${process.pid}-${Math.random().toString(36).slice(2)}`);
|
|
737
|
-
const vendorCacheRoot = transientVendorRoot ?? vendorBuildCacheRoot(persistentCache?.root);
|
|
738
|
-
try {
|
|
739
|
-
const buildDir = await mkdtemp(join(tmpdir(), "scriptc-lib-"));
|
|
740
|
-
try {
|
|
741
|
-
const lreObjects = regex
|
|
742
|
-
? await stageVendorInputs(() => ensureLreObjects(sanitize, driver, vendorBuildIdentity, vendorCacheRoot), join(buildDir, "vendor-lre"))
|
|
743
|
-
: [];
|
|
744
|
-
const zlibObjects = opts.zlib
|
|
745
|
-
? await stageVendorInputs(() => ensureZlibObjects(sanitize, driver, vendorBuildIdentity, vendorCacheRoot), join(buildDir, "vendor-zlib"))
|
|
746
|
-
: [];
|
|
747
|
-
const compileOne = async (src, objName, compilerVisibleSource) => {
|
|
748
|
-
const obj = join(buildDir, objName);
|
|
749
|
-
const args = [
|
|
750
|
-
...driver.argv.slice(1),
|
|
751
|
-
...cflags,
|
|
752
|
-
...(compilerVisibleSource !== undefined
|
|
753
|
-
? [
|
|
754
|
-
`-ffile-prefix-map=${src}=${compilerVisibleSource}`,
|
|
755
|
-
"-iquote",
|
|
756
|
-
dirname(resolve(compilerVisibleSource)),
|
|
757
|
-
]
|
|
758
|
-
: []),
|
|
759
|
-
...(src.endsWith(".ll") ? ["-Wno-override-module"] : []),
|
|
760
|
-
"-c", src,
|
|
761
|
-
"-o", obj,
|
|
762
|
-
];
|
|
763
|
-
try {
|
|
764
|
-
await execFileAsync(driver.argv[0] ?? "clang", args);
|
|
765
|
-
}
|
|
766
|
-
catch (err) {
|
|
767
|
-
const stderr = subprocessFailureDetail(err);
|
|
768
|
-
throw new Error(`${driver.argv.join(" ")} failed compiling ${src} for the library archive.\n` +
|
|
769
|
-
`This is a scriptc bug (generated/runtime C should always compile) unless the compiler itself is missing/broken.\n\n${stderr}`);
|
|
770
|
-
}
|
|
771
|
-
return obj;
|
|
772
|
-
};
|
|
773
|
-
const stem = basename(opts.cPath).replace(/\.(c|ll)$/, "");
|
|
774
|
-
const programSource = cachedProgramBytes === null
|
|
775
|
-
? opts.cPath
|
|
776
|
-
: join(buildDir, `program${opts.cPath.endsWith(".ll") ? ".ll" : ".c"}`);
|
|
777
|
-
if (cachedProgramBytes !== null && programShards === null) {
|
|
778
|
-
await writeFile(programSource, cachedProgramBytes);
|
|
779
|
-
}
|
|
780
|
-
let cachedProgramObject = null;
|
|
781
|
-
if (persistentCache !== null && cachedProgramBytes !== null && compilerVersion !== "" &&
|
|
782
|
-
implicitToolchain !== null && programCompilerInvocation !== null) {
|
|
783
|
-
const programKey = createHash("sha256")
|
|
784
|
-
.update("lib-program-obj-v3\0")
|
|
785
|
-
.update(cacheTargetIdentity(driver)).update("\0")
|
|
786
|
-
.update(toolchainEnv).update("\0")
|
|
787
|
-
.update(implicitToolchain).update("\0")
|
|
788
|
-
.update(programCompilerInvocation).update("\0")
|
|
789
|
-
.update(persistentCache.identity).update("\0")
|
|
790
|
-
.update(driver.argv.join("\x1f")).update("\0")
|
|
791
|
-
.update(compilerVersion).update("\0")
|
|
792
|
-
.update(runtimeHash).update("\0")
|
|
793
|
-
.update(programDependencyHash).update("\0")
|
|
794
|
-
.update(programCompilerArgs.join("\x1f")).update("\0")
|
|
795
|
-
.update(opts.cPath).update("\0")
|
|
796
|
-
.update(resolve(opts.cPath)).update("\0")
|
|
797
|
-
.update(cachedProgramBytes);
|
|
798
|
-
updateProgramShardCacheIdentity(programKey, programShards ?? undefined, programPublicSymbols, programShardMergeIdentity ?? undefined);
|
|
799
|
-
const programKeyHex = programKey.digest("hex");
|
|
800
|
-
cachedProgramObject = join(persistentCache.root, "program-obj", programKeyHex);
|
|
801
|
-
}
|
|
802
|
-
const stagedProgramObject = join(buildDir, `${stem}.program.o`);
|
|
803
|
-
let programObject;
|
|
804
|
-
let programShardFallback = false;
|
|
805
|
-
if (cachedProgramObject !== null &&
|
|
806
|
-
await copyValidCachedFile(cachedProgramObject, stagedProgramObject)) {
|
|
807
|
-
programObject = stagedProgramObject;
|
|
808
|
-
}
|
|
809
|
-
else if (programShards !== null) {
|
|
810
|
-
try {
|
|
811
|
-
const shardEntries = programShards.map((shard, index) => {
|
|
812
|
-
const sourcePath = join(buildDir, shard.name);
|
|
813
|
-
const staged = join(buildDir, `${stem}.program-${index.toString().padStart(3, "0")}.o`);
|
|
814
|
-
let cachePath = null;
|
|
815
|
-
if (persistentCache !== null && compilerVersion !== "" &&
|
|
816
|
-
implicitCompileToolchain !== null && programCompilerInvocation !== null) {
|
|
817
|
-
const key = createHash("sha256")
|
|
818
|
-
// v2 removes the broad implicit-toolchain fingerprint: it
|
|
819
|
-
// includes the linker selected by the driver, but raw shard
|
|
820
|
-
// objects are compile-only outputs. The compile-only toolchain
|
|
821
|
-
// identity retains compiler/config/header/assembler inputs;
|
|
822
|
-
// the effective program invocation pins this exact flag lane.
|
|
823
|
-
// Merge-tool identity belongs only to the merged-object and
|
|
824
|
-
// completed-archive tiers.
|
|
825
|
-
.update("lib-program-shard-v2\0")
|
|
826
|
-
.update(cacheTargetIdentity(driver)).update("\0")
|
|
827
|
-
.update(toolchainEnv).update("\0")
|
|
828
|
-
.update(implicitCompileToolchain).update("\0")
|
|
829
|
-
.update(programCompilerInvocation).update("\0")
|
|
830
|
-
.update(persistentCache.identity).update("\0")
|
|
831
|
-
.update(driver.argv.join("\x1f")).update("\0")
|
|
832
|
-
.update(compilerVersion).update("\0")
|
|
833
|
-
.update(runtimeHash).update("\0")
|
|
834
|
-
.update(programDependencyHash).update("\0")
|
|
835
|
-
.update(programCompilerArgs.join("\x1f")).update("\0")
|
|
836
|
-
.update(opts.cPath).update("\0")
|
|
837
|
-
.update(resolve(opts.cPath)).update("\0")
|
|
838
|
-
.update(shard.name).update("\0")
|
|
839
|
-
.update(shard.source)
|
|
840
|
-
.digest("hex");
|
|
841
|
-
cachePath = join(persistentCache.root, "program-shard", key);
|
|
842
|
-
}
|
|
843
|
-
return { ...shard, sourcePath, staged, cachePath, missed: false };
|
|
844
|
-
});
|
|
845
|
-
const shardWidth = Math.min(8, availableParallelism());
|
|
846
|
-
for (let i = 0; i < shardEntries.length; i += shardWidth) {
|
|
847
|
-
await Promise.all(shardEntries.slice(i, i + shardWidth).map(async (entry) => {
|
|
848
|
-
await writeFile(entry.sourcePath, entry.source);
|
|
849
|
-
if (entry.cachePath !== null &&
|
|
850
|
-
await copyValidCachedFile(entry.cachePath, entry.staged))
|
|
851
|
-
return;
|
|
852
|
-
entry.missed = true;
|
|
853
|
-
await compileOne(entry.sourcePath, basename(entry.staged), opts.cPath);
|
|
854
|
-
}));
|
|
855
|
-
}
|
|
856
|
-
const publishable = shardEntries.filter((entry) => entry.missed && entry.cachePath !== null);
|
|
857
|
-
const mergedProgramObject = await localizeLibraryObjects(driver, arArgv, buildDir, shardEntries.map((entry) => entry.staged), [], programPublicSymbols, `${stem}.program`);
|
|
858
|
-
// Keep the canonical archive member spelling. The fact that native
|
|
859
|
-
// compilation used shards is an implementation detail; consumers and
|
|
860
|
-
// deterministic cache tests continue to see `<stem>.program.o`.
|
|
861
|
-
await rename(mergedProgramObject, stagedProgramObject);
|
|
862
|
-
programObject = stagedProgramObject;
|
|
863
|
-
if (cachedProgramObject !== null || publishable.length > 0) {
|
|
864
|
-
try {
|
|
865
|
-
const [currentRuntime, currentInvocation, currentDependencies, currentCompiler] = await Promise.all([
|
|
866
|
-
runtimeFingerprint(rtDir),
|
|
867
|
-
effectiveCompilerInvocationFingerprint(driver, toolchainEnv, programCompilerArgs, programSourceExtension),
|
|
868
|
-
translationUnitDependencyFingerprint(driver, cflags, opts.cPath, cachedProgramBytes, toolchainEnv),
|
|
869
|
-
ccVersion(driver.argv, toolchainEnv, true),
|
|
870
|
-
]);
|
|
871
|
-
const shardInputsStillMatch = currentRuntime === runtimeHash &&
|
|
872
|
-
currentInvocation === programCompilerInvocation &&
|
|
873
|
-
currentDependencies === programDependencyHash && currentCompiler === compilerVersion;
|
|
874
|
-
const currentFingerprints = shardInputsStillMatch
|
|
875
|
-
? await implicitToolchainFingerprints(driver, toolchainEnv)
|
|
876
|
-
: null;
|
|
877
|
-
const compileInputsStillMatch = shardInputsStillMatch &&
|
|
878
|
-
currentFingerprints?.compile === implicitCompileToolchain;
|
|
879
|
-
let mergedInputsStillMatch = false;
|
|
880
|
-
if (compileInputsStillMatch && cachedProgramObject !== null) {
|
|
881
|
-
const currentMerge = await resolveProgramShardMergeIdentity(driver).catch(() => null);
|
|
882
|
-
mergedInputsStillMatch =
|
|
883
|
-
currentFingerprints?.complete === implicitToolchain &&
|
|
884
|
-
currentMerge === programShardMergeIdentity;
|
|
885
|
-
}
|
|
886
|
-
if (compileInputsStillMatch) {
|
|
887
|
-
await Promise.all([
|
|
888
|
-
...(mergedInputsStillMatch
|
|
889
|
-
? [publishCachedFile(programObject, cachedProgramObject)]
|
|
890
|
-
: []),
|
|
891
|
-
...publishable.map((entry) => publishCachedFile(entry.staged, entry.cachePath)),
|
|
892
|
-
]);
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
catch {
|
|
896
|
-
// Best-effort: the merged program object is already valid.
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
catch {
|
|
901
|
-
// Sharding is only an optimization. A present but incompatible or
|
|
902
|
-
// failing merge tool (or a shard-only compiler failure) must not
|
|
903
|
-
// turn a valid canonical LLVM TU into a failed library build. Do not
|
|
904
|
-
// publish this retry under shard-derived object/archive cache keys.
|
|
905
|
-
programShardFallback = true;
|
|
906
|
-
if (cachedProgramBytes !== null)
|
|
907
|
-
await writeFile(programSource, cachedProgramBytes);
|
|
908
|
-
programObject = await compileOne(programSource, `${stem}.program.o`, cachedProgramBytes === null ? undefined : opts.cPath);
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
else {
|
|
912
|
-
programObject = await compileOne(programSource, `${stem}.program.o`, cachedProgramBytes === null ? undefined : opts.cPath);
|
|
913
|
-
if (cachedProgramObject !== null) {
|
|
914
|
-
try {
|
|
915
|
-
const [currentRuntime, currentImplicit, currentInvocation, currentDependencies, currentCompiler] = await Promise.all([
|
|
916
|
-
runtimeFingerprint(rtDir),
|
|
917
|
-
implicitToolchainFingerprint(driver, toolchainEnv),
|
|
918
|
-
effectiveCompilerInvocationFingerprint(driver, toolchainEnv, programCompilerArgs, programSourceExtension),
|
|
919
|
-
translationUnitDependencyFingerprint(driver, cflags, opts.cPath, cachedProgramBytes, toolchainEnv),
|
|
920
|
-
ccVersion(driver.argv, toolchainEnv, true),
|
|
921
|
-
]);
|
|
922
|
-
if (currentRuntime === runtimeHash &&
|
|
923
|
-
currentImplicit === implicitToolchain &&
|
|
924
|
-
currentInvocation === programCompilerInvocation &&
|
|
925
|
-
currentDependencies === programDependencyHash &&
|
|
926
|
-
currentCompiler === compilerVersion) {
|
|
927
|
-
await publishCachedFile(programObject, cachedProgramObject);
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
catch {
|
|
931
|
-
// Best-effort: the archive build already owns a valid object.
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
const identityObject = identityBytes === null
|
|
936
|
-
? null
|
|
937
|
-
: await (async () => {
|
|
938
|
-
const source = join(buildDir, "identity.ll");
|
|
939
|
-
await writeFile(source, identityBytes);
|
|
940
|
-
return compileOne(source, `${stem}.identity.o`);
|
|
941
|
-
})();
|
|
942
|
-
let runtimeObjects = null;
|
|
943
|
-
let cacheInputsStable = true;
|
|
944
|
-
let objectImplicitVerification = null;
|
|
945
|
-
const objectImplicitToolchainStillMatches = () => {
|
|
946
|
-
objectImplicitVerification ??= Promise.all([
|
|
947
|
-
implicitToolchainFingerprint(driver, toolchainEnv),
|
|
948
|
-
effectiveCompilerInvocationFingerprint(driver, toolchainEnv, cflags),
|
|
949
|
-
]).then(([currentImplicit, currentInvocation]) => currentImplicit === implicitToolchain &&
|
|
950
|
-
currentInvocation === runtimeCompilerInvocation, () => false);
|
|
951
|
-
return objectImplicitVerification;
|
|
952
|
-
};
|
|
953
|
-
if (persistentCache !== null && compilerVersion !== "" && runtimeHash !== "") {
|
|
954
|
-
try {
|
|
955
|
-
const sourcePaths = sources.map((f) => join(rtDir, f));
|
|
956
|
-
const cached = await ensureRuntimeObjects(persistentCache.root, driver.argv, cflags, sourcePaths, `lib-obj-v5\0${cacheTargetIdentity(driver)}\0${toolchainEnv}\0${implicitToolchain}\0${runtimeCompilerInvocation}\0${driver.argv.join(" ")}\0${compilerVersion}\0${runtimeHash}\0`, async () => (await runtimeFingerprint(rtDir)) === runtimeHash &&
|
|
957
|
-
(await objectImplicitToolchainStillMatches()));
|
|
958
|
-
const staged = await stageRuntimeObjects(cached, join(buildDir, "cached-runtime"));
|
|
959
|
-
runtimeObjects = sourcePaths.map((path) => staged.get(path));
|
|
960
|
-
}
|
|
961
|
-
catch (err) {
|
|
962
|
-
if (err instanceof CacheInputsChangedError)
|
|
963
|
-
cacheInputsStable = false;
|
|
964
|
-
runtimeObjects = null;
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
if (runtimeObjects === null) {
|
|
968
|
-
runtimeObjects = [];
|
|
969
|
-
const width = Math.min(4, availableParallelism());
|
|
970
|
-
for (let i = 0; i < sources.length; i += width) {
|
|
971
|
-
runtimeObjects.push(...(await Promise.all(sources.slice(i, i + width).map((f) => compileOne(join(rtDir, f), f.replace(/\.c$/, ".o"))))));
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
const objects = [programObject, ...(identityObject === null ? [] : [identityObject]), ...runtimeObjects, ...lreObjects, ...zlibObjects];
|
|
975
|
-
// Multi-instance library mode: the archive's one member becomes the
|
|
976
|
-
// combined, symbol-localized object (cached vendor/runtime objects
|
|
977
|
-
// are read-only inputs here — the combine step never mutates them).
|
|
978
|
-
const archiveMembers = opts.localizeSymbols === undefined
|
|
979
|
-
? objects
|
|
980
|
-
: [
|
|
981
|
-
await localizeLibraryObjects(driver, arArgv, buildDir, [programObject, ...(identityObject === null ? [] : [identityObject])], [...runtimeObjects, ...lreObjects, ...zlibObjects], opts.localizeSymbols, stem),
|
|
982
|
-
];
|
|
983
|
-
// A cacheable or runtime-localized build owns a private archive from
|
|
984
|
-
// `ar` through publication. Localized archives deliberately bypass the
|
|
985
|
-
// completed-artifact cache, but still need atomic installation so two
|
|
986
|
-
// invocations sharing a caller-visible output cannot race `rm`/`ar` on
|
|
987
|
-
// that path.
|
|
988
|
-
const archiveOutput = cachedArchive === null && opts.localizeSymbols === undefined
|
|
989
|
-
? opts.outPath
|
|
990
|
-
: join(buildDir, "artifact.lib.a");
|
|
991
|
-
await rm(archiveOutput, { force: true }); // `ar r` would append into a stale archive
|
|
992
|
-
await mkdir(dirname(archiveOutput), { recursive: true });
|
|
993
|
-
await execFileAsync(arArgv[0] ?? "ar", [
|
|
994
|
-
...arArgv.slice(1),
|
|
995
|
-
"rcs",
|
|
996
|
-
archiveOutput,
|
|
997
|
-
...archiveMembers,
|
|
998
|
-
]);
|
|
999
|
-
if (archiveOutput !== opts.outPath)
|
|
1000
|
-
await installArtifact(archiveOutput, opts.outPath);
|
|
1001
|
-
let runtimeStillMatchesKey = false;
|
|
1002
|
-
if (cachedArchive !== null &&
|
|
1003
|
-
persistentCache !== null &&
|
|
1004
|
-
runtimeHash !== "" &&
|
|
1005
|
-
programDependencyHash !== "" &&
|
|
1006
|
-
compilerVersion !== "" &&
|
|
1007
|
-
archiverVersion !== "") {
|
|
1008
|
-
const [currentRuntime, currentImplicit, currentRuntimeInvocation, currentProgramInvocation, currentProgramDependencies, currentCompiler, currentArchiver, currentProgramShardMerge] = await Promise.all([
|
|
1009
|
-
runtimeFingerprint(rtDir).catch(() => null),
|
|
1010
|
-
implicitToolchainFingerprint(driver, toolchainEnv).catch(() => null),
|
|
1011
|
-
effectiveCompilerInvocationFingerprint(driver, toolchainEnv, cflags).catch(() => null),
|
|
1012
|
-
effectiveCompilerInvocationFingerprint(driver, toolchainEnv, programCompilerArgs, programSourceExtension).catch(() => null),
|
|
1013
|
-
translationUnitDependencyFingerprint(driver, cflags, opts.cPath, cachedProgramBytes, toolchainEnv).catch(() => null),
|
|
1014
|
-
ccVersion(driver.argv, toolchainEnv, true).catch(() => null),
|
|
1015
|
-
toolVersionOnce(arArgv, toolchainEnv, true).catch(() => null),
|
|
1016
|
-
programShards === null
|
|
1017
|
-
? Promise.resolve(null)
|
|
1018
|
-
: resolveProgramShardMergeIdentity(driver).catch(() => null),
|
|
1019
|
-
]);
|
|
1020
|
-
runtimeStillMatchesKey =
|
|
1021
|
-
cacheInputsStable &&
|
|
1022
|
-
!programShardFallback &&
|
|
1023
|
-
currentRuntime === runtimeHash &&
|
|
1024
|
-
currentImplicit === implicitToolchain &&
|
|
1025
|
-
currentRuntimeInvocation === runtimeCompilerInvocation &&
|
|
1026
|
-
currentProgramInvocation === programCompilerInvocation &&
|
|
1027
|
-
currentProgramDependencies === programDependencyHash &&
|
|
1028
|
-
currentCompiler === compilerVersion &&
|
|
1029
|
-
currentArchiver === archiverVersion &&
|
|
1030
|
-
currentProgramShardMerge === programShardMergeIdentity;
|
|
1031
|
-
}
|
|
1032
|
-
if (cachedArchive !== null && persistentCache !== null && runtimeStillMatchesKey) {
|
|
1033
|
-
try {
|
|
1034
|
-
await publishCachedFile(archiveOutput, cachedArchive);
|
|
1035
|
-
}
|
|
1036
|
-
catch {
|
|
1037
|
-
// Publishing is best-effort; the requested archive is already valid.
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
finally {
|
|
1042
|
-
await rm(buildDir, { recursive: true, force: true });
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
finally {
|
|
1046
|
-
if (transientVendorRoot !== null) {
|
|
1047
|
-
await rm(transientVendorRoot, { recursive: true, force: true }).catch(() => undefined);
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
if (persistentCache !== null) {
|
|
1051
|
-
await pruneCache(persistentCache.root).catch(() => undefined);
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
/* Multi-instance library mode's localization step: combine the program
|
|
1055
|
-
* object with exactly the runtime/vendor members it reaches into ONE
|
|
1056
|
-
* relocatable object, then demote every scriptc external definition except
|
|
1057
|
-
* the profile-declared symbols to a local symbol. The internals are not
|
|
1058
|
-
* renamed apart — they stop being visible to the embedder's linker at all,
|
|
1059
|
-
* so a second archive built under a different prefix brings its own private
|
|
1060
|
-
* copy of the whole runtime (allocator, collector, arena, panic sink) into
|
|
1061
|
-
* the same process. Undefined references (the target C/math runtime and
|
|
1062
|
-
* system APIs, plus sanitizer ABI in instrumented builds) keep their global
|
|
1063
|
-
* binding: those platform services and the sanitizer are the embedder's,
|
|
1064
|
-
* shared by design. Windows embedders additionally link advapi32, iphlpapi,
|
|
1065
|
-
* and ws2_32.
|
|
1066
|
-
*
|
|
1067
|
-
* The generated program and optional identity objects are mandatory roots;
|
|
1068
|
-
* support-member selection still matters. A classic archive's unused members (and their
|
|
1069
|
-
* undefined references to units library mode excludes, like the
|
|
1070
|
-
* fs-promises unit's fiber symbols) never reach an embedder's link. A
|
|
1071
|
-
* blind merge of every object would carry those references into the one
|
|
1072
|
-
* combined member. Staging the support objects into an intermediate
|
|
1073
|
-
* archive keeps the linker's own member semantics: `ld -r` pulls only the
|
|
1074
|
-
* members the program object transitively needs (the COFF arm implements
|
|
1075
|
-
* the same member semantics in process).
|
|
1076
|
-
*
|
|
1077
|
-
* Mach-O — one host-ld64 invocation: -r merges roots + needed members,
|
|
1078
|
-
* -exported_symbols_list demotes every unlisted global to
|
|
1079
|
-
* private extern, and -r without -keep_private_externs writes
|
|
1080
|
-
* private externs out as non-external symbols. Apple ASan's
|
|
1081
|
-
* image-registration COMMON remains shared so the final Mach-O
|
|
1082
|
-
* image registers its globals once. ld64 reads every Apple
|
|
1083
|
-
* platform's objects — macOS architectures and the iOS/
|
|
1084
|
-
* iOS-simulator triples alike (LC_BUILD_VERSION platform and
|
|
1085
|
-
* minos survive the merge) — so darwin-host macos AND ios
|
|
1086
|
-
* targets ride the same arm; compileLibrary refuses those
|
|
1087
|
-
* targets elsewhere.
|
|
1088
|
-
* ELF, native linux host — ld -r merges with --force-group-allocation
|
|
1089
|
-
* (ASan's instrumented globals ride ELF section groups whose
|
|
1090
|
-
* signatures repeat across archives sharing runtime objects;
|
|
1091
|
-
* resolving the groups into the combined member keeps a later
|
|
1092
|
-
* multi-archive link from discarding one archive's copies),
|
|
1093
|
-
* then binutils objcopy --keep-global-symbols localizes every
|
|
1094
|
-
* other DEFINED global (objcopy leaves undefined symbols
|
|
1095
|
-
* global by its own rule). The exact historical recipe.
|
|
1096
|
-
* ELF, cross triples from any host — `zig cc -target <triple> -r`
|
|
1097
|
-
* merges (zig is already the cross driver's hard requirement),
|
|
1098
|
-
* then localizeElfObject demotes in process and resolves
|
|
1099
|
-
* section groups the way --force-group-allocation does, with
|
|
1100
|
-
* no host binutils/llvm-objcopy dependency.
|
|
1101
|
-
* COFF, native win32 hosts and windows cross triples from any host —
|
|
1102
|
-
* mergeAndLocalizeCoffObjects performs member selection, the
|
|
1103
|
-
* merge, cross-object symbol resolution, and demotion in
|
|
1104
|
-
* process: no linker offers a COFF relocatable mode (lld-link
|
|
1105
|
-
* mirrors MSVC link.exe; zig's COFF driver refuses multi-object
|
|
1106
|
-
* merges) and llvm-objcopy rejects symbol-scope flags for
|
|
1107
|
-
* COFF, so no tool pairing exists to shell out to. */
|
|
1108
|
-
export async function localizeLibraryObjects(driver, arArgv, buildDir, rootObjects, supportObjects, keepSymbols, stem, platform = targetPlatform(driver)) {
|
|
1109
|
-
const combined = join(buildDir, `${stem}.localized.o`);
|
|
1110
|
-
const staging = join(buildDir, `${stem}.localize-staging.a`);
|
|
1111
|
-
const keepFile = join(buildDir, "localize-keep.syms");
|
|
1112
|
-
const run = async (argv) => {
|
|
1113
|
-
try {
|
|
1114
|
-
await execFileAsync(argv[0], [...argv.slice(1)]);
|
|
1115
|
-
}
|
|
1116
|
-
catch (err) {
|
|
1117
|
-
const stderr = subprocessFailureDetail(err);
|
|
1118
|
-
throw new Error(`${argv[0]} failed while localizing the library archive's runtime symbols (abi.localize_runtime).\n` +
|
|
1119
|
-
`Runtime symbol localization needs the ${platform === "darwin" ? "host toolchain's ld" : driver.target === null ? "host toolchain's ld and objcopy" : "cross driver's relocatable link"} beside the C compiler.\n\n${stderr}`);
|
|
1120
|
-
}
|
|
1121
|
-
};
|
|
1122
|
-
if (platform === "win32") {
|
|
1123
|
-
// COFF has no relocatable-link tool to stage through; the member
|
|
1124
|
-
// selection and combine+demote happen in process over the object bytes.
|
|
1125
|
-
const [roots, support] = await Promise.all([
|
|
1126
|
-
Promise.all(rootObjects.map((path) => readFile(path))),
|
|
1127
|
-
Promise.all(supportObjects.map((path) => readFile(path))),
|
|
1128
|
-
]);
|
|
1129
|
-
try {
|
|
1130
|
-
await writeFile(combined, mergeAndLocalizeCoffObjects(roots, support, new Set(keepSymbols), {
|
|
1131
|
-
roots: rootObjects.map((path) => basename(path)),
|
|
1132
|
-
support: supportObjects.map((path) => basename(path)),
|
|
1133
|
-
}));
|
|
1134
|
-
}
|
|
1135
|
-
catch (err) {
|
|
1136
|
-
throw new Error(`COFF symbol localization failed while localizing the library archive's runtime symbols (abi.localize_runtime).\n\n${err.message}`);
|
|
1137
|
-
}
|
|
1138
|
-
return combined;
|
|
1139
|
-
}
|
|
1140
|
-
const supportArgs = supportObjects.length === 0 ? [] : [staging];
|
|
1141
|
-
if (supportObjects.length > 0) {
|
|
1142
|
-
await run([arArgv[0] ?? "ar", ...arArgv.slice(1), "rcs", staging, ...supportObjects]);
|
|
1143
|
-
}
|
|
1144
|
-
if (platform === "darwin") {
|
|
1145
|
-
await writeFile(keepFile, keepSymbols.map((s) => `_${s}\n`).join(""));
|
|
1146
|
-
await run(["ld", "-r", ...rootObjects, ...supportArgs, "-o", combined, "-exported_symbols_list", keepFile]);
|
|
1147
|
-
}
|
|
1148
|
-
else if (platform === "linux" && driver.target === null) {
|
|
1149
|
-
await writeFile(keepFile, keepSymbols.map((s) => `${s}\n`).join(""));
|
|
1150
|
-
await run(["ld", "-r", "--force-group-allocation", ...rootObjects, ...supportArgs, "-o", combined]);
|
|
1151
|
-
await run(["objcopy", `--keep-global-symbols=${keepFile}`, combined]);
|
|
1152
|
-
}
|
|
1153
|
-
else if (platform === "linux") {
|
|
1154
|
-
// Cross ELF: the cross driver's own lld performs the relocatable merge
|
|
1155
|
-
// (with the staging archive's member semantics); demotion and section-
|
|
1156
|
-
// group resolution happen in process. -nostdlib keeps zig from feeding
|
|
1157
|
-
// libc/compiler-rt inputs into the merge. Android rides this arm
|
|
1158
|
-
// unchanged: bionic archives are ordinary aarch64 ELF64, and the merge
|
|
1159
|
-
// uses the driver's zig spelling (which pins the API floor).
|
|
1160
|
-
await run([
|
|
1161
|
-
driver.argv[0] ?? "zig",
|
|
1162
|
-
...driver.argv.slice(1),
|
|
1163
|
-
"-target", driver.zigTarget ?? driver.target,
|
|
1164
|
-
"-nostdlib",
|
|
1165
|
-
"-r", ...rootObjects, ...supportArgs,
|
|
1166
|
-
"-o", combined,
|
|
1167
|
-
]);
|
|
1168
|
-
try {
|
|
1169
|
-
await writeFile(combined, localizeElfObject(await readFile(combined), new Set(keepSymbols)));
|
|
1170
|
-
}
|
|
1171
|
-
catch (err) {
|
|
1172
|
-
throw new Error(`ELF symbol localization failed while localizing the library archive's runtime symbols (abi.localize_runtime).\n\n${err.message}`);
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
else {
|
|
1176
|
-
throw new InternalCompilerError(`runtime symbol localization (abi.localize_runtime) has no ${platform} arm; compileLibrary admits darwin, linux, and win32 builds only`);
|
|
1177
|
-
}
|
|
1178
|
-
return combined;
|
|
1179
|
-
}
|
|
1180
|
-
/* -------------------------- persistent build cache ---------------------------
|
|
1181
|
-
* Content-addressed caches that let repeat builds of unchanged programs skip
|
|
1182
|
-
* payload code generation/linking — the test lanes' dominant cost. Executable
|
|
1183
|
-
* lookups still run lightweight dependency and link-input metadata probes.
|
|
1184
|
-
* Principal keyspaces under the cache root:
|
|
1185
|
-
*
|
|
1186
|
-
* bin/<key> — whole program binaries. key = sha256(resolved clang
|
|
1187
|
-
* identity/version, target + compiler/linker environment,
|
|
1188
|
-
* effective driver invocations for the real build flags,
|
|
1189
|
-
* implicit system-header and resolved linker-input bytes,
|
|
1190
|
-
* linker/assembler identities, runtime fingerprint
|
|
1191
|
-
* (every .c/.h in the runtime src dir plus the vendor pin
|
|
1192
|
-
* QJS_COMMIT), the caller's dependency identity, the
|
|
1193
|
-
* compiler-visible TU path, Darwin output basename, the
|
|
1194
|
-
* FULL normalized command line, and the emitted LLVM bytes).
|
|
1195
|
-
* Emitted LLVM is
|
|
1196
|
-
* deterministic, so unchanged programs
|
|
1197
|
-
* hit; any flag difference — e.g. the sanitized lane's
|
|
1198
|
-
* -O1/-fsanitize=address/-DSCR_RC_AUDIT — lands in a
|
|
1199
|
-
* naturally distinct key. On a hit the cached binary is
|
|
1200
|
-
* copied and atomically renamed onto outPath (never an
|
|
1201
|
-
* in-place overwrite — see the hit path) after its digest
|
|
1202
|
-
* is verified; the binary is still EXECUTED live by whoever
|
|
1203
|
-
* asked for it, so no comparison or sanitizer coverage
|
|
1204
|
-
* is ever skipped.
|
|
1205
|
-
*
|
|
1206
|
-
* lib/<key> — whole library archives. The identity covers the resolved
|
|
1207
|
-
* compiler and archiver identities/versions, compiler/linker
|
|
1208
|
-
* environment, effective build-flavor driver invocations,
|
|
1209
|
-
* implicit toolchain inputs, runtime fingerprint,
|
|
1210
|
-
* target/flags, gated source set, caller dependency
|
|
1211
|
-
* identity, TU path, and program-TU bytes.
|
|
1212
|
-
* Checksum-verified hits skip native code generation and ar.
|
|
1213
|
-
*
|
|
1214
|
-
* program-obj/<key> — the canonical library program object, after any LLVM
|
|
1215
|
-
* shard merge. Exact TU repeats reuse it directly.
|
|
1216
|
-
*
|
|
1217
|
-
* program-shard/<key> — stable LLVM dev-library buckets keyed by their IR
|
|
1218
|
-
* text and complete toolchain/TU identity. Local edits
|
|
1219
|
-
* retain every bucket whose definitions/declarations did
|
|
1220
|
-
* not change.
|
|
1221
|
-
*
|
|
1222
|
-
* obj/<set>/<f>.o — per-flavor runtime objects for cache-miss builds. The
|
|
1223
|
-
* historical single invocation recompiles every runtime
|
|
1224
|
-
* TU per program (~1.3s at -O2); with cached objects a
|
|
1225
|
-
* miss compiles only the program's LLVM and links (~0.15s).
|
|
1226
|
-
* Library-mode -DSCR_LIB objects use a distinct flavor.
|
|
1227
|
-
* The clang driver hands every input the same option set,
|
|
1228
|
-
* so per-TU `-c` compiles with those options plus a final
|
|
1229
|
-
* link reproduce the single invocation exactly; the
|
|
1230
|
-
* driver's effective invocation under that flavor also
|
|
1231
|
-
* joins the object-set identity. Every
|
|
1232
|
-
* cached object carries a verified digest. Object
|
|
1233
|
-
* compiles go through ccache when it is installed (silent
|
|
1234
|
-
* fallback when not).
|
|
1235
|
-
*
|
|
1236
|
-
* Frontend-generated production builds supply a dependency identity and use a
|
|
1237
|
-
* per-user platform cache by default. Arbitrary low-level TUs omit the identity
|
|
1238
|
-
* and bypass this cache because their include graph is caller-owned.
|
|
1239
|
-
* Compiler wrappers also bypass every persistent tier: a wrapper can branch on
|
|
1240
|
-
* the real source/object topology and inject inputs no synthetic probe sees.
|
|
1241
|
-
* Direct Clang/Zig drivers and Apple's immutable /usr/bin/clang handoff retain
|
|
1242
|
-
* caching; wrapper-driven builds use private, invocation-local vendor outputs.
|
|
1243
|
-
* Opaque archiver wrappers bypass completed library archives for the same
|
|
1244
|
-
* reason while retaining safely keyed runtime objects.
|
|
1245
|
-
* Builds with caller-supplied native link inputs (archive/object paths or
|
|
1246
|
-
* `-l<name>` libraries) also bypass the whole binary cache: a thin archive,
|
|
1247
|
-
* linker script, or ambient resolution can change transitively without
|
|
1248
|
-
* changing the named input's bytes or spelling. Their runtime objects remain
|
|
1249
|
-
* cached, and every invocation performs the final link against current inputs.
|
|
1250
|
-
* SCRIPTC_CACHE_DIR overrides the root (the test lanes use this to stay
|
|
1251
|
-
* repo-local); an explicitly empty value or SCRIPTC_NO_CACHE=1 disables reads
|
|
1252
|
-
* and writes. With caching disabled compileC issues the exact historical
|
|
1253
|
-
* command line.
|
|
1254
|
-
*
|
|
1255
|
-
* Eviction: size-capped LRU over the whole cache root (SCRIPTC_CACHE_MAX_MB,
|
|
1256
|
-
* default 4096). Explicit caps are checked after every successful write; the
|
|
1257
|
-
* large default is swept on the first and every 64th write so corpus/watch
|
|
1258
|
-
* loops do not repeatedly walk a growing tree. Reads bump mtimes. The harness's
|
|
1259
|
-
* oracle cache lives under the same root and is swept by the same pass. Cache
|
|
1260
|
-
* trouble is never a build failure — every cache error falls back to a real
|
|
1261
|
-
* compile. */
|
|
1262
|
-
const ccVersionMemos = new Map();
|
|
1263
|
-
/** Identity of the compiler implementation and configuration selected by a
|
|
1264
|
-
* fresh driver invocation. In particular, Darwin's stable /usr/bin/clang shim
|
|
1265
|
-
* exposes the currently selected Xcode clang only in its `-###` trace. The
|
|
1266
|
-
* normalized trace also covers output-affecting default driver configuration
|
|
1267
|
-
* that can change while argv[0] and PATH retain the same spelling. */
|
|
1268
|
-
async function effectiveCompilerEnvironmentIdentity(driver, env) {
|
|
1269
|
-
const compiler = driver.argv[0] ?? "clang";
|
|
1270
|
-
const resolvedDriver = await resolvedTool(compiler, env);
|
|
1271
|
-
if (resolvedDriver === null)
|
|
1272
|
-
return `<unresolved:${compiler}>`;
|
|
1273
|
-
const probeDir = await mkdtemp(join(tmpdir(), "scriptc-early-cc-probe-"));
|
|
1274
|
-
try {
|
|
1275
|
-
const source = join(probeDir, "empty.c");
|
|
1276
|
-
const object = join(probeDir, "empty.o");
|
|
1277
|
-
await writeFile(source, "int scriptc_early_driver_probe;\n");
|
|
1278
|
-
const trace = await execFileAsync(compiler, [
|
|
1279
|
-
...driver.argv.slice(1),
|
|
1280
|
-
...driver.targetArgs,
|
|
1281
|
-
"-###",
|
|
1282
|
-
"-std=c11",
|
|
1283
|
-
"-c",
|
|
1284
|
-
source,
|
|
1285
|
-
"-o",
|
|
1286
|
-
object,
|
|
1287
|
-
], { cwd: probeDir, env, maxBuffer: 16 * 1024 * 1024 });
|
|
1288
|
-
const effectiveSpellings = [];
|
|
1289
|
-
for (const line of `${trace.stdout}\n${trace.stderr}`.split(/\r?\n/)) {
|
|
1290
|
-
const tokens = driverTraceCandidates(line);
|
|
1291
|
-
const cc1 = tokens.indexOf("-cc1");
|
|
1292
|
-
if (cc1 > 0)
|
|
1293
|
-
effectiveSpellings.push(tokens[cc1 - 1]);
|
|
1294
|
-
}
|
|
1295
|
-
const effective = effectiveSpellings.length === 1
|
|
1296
|
-
? await resolvedTool(effectiveSpellings[0], env)
|
|
1297
|
-
: null;
|
|
1298
|
-
return createHash("sha256")
|
|
1299
|
-
.update("effective-compiler-environment-v1\0")
|
|
1300
|
-
.update(resolvedDriver.cacheIdentity).update("\0")
|
|
1301
|
-
.update(normalizedProbeInvocation(trace, probeDir)).update("\0")
|
|
1302
|
-
.update(effective?.cacheIdentity ?? `<unresolved-effective:${effectiveSpellings.join("\x1f")}>`)
|
|
1303
|
-
.digest("hex");
|
|
1304
|
-
}
|
|
1305
|
-
finally {
|
|
1306
|
-
await rm(probeDir, { recursive: true, force: true }).catch(() => undefined);
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
/** Resolve the executable the OS will select for an argv[0] spelling. The
|
|
1310
|
-
* path and inode metadata join the version output below: two PATH postures
|
|
1311
|
-
* must not share cache entries merely because both drivers call themselves
|
|
1312
|
-
* `clang` or print the same upstream version. ctime catches an in-place tool
|
|
1313
|
-
* replacement even when a package manager preserves its size and mtime. */
|
|
1314
|
-
async function resolvedTool(command, env = process.env) {
|
|
1315
|
-
const hasSeparator = command.includes("/") || command.includes("\\");
|
|
1316
|
-
const configuredPath = (env["PATH"] ?? (process.platform === "win32" ? "" : "/usr/bin:/bin"))
|
|
1317
|
-
.split(delimiter);
|
|
1318
|
-
const pathEntries = hasSeparator
|
|
1319
|
-
? [""]
|
|
1320
|
-
: process.platform === "win32"
|
|
1321
|
-
? ["", dirname(process.execPath), ...configuredPath]
|
|
1322
|
-
: configuredPath;
|
|
1323
|
-
const windowsExtensions = process.platform === "win32" && extname(command) === ""
|
|
1324
|
-
? (env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD").split(";").filter((extension) => extension !== "")
|
|
1325
|
-
: [""];
|
|
1326
|
-
for (const entry of pathEntries) {
|
|
1327
|
-
const directory = entry.startsWith('"') && entry.endsWith('"') ? entry.slice(1, -1) : entry;
|
|
1328
|
-
const base = hasSeparator
|
|
1329
|
-
? isAbsolute(command) ? command : resolve(command)
|
|
1330
|
-
: join(directory === "" ? process.cwd() : directory, command);
|
|
1331
|
-
for (const extension of windowsExtensions) {
|
|
1332
|
-
const candidate = `${base}${extension}`;
|
|
1333
|
-
try {
|
|
1334
|
-
await access(candidate, process.platform === "win32" ? fsConstants.F_OK : fsConstants.X_OK);
|
|
1335
|
-
const [canonical, info] = await Promise.all([realpath(candidate), stat(candidate)]);
|
|
1336
|
-
if (!info.isFile())
|
|
1337
|
-
continue;
|
|
1338
|
-
const fileIdentity = [
|
|
1339
|
-
canonical,
|
|
1340
|
-
info.dev,
|
|
1341
|
-
info.ino,
|
|
1342
|
-
info.size,
|
|
1343
|
-
info.mtimeMs,
|
|
1344
|
-
info.ctimeMs,
|
|
1345
|
-
].join("\0");
|
|
1346
|
-
return {
|
|
1347
|
-
canonicalPath: canonical,
|
|
1348
|
-
fileIdentity,
|
|
1349
|
-
cacheIdentity: [resolve(candidate), fileIdentity].join("\0"),
|
|
1350
|
-
};
|
|
1351
|
-
}
|
|
1352
|
-
catch {
|
|
1353
|
-
// Keep searching PATH/PATHEXT exactly as process spawning would.
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
return null;
|
|
1358
|
-
}
|
|
1359
|
-
async function resolvedToolIdentity(command, env = process.env) {
|
|
1360
|
-
return (await resolvedTool(command, env))?.cacheIdentity ?? null;
|
|
1361
|
-
}
|
|
1362
|
-
const directCompilerDriverMemos = new Map();
|
|
1363
|
-
const directCompilerSelections = new Map();
|
|
1364
|
-
function compilerDriverProbeKey(driver, environmentFingerprint) {
|
|
1365
|
-
return [
|
|
1366
|
-
environmentFingerprint,
|
|
1367
|
-
driver.argv.join("\x1f"),
|
|
1368
|
-
driver.target ?? "<native>",
|
|
1369
|
-
driver.targetArgs.join("\x1f"),
|
|
1370
|
-
].join("\0");
|
|
1371
|
-
}
|
|
1372
|
-
/** `/usr/bin/clang` on Darwin is Apple's immutable driver shim: its `-###`
|
|
1373
|
-
* trace names the selected Xcode/CommandLineTools clang rather than the shim
|
|
1374
|
-
* itself. The ordinary implicit-toolchain fingerprint below captures that
|
|
1375
|
-
* selected executable and its dependencies, so this trusted system handoff is
|
|
1376
|
-
* the one intentional exception to the same-executable rule. */
|
|
1377
|
-
function isAppleSystemClangHandoff(driver, effectiveCompiler) {
|
|
1378
|
-
return process.platform === "darwin" &&
|
|
1379
|
-
driver.canonicalPath === "/usr/bin/clang" &&
|
|
1380
|
-
basename(effectiveCompiler.canonicalPath) === "clang";
|
|
1381
|
-
}
|
|
1382
|
-
/** Persistent caches require an inspectable compiler driver. A general
|
|
1383
|
-
* wrapper can branch on the real source/object paths or argument topology and
|
|
1384
|
-
* inject inputs only into the final invocation; no synthetic metadata probe
|
|
1385
|
-
* can safely represent that behavior. Accept direct Clang/Zig drivers (plus
|
|
1386
|
-
* Apple's system shim) and conservatively keep wrapper-driven builds on the
|
|
1387
|
-
* uncached path. */
|
|
1388
|
-
export async function compilerDriverSupportsPersistentCache(driver, environmentFingerprint) {
|
|
1389
|
-
// Cache-race tests exercise publication below an intentionally instrumented
|
|
1390
|
-
// wrapper. This is deliberately undocumented and test-scoped.
|
|
1391
|
-
if (process.env["SCRIPTC_TEST_TRUST_COMPILER_WRAPPER"] === "1")
|
|
1392
|
-
return true;
|
|
1393
|
-
const driverKey = compilerDriverProbeKey(driver, environmentFingerprint);
|
|
1394
|
-
const compiler = driver.argv[0] ?? "clang";
|
|
1395
|
-
const resolvedDriver = await resolvedTool(compiler);
|
|
1396
|
-
// A prior dependency list cannot prove that name resolution is unchanged:
|
|
1397
|
-
// a new header in an earlier search directory leaves every previously
|
|
1398
|
-
// resolved file untouched. Require the driver to be present so each cache
|
|
1399
|
-
// invocation can rediscover its complete dependency graph.
|
|
1400
|
-
if (resolvedDriver === null)
|
|
1401
|
-
return false;
|
|
1402
|
-
const probeKey = `${driverKey}\0${resolvedDriver.fileIdentity}`;
|
|
1403
|
-
const memoized = directCompilerDriverMemos.get(probeKey);
|
|
1404
|
-
if (memoized !== undefined)
|
|
1405
|
-
return memoized;
|
|
1406
|
-
const probeDir = await mkdtemp(join(tmpdir(), "scriptc-driver-probe-"));
|
|
1407
|
-
let direct = false;
|
|
1408
|
-
try {
|
|
1409
|
-
const source = join(probeDir, "empty.c");
|
|
1410
|
-
const object = join(probeDir, "empty.o");
|
|
1411
|
-
await writeFile(source, "int scriptc_driver_probe;\n");
|
|
1412
|
-
const trace = await execFileAsync(compiler, [
|
|
1413
|
-
...driver.argv.slice(1),
|
|
1414
|
-
...driver.targetArgs,
|
|
1415
|
-
"-###",
|
|
1416
|
-
"-std=c11",
|
|
1417
|
-
"-c",
|
|
1418
|
-
source,
|
|
1419
|
-
"-o",
|
|
1420
|
-
object,
|
|
1421
|
-
], { cwd: probeDir, maxBuffer: 16 * 1024 * 1024 });
|
|
1422
|
-
const effectiveSpellings = [];
|
|
1423
|
-
for (const line of `${trace.stdout}\n${trace.stderr}`.split(/\r?\n/)) {
|
|
1424
|
-
const tokens = driverTraceCandidates(line);
|
|
1425
|
-
const cc1 = tokens.indexOf("-cc1");
|
|
1426
|
-
if (cc1 > 0)
|
|
1427
|
-
effectiveSpellings.push(tokens[cc1 - 1]);
|
|
1428
|
-
}
|
|
1429
|
-
if (effectiveSpellings.length === 1) {
|
|
1430
|
-
const effectiveCompiler = await resolvedTool(effectiveSpellings[0]);
|
|
1431
|
-
direct = effectiveCompiler !== null &&
|
|
1432
|
-
(effectiveCompiler.fileIdentity === resolvedDriver.fileIdentity ||
|
|
1433
|
-
isAppleSystemClangHandoff(resolvedDriver, effectiveCompiler));
|
|
1434
|
-
if (direct)
|
|
1435
|
-
directCompilerSelections.set(driverKey, effectiveCompiler);
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
catch {
|
|
1439
|
-
direct = false;
|
|
1440
|
-
}
|
|
1441
|
-
finally {
|
|
1442
|
-
await rm(probeDir, { recursive: true, force: true }).catch(() => undefined);
|
|
1443
|
-
}
|
|
1444
|
-
directCompilerDriverMemos.set(probeKey, direct);
|
|
1445
|
-
if (!direct)
|
|
1446
|
-
directCompilerSelections.delete(driverKey);
|
|
1447
|
-
return direct;
|
|
1448
|
-
}
|
|
1449
|
-
/** Complete library archives may skip `ar`, so the selected archiver must be
|
|
1450
|
-
* as inspectable as the compiler. `zig ar` is the already-validated Zig
|
|
1451
|
-
* executable. For the host spelling, accept only the immutable platform
|
|
1452
|
-
* tool locations; a PATH wrapper can branch on the real member topology or
|
|
1453
|
-
* inject mutable inputs that `ar --version` cannot expose. Other archivers
|
|
1454
|
-
* retain runtime-object reuse but rebuild the program member and archive. */
|
|
1455
|
-
async function archiverSupportsPersistentCache(arArgv, driver) {
|
|
1456
|
-
if (arArgv.length === 2 &&
|
|
1457
|
-
arArgv[0] === driver.argv[0] &&
|
|
1458
|
-
arArgv[1] === "ar" &&
|
|
1459
|
-
driver.argv[0] === "zig") {
|
|
1460
|
-
return true;
|
|
1461
|
-
}
|
|
1462
|
-
if (arArgv.length !== 1)
|
|
1463
|
-
return false;
|
|
1464
|
-
const archiver = await resolvedTool(arArgv[0] ?? "ar");
|
|
1465
|
-
if (archiver === null || !/(?:^|-)ar$/.test(basename(archiver.canonicalPath))) {
|
|
1466
|
-
return false;
|
|
1467
|
-
}
|
|
1468
|
-
if (archiver.canonicalPath.startsWith("/usr/bin/") || archiver.canonicalPath.startsWith("/bin/")) {
|
|
1469
|
-
return true;
|
|
1470
|
-
}
|
|
1471
|
-
return process.platform === "darwin" &&
|
|
1472
|
-
/^\/Applications\/Xcode[^/]*\.app\/Contents\/Developer\/Toolchains\/[^/]+\.xctoolchain\/usr\/bin\/[^/]*ar$/.test(archiver.canonicalPath);
|
|
1473
|
-
}
|
|
1474
|
-
export async function ccVersion(argv, environmentFingerprint = toolchainEnvironmentFingerprint(), fresh = false) {
|
|
1475
|
-
const spellingKey = `${environmentFingerprint}\0${argv.join("\x1f")}`;
|
|
1476
|
-
const executableIdentity = await resolvedToolIdentity(argv[0] ?? "clang");
|
|
1477
|
-
const key = `${spellingKey}\0${executableIdentity ?? "<unresolved>"}`;
|
|
1478
|
-
const probe = () =>
|
|
1479
|
-
// `zig cc --version` (zig 0.16) drops an empty a.o in its cwd. A private
|
|
1480
|
-
// probe directory avoids both caller pollution and the cross-process /
|
|
1481
|
-
// cross-user collision a fixed tmpdir()/a.o would create on Linux.
|
|
1482
|
-
(async () => {
|
|
1483
|
-
const probeDir = await mkdtemp(join(tmpdir(), "scriptc-cc-version-"));
|
|
1484
|
-
try {
|
|
1485
|
-
const r = await execFileAsync(argv[0] ?? "clang", [...argv.slice(1), "--version"], { cwd: probeDir });
|
|
1486
|
-
return `${executableIdentity ?? "<unresolved>"}\0${`${r.stdout}\n${r.stderr}`.trim()}`;
|
|
1487
|
-
}
|
|
1488
|
-
finally {
|
|
1489
|
-
await rm(probeDir, { recursive: true, force: true }).catch(() => undefined);
|
|
1490
|
-
}
|
|
1491
|
-
})();
|
|
1492
|
-
const requireFreshProbe = fresh && !stableTestToolchainSession();
|
|
1493
|
-
let memo = requireFreshProbe && executableIdentity !== null ? probe() : ccVersionMemos.get(key);
|
|
1494
|
-
if (memo === undefined) {
|
|
1495
|
-
memo = probe();
|
|
1496
|
-
ccVersionMemos.set(key, memo);
|
|
1497
|
-
}
|
|
1498
|
-
else if (requireFreshProbe) {
|
|
1499
|
-
ccVersionMemos.set(key, memo);
|
|
1500
|
-
}
|
|
1501
|
-
return memo;
|
|
1502
|
-
}
|
|
1503
|
-
const toolVersionMemos = new Map();
|
|
1504
|
-
const toolVersionFallbacks = new Map();
|
|
1505
|
-
async function toolVersionOnce(argv, environmentFingerprint = toolchainEnvironmentFingerprint(), fresh = false) {
|
|
1506
|
-
const spellingKey = `${environmentFingerprint}\0${argv.join("\x1f")}`;
|
|
1507
|
-
const executableIdentity = await resolvedToolIdentity(argv[0] ?? "ar");
|
|
1508
|
-
if (executableIdentity === null) {
|
|
1509
|
-
const fallback = toolVersionFallbacks.get(spellingKey);
|
|
1510
|
-
if (fallback !== undefined)
|
|
1511
|
-
return fallback;
|
|
1512
|
-
}
|
|
1513
|
-
const key = `${spellingKey}\0${executableIdentity ?? "<unresolved>"}`;
|
|
1514
|
-
const probe = () => execFileAsync(argv[0] ?? "ar", [...argv.slice(1), "--version"]).then((r) => `${executableIdentity ?? "<unresolved>"}\0${`${r.stdout}\n${r.stderr}`.trim()}`, (err) => `${executableIdentity ?? "<unresolved>"}\0${`${err.stdout ?? ""}\n${err.stderr ?? ""}`.trim() || err.message || key}`);
|
|
1515
|
-
let memo = fresh && executableIdentity !== null ? probe() : toolVersionMemos.get(key);
|
|
1516
|
-
if (memo === undefined) {
|
|
1517
|
-
memo = probe();
|
|
1518
|
-
toolVersionMemos.set(key, memo);
|
|
1519
|
-
}
|
|
1520
|
-
else if (fresh) {
|
|
1521
|
-
toolVersionMemos.set(key, memo);
|
|
1522
|
-
}
|
|
1523
|
-
if (executableIdentity !== null)
|
|
1524
|
-
toolVersionFallbacks.set(spellingKey, memo);
|
|
1525
|
-
return memo;
|
|
1526
|
-
}
|
|
1527
|
-
/** Dependency paths discovered while computing one strict fingerprint. The
|
|
1528
|
-
* output-local cache stamp snapshots these exact files/directories after a
|
|
1529
|
-
* validated build, then can prove a later same-output no-op without spawning
|
|
1530
|
-
* clang again. The content identity closes the gap between hashing and that
|
|
1531
|
-
* metadata snapshot: a file changed in the gap cannot make new bytes ride an
|
|
1532
|
-
* old key. Keep the map bounded for long-lived corpus/test processes. */
|
|
1533
|
-
const fingerprintDependencies = new Map();
|
|
1534
|
-
function rememberFingerprintDependencies(fingerprint, paths, contentPaths = [], contentFingerprint = null) {
|
|
1535
|
-
fingerprintDependencies.set(fingerprint, {
|
|
1536
|
-
paths: [...new Set(paths)].sort(),
|
|
1537
|
-
contentPaths: [...new Set(contentPaths)].sort(),
|
|
1538
|
-
contentFingerprint,
|
|
1539
|
-
});
|
|
1540
|
-
if (fingerprintDependencies.size > 256) {
|
|
1541
|
-
const oldest = fingerprintDependencies.keys().next().value;
|
|
1542
|
-
if (oldest !== undefined)
|
|
1543
|
-
fingerprintDependencies.delete(oldest);
|
|
1544
|
-
}
|
|
1545
|
-
return fingerprint;
|
|
1546
|
-
}
|
|
1547
|
-
function fingerprintDependencyPaths(fingerprint) {
|
|
1548
|
-
return fingerprintDependencies.get(fingerprint)?.paths ?? [];
|
|
1549
|
-
}
|
|
1550
|
-
function parseMakeDependencies(output, cwd = process.cwd()) {
|
|
1551
|
-
const flattened = output.replace(/\\\r?\n/g, " ");
|
|
1552
|
-
const separator = flattened.indexOf(": ");
|
|
1553
|
-
if (separator < 0)
|
|
1554
|
-
throw new Error("compiler dependency probe returned no make rule");
|
|
1555
|
-
const input = flattened.slice(separator + 2);
|
|
1556
|
-
const paths = [];
|
|
1557
|
-
let current = "";
|
|
1558
|
-
let escaped = false;
|
|
1559
|
-
for (const char of input) {
|
|
1560
|
-
if (escaped) {
|
|
1561
|
-
current += char;
|
|
1562
|
-
escaped = false;
|
|
1563
|
-
}
|
|
1564
|
-
else if (char === "\\") {
|
|
1565
|
-
escaped = true;
|
|
1566
|
-
}
|
|
1567
|
-
else if (/\s/.test(char)) {
|
|
1568
|
-
if (current !== "") {
|
|
1569
|
-
paths.push(current.replace(/\$\$/g, "$"));
|
|
1570
|
-
current = "";
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
else {
|
|
1574
|
-
current += char;
|
|
1575
|
-
}
|
|
1576
|
-
}
|
|
1577
|
-
if (escaped)
|
|
1578
|
-
current += "\\";
|
|
1579
|
-
if (current !== "")
|
|
1580
|
-
paths.push(current.replace(/\$\$/g, "$"));
|
|
1581
|
-
return [...new Set(paths.map((path) => resolve(cwd, path)))].sort();
|
|
1582
|
-
}
|
|
1583
|
-
function normalizedProbeInvocation(result, probeDir) {
|
|
1584
|
-
return `${result.stdout}\n${result.stderr}`
|
|
1585
|
-
// Probe-local paths vary on every invocation and carry no toolchain
|
|
1586
|
-
// identity. Both ordinary and shell-escaped Windows spellings can appear
|
|
1587
|
-
// in a driver's quoted trace.
|
|
1588
|
-
.split(probeDir).join("<probe>")
|
|
1589
|
-
.split(probeDir.replace(/\\/g, "\\\\")).join("<probe>")
|
|
1590
|
-
.trim();
|
|
1591
|
-
}
|
|
1592
|
-
/** The effective cc1 invocation and injected dependencies for the flags used
|
|
1593
|
-
* by real runtime/program compiles. The broad implicit-toolchain probe below
|
|
1594
|
-
* intentionally uses a target-wide synthetic TU so it can discover every
|
|
1595
|
-
* owned system header, but that generic command is not sufficient identity for
|
|
1596
|
-
* a wrapper that injects flags or preincluded files only for a particular build
|
|
1597
|
-
* flavor (for example, only when it sees -O2 or -DSCR_DYNAMIC). */
|
|
1598
|
-
async function effectiveCompilerInvocationFingerprintFresh(driver, environmentFingerprint, compileArgs, sourceExtension = ".c") {
|
|
1599
|
-
const compiler = driver.argv[0] ?? "clang";
|
|
1600
|
-
const compilerIdentity = await resolvedToolIdentity(compiler);
|
|
1601
|
-
if (compilerIdentity === null) {
|
|
1602
|
-
throw new Error("compiler unavailable before effective invocation identity was established");
|
|
1603
|
-
}
|
|
1604
|
-
let probe;
|
|
1605
|
-
{
|
|
1606
|
-
const probeDir = await mkdtemp(join(tmpdir(), "scriptc-effective-cc-probe-"));
|
|
1607
|
-
try {
|
|
1608
|
-
const source = join(probeDir, `program${sourceExtension}`);
|
|
1609
|
-
const output = join(probeDir, "program.o");
|
|
1610
|
-
await writeFile(source, sourceExtension === ".ll"
|
|
1611
|
-
? "define i32 @scriptc_effective_probe() { ret i32 0 }\n"
|
|
1612
|
-
: "int scriptc_effective_probe(void) { return 0; }\n");
|
|
1613
|
-
const prefix = [...driver.argv.slice(1), ...compileArgs];
|
|
1614
|
-
const [invocation, dependencyResult] = await Promise.all([
|
|
1615
|
-
execFileAsync(compiler, [...prefix, "-###", "-c", source, "-o", output], { cwd: probeDir, maxBuffer: 16 * 1024 * 1024 }),
|
|
1616
|
-
sourceExtension === ".c"
|
|
1617
|
-
? execFileAsync(compiler, [...prefix, "-M", source], {
|
|
1618
|
-
cwd: probeDir,
|
|
1619
|
-
maxBuffer: 16 * 1024 * 1024,
|
|
1620
|
-
})
|
|
1621
|
-
: Promise.resolve(null),
|
|
1622
|
-
]);
|
|
1623
|
-
const dependencies = dependencyResult === null
|
|
1624
|
-
? []
|
|
1625
|
-
: parseMakeDependencies(dependencyResult.stdout, probeDir).filter((path) => path !== resolve(source));
|
|
1626
|
-
probe = {
|
|
1627
|
-
compilerIdentity,
|
|
1628
|
-
invocation: normalizedProbeInvocation(invocation, probeDir),
|
|
1629
|
-
dependencies,
|
|
1630
|
-
dependencyFingerprint: await fingerprintDependencyFiles(dependencies),
|
|
1631
|
-
invocationPaths: await existingDriverTracePaths(`${invocation.stdout}\n${invocation.stderr}`, probeDir, probeDir),
|
|
1632
|
-
};
|
|
1633
|
-
}
|
|
1634
|
-
finally {
|
|
1635
|
-
await rm(probeDir, { recursive: true, force: true }).catch(() => undefined);
|
|
1636
|
-
}
|
|
1637
|
-
}
|
|
1638
|
-
const fingerprint = createHash("sha256")
|
|
1639
|
-
.update("effective-compiler-invocation-v2\0")
|
|
1640
|
-
.update(environmentFingerprint)
|
|
1641
|
-
.update("\0")
|
|
1642
|
-
.update(probe.compilerIdentity)
|
|
1643
|
-
.update("\0")
|
|
1644
|
-
.update(driver.argv.join("\x1f"))
|
|
1645
|
-
.update("\0")
|
|
1646
|
-
.update(compileArgs.join("\x1f"))
|
|
1647
|
-
.update("\0")
|
|
1648
|
-
.update(sourceExtension)
|
|
1649
|
-
.update("\0")
|
|
1650
|
-
.update(probe.invocation)
|
|
1651
|
-
.update("\0")
|
|
1652
|
-
.update(probe.dependencies.join("\x1f"))
|
|
1653
|
-
.update("\0")
|
|
1654
|
-
.update(probe.dependencyFingerprint)
|
|
1655
|
-
.digest("hex");
|
|
1656
|
-
return rememberFingerprintDependencies(fingerprint, [...probe.dependencies, ...probe.invocationPaths], probe.dependencies, probe.dependencyFingerprint);
|
|
1657
|
-
}
|
|
1658
|
-
const stableEffectiveCompilerInvocationMemos = new Map();
|
|
1659
|
-
function effectiveCompilerInvocationFingerprint(driver, environmentFingerprint, compileArgs, sourceExtension = ".c") {
|
|
1660
|
-
const key = [
|
|
1661
|
-
environmentFingerprint,
|
|
1662
|
-
driver.argv.join("\x1f"),
|
|
1663
|
-
compileArgs.join("\x1f"),
|
|
1664
|
-
sourceExtension,
|
|
1665
|
-
].join("\0");
|
|
1666
|
-
return stableTestMemo(stableEffectiveCompilerInvocationMemos, key, () => effectiveCompilerInvocationFingerprintFresh(driver, environmentFingerprint, compileArgs, sourceExtension));
|
|
1667
|
-
}
|
|
1668
|
-
async function nativeSourceFiles(directory, recursive, include = (name) => name.endsWith(".c") || name.endsWith(".h")) {
|
|
1669
|
-
const files = [];
|
|
1670
|
-
const walk = async (current, ancestors) => {
|
|
1671
|
-
const canonical = await realpath(current).catch(() => resolve(current));
|
|
1672
|
-
if (ancestors.has(canonical))
|
|
1673
|
-
return;
|
|
1674
|
-
const nestedAncestors = new Set(ancestors).add(canonical);
|
|
1675
|
-
const entries = await readdir(current, { withFileTypes: true }).catch(() => []);
|
|
1676
|
-
for (const entry of entries) {
|
|
1677
|
-
const path = join(current, entry.name);
|
|
1678
|
-
if (entry.isDirectory() && recursive) {
|
|
1679
|
-
await walk(path, nestedAncestors);
|
|
1680
|
-
}
|
|
1681
|
-
else if (entry.isSymbolicLink()) {
|
|
1682
|
-
const target = await stat(path).catch(() => null);
|
|
1683
|
-
if (target?.isDirectory() && recursive)
|
|
1684
|
-
await walk(path, nestedAncestors);
|
|
1685
|
-
else if (target?.isFile() && include(entry.name))
|
|
1686
|
-
files.push(path);
|
|
1687
|
-
}
|
|
1688
|
-
else if (entry.isFile() && include(entry.name)) {
|
|
1689
|
-
files.push(path);
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
};
|
|
1693
|
-
await walk(directory, new Set());
|
|
1694
|
-
return files.sort();
|
|
1695
|
-
}
|
|
1696
|
-
/** Every system-header spelling consumed by a source tree scriptc owns. The
|
|
1697
|
-
* dependency probe preprocesses these spellings with the selected driver and
|
|
1698
|
-
* hashes the resolved files. Runtime-local and vendored source/header bytes
|
|
1699
|
-
* have their own content/version identities; scanning every owned file for
|
|
1700
|
-
* angle includes closes the system-header gap in separately built QuickJS,
|
|
1701
|
-
* libregexp, mbedTLS, zlib, curl, and Ryū translation units. */
|
|
1702
|
-
export async function implicitDependencyProbeIncludes(rtDir) {
|
|
1703
|
-
const vendor = join(rtDir, "..", "vendor");
|
|
1704
|
-
const quickjsSources = new Set([...QJS_ENGINE_SOURCES, ...LRE_SOURCES]);
|
|
1705
|
-
const zlibSources = new Set(ZLIB_SOURCES);
|
|
1706
|
-
const fileGroups = await Promise.all([
|
|
1707
|
-
nativeSourceFiles(rtDir, false),
|
|
1708
|
-
nativeSourceFiles(join(vendor, "ryu"), false),
|
|
1709
|
-
nativeSourceFiles(join(vendor, "quickjs-ng"), false, (name) => name.endsWith(".h") || quickjsSources.has(name)),
|
|
1710
|
-
nativeSourceFiles(join(vendor, "mbedtls", "include"), true),
|
|
1711
|
-
nativeSourceFiles(join(vendor, "mbedtls", "library"), true),
|
|
1712
|
-
nativeSourceFiles(join(vendor, "zlib"), false, (name) => name.endsWith(".h") || zlibSources.has(name)),
|
|
1713
|
-
nativeSourceFiles(join(vendor, "curl", "include"), true, (name) => name.endsWith(".h")),
|
|
1714
|
-
]);
|
|
1715
|
-
const includes = new Set();
|
|
1716
|
-
for (const path of fileGroups.flat()) {
|
|
1717
|
-
const source = await readFile(path, "utf8");
|
|
1718
|
-
for (const match of source.matchAll(/^\s*#\s*include\s*<([^>]+)>/gm)) {
|
|
1719
|
-
includes.add(`<${match[1]}>`);
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
1722
|
-
return [...includes].sort();
|
|
1723
|
-
}
|
|
1724
|
-
function implicitDependencyIncludeDirective(include) {
|
|
1725
|
-
const targetCondition = include === "<cpuid.h>" || include === "<immintrin.h>"
|
|
1726
|
-
? " && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64))"
|
|
1727
|
-
: include === "<intrin.h>"
|
|
1728
|
-
? " && defined(_WIN32)"
|
|
1729
|
-
: include === "<arm64_neon.h>" || include === "<arm_acle.h>" || include === "<arm_neon.h>"
|
|
1730
|
-
? " && (defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64))"
|
|
1731
|
-
: "";
|
|
1732
|
-
return `#if __has_include(${include})${targetCondition}\n#include ${include}\n#endif\n`;
|
|
1733
|
-
}
|
|
1734
|
-
async function fingerprintDependencyFiles(paths) {
|
|
1735
|
-
const hash = createHash("sha256").update("implicit-dependencies-v1\0");
|
|
1736
|
-
for (let i = 0; i < paths.length; i += 128) {
|
|
1737
|
-
const batch = paths.slice(i, i + 128);
|
|
1738
|
-
const contents = await Promise.all(batch.map((path) => readFile(path)));
|
|
1739
|
-
for (let j = 0; j < batch.length; j++) {
|
|
1740
|
-
hash.update(batch[j]).update("\0").update(contents[j]).update("\0");
|
|
1741
|
-
}
|
|
1742
|
-
}
|
|
1743
|
-
return hash.digest("hex");
|
|
1744
|
-
}
|
|
1745
|
-
async function fingerprintDependenciesStillMatch(fingerprints) {
|
|
1746
|
-
const distinct = [...new Set(fingerprints)];
|
|
1747
|
-
const identities = distinct
|
|
1748
|
-
.map((fingerprint) => fingerprintDependencies.get(fingerprint))
|
|
1749
|
-
// A restored native-metadata stamp carries and revalidates the dependency
|
|
1750
|
-
// snapshot from the process that minted this fingerprint. Only identities
|
|
1751
|
-
// computed in this process have an additional content hash to recheck here.
|
|
1752
|
-
.filter((identity) => identity !== undefined);
|
|
1753
|
-
return (await Promise.all(identities.map(async (identity) => identity.contentFingerprint === null ||
|
|
1754
|
-
await fingerprintDependencyFiles(identity.contentPaths).catch(() => null) ===
|
|
1755
|
-
identity.contentFingerprint))).every(Boolean);
|
|
1756
|
-
}
|
|
1757
|
-
/** Exact headers selected while preprocessing the caller's translation unit.
|
|
1758
|
-
* The shared toolchain probe covers the runtime/vendor trees, but compileC is
|
|
1759
|
-
* also a public API: an opted-in caller can include a system or header-only SDK
|
|
1760
|
-
* surface that no runtime source names. Probe an invocation-private snapshot
|
|
1761
|
-
* of the keyed bytes with the real compile flags, preserving the original
|
|
1762
|
-
* quote-include directory and compiler-visible source spelling. */
|
|
1763
|
-
async function translationUnitDependencyFingerprintFresh(driver, cflags, sourcePath, sourceBytes, environmentFingerprint) {
|
|
1764
|
-
if (sourcePath.endsWith(".ll")) {
|
|
1765
|
-
return rememberFingerprintDependencies(createHash("sha256").update("translation-unit-dependencies-v1\0llvm-ir").digest("hex"), []);
|
|
1766
|
-
}
|
|
1767
|
-
const compiler = driver.argv[0] ?? "clang";
|
|
1768
|
-
const compilerIdentity = await resolvedToolIdentity(compiler);
|
|
1769
|
-
if (compilerIdentity === null) {
|
|
1770
|
-
throw new Error("compiler unavailable before translation-unit dependencies were established");
|
|
1771
|
-
}
|
|
1772
|
-
let probe;
|
|
1773
|
-
{
|
|
1774
|
-
const probeDir = await mkdtemp(join(tmpdir(), "scriptc-tu-probe-"));
|
|
1775
|
-
try {
|
|
1776
|
-
const snapshot = join(probeDir, "program.c");
|
|
1777
|
-
await writeFile(snapshot, sourceBytes);
|
|
1778
|
-
const result = await execFileAsync(compiler, [
|
|
1779
|
-
...driver.argv.slice(1),
|
|
1780
|
-
// The real source directory is searched before every caller-supplied
|
|
1781
|
-
// -iquote/-I directory. Put its surrogate first to preserve that
|
|
1782
|
-
// precedence after moving the keyed bytes into the probe directory.
|
|
1783
|
-
"-iquote",
|
|
1784
|
-
dirname(resolve(sourcePath)),
|
|
1785
|
-
...cflags,
|
|
1786
|
-
`-ffile-prefix-map=${snapshot}=${sourcePath}`,
|
|
1787
|
-
"-M",
|
|
1788
|
-
snapshot,
|
|
1789
|
-
], { cwd: probeDir, maxBuffer: 16 * 1024 * 1024 });
|
|
1790
|
-
const ownPaths = new Set([resolve(snapshot), resolve(sourcePath)]);
|
|
1791
|
-
const dependencies = parseMakeDependencies(result.stdout, probeDir).filter((path) => !ownPaths.has(path));
|
|
1792
|
-
probe = {
|
|
1793
|
-
compilerIdentity,
|
|
1794
|
-
dependencies,
|
|
1795
|
-
dependencyFingerprint: await fingerprintDependencyFiles(dependencies),
|
|
1796
|
-
};
|
|
1797
|
-
}
|
|
1798
|
-
finally {
|
|
1799
|
-
await rm(probeDir, { recursive: true, force: true }).catch(() => undefined);
|
|
1800
|
-
}
|
|
1801
|
-
}
|
|
1802
|
-
const fingerprint = createHash("sha256")
|
|
1803
|
-
.update("translation-unit-dependencies-v1\0")
|
|
1804
|
-
.update(environmentFingerprint)
|
|
1805
|
-
.update("\0")
|
|
1806
|
-
.update(probe.compilerIdentity)
|
|
1807
|
-
.update("\0")
|
|
1808
|
-
.update(probe.dependencies.join("\x1f"))
|
|
1809
|
-
.update("\0")
|
|
1810
|
-
.update(probe.dependencyFingerprint)
|
|
1811
|
-
.digest("hex");
|
|
1812
|
-
return rememberFingerprintDependencies(fingerprint, probe.dependencies, probe.dependencies, probe.dependencyFingerprint);
|
|
1813
|
-
}
|
|
1814
|
-
const stableTranslationUnitDependencyMemos = new Map();
|
|
1815
|
-
function translationUnitDependencyFingerprint(driver, cflags, sourcePath, sourceBytes, environmentFingerprint) {
|
|
1816
|
-
const key = createHash("sha256")
|
|
1817
|
-
.update(environmentFingerprint)
|
|
1818
|
-
.update("\0")
|
|
1819
|
-
.update(driver.argv.join("\x1f"))
|
|
1820
|
-
.update("\0")
|
|
1821
|
-
.update(cflags.join("\x1f"))
|
|
1822
|
-
.update("\0")
|
|
1823
|
-
.update(sourcePath)
|
|
1824
|
-
.update("\0")
|
|
1825
|
-
.update(resolve(sourcePath))
|
|
1826
|
-
.update("\0")
|
|
1827
|
-
.update(sourceBytes)
|
|
1828
|
-
.digest("hex");
|
|
1829
|
-
return stableTestMemo(stableTranslationUnitDependencyMemos, key, () => translationUnitDependencyFingerprintFresh(driver, cflags, sourcePath, sourceBytes, environmentFingerprint));
|
|
1830
|
-
}
|
|
1831
|
-
/** Identity of implicit compiler inputs that do not appear in buildArgs:
|
|
1832
|
-
* default SDK/system headers and the assembler/linker selected by the driver.
|
|
1833
|
-
* Small preprocessor dependency probes include every header spelling used by
|
|
1834
|
-
* the runtime when it is available for the selected target (and therefore the
|
|
1835
|
-
* vendored headers those TUs consume), then hash the exact dependency bytes.
|
|
1836
|
-
* Vendored source snapshots remain keyed by their version pins. When the
|
|
1837
|
-
* compiler is available, dependency discovery runs afresh so an SDK/config
|
|
1838
|
-
* change that redirects includes cannot hide behind an unchanged old path
|
|
1839
|
-
* list. Dependency discovery must succeed on every cache-enabled invocation;
|
|
1840
|
-
* a prior path list cannot reveal a new higher-priority header. */
|
|
1841
|
-
async function implicitToolchainFingerprintsFresh(driver, environmentFingerprint) {
|
|
1842
|
-
let probe;
|
|
1843
|
-
const compiler = driver.argv[0] ?? "clang";
|
|
1844
|
-
const compilerIdentity = await resolvedToolIdentity(compiler);
|
|
1845
|
-
if (compilerIdentity === null) {
|
|
1846
|
-
throw new Error("compiler unavailable before implicit toolchain identity was established");
|
|
1847
|
-
}
|
|
1848
|
-
{
|
|
1849
|
-
const probeDir = await mkdtemp(join(tmpdir(), "scriptc-toolchain-probe-"));
|
|
1850
|
-
try {
|
|
1851
|
-
const dependencyIncludes = await implicitDependencyProbeIncludes(runtimeSrcDir());
|
|
1852
|
-
// Clang's GNU cpuid.h and Windows intrin.h both define `__cpuid` with
|
|
1853
|
-
// incompatible signatures. Real TUs select only one context; preserve
|
|
1854
|
-
// that isolation while still discovering both targets' dependency sets.
|
|
1855
|
-
const sourceGroups = [
|
|
1856
|
-
dependencyIncludes.filter((include) => include !== "<intrin.h>"),
|
|
1857
|
-
dependencyIncludes.filter((include) => include === "<intrin.h>"),
|
|
1858
|
-
].filter((group) => group.length > 0);
|
|
1859
|
-
const sources = sourceGroups.map((_, index) => join(probeDir, `empty-${index}.c`));
|
|
1860
|
-
const driverSource = join(probeDir, "driver-empty.c");
|
|
1861
|
-
const driverOutput = join(probeDir, "driver-output.o");
|
|
1862
|
-
await Promise.all([
|
|
1863
|
-
...sources.map((source, index) => writeFile(source, sourceGroups[index].map(implicitDependencyIncludeDirective).join(""))),
|
|
1864
|
-
writeFile(driverSource, "int scriptc_driver_probe;\n"),
|
|
1865
|
-
]);
|
|
1866
|
-
const prefix = [...driver.argv.slice(1), ...driver.targetArgs];
|
|
1867
|
-
const probeArgs = [
|
|
1868
|
-
...prefix,
|
|
1869
|
-
"-std=c11",
|
|
1870
|
-
"-D_GNU_SOURCE",
|
|
1871
|
-
"-D_XOPEN_SOURCE=700",
|
|
1872
|
-
"-I", runtimeSrcDir(),
|
|
1873
|
-
"-I", vendorEngineDir(),
|
|
1874
|
-
"-I", join(vendorTlsDir(), "include"),
|
|
1875
|
-
"-I", join(vendorTlsDir(), "library"),
|
|
1876
|
-
"-I", vendorZlibDir(),
|
|
1877
|
-
"-I", join(vendorCurlDir(), "include"),
|
|
1878
|
-
"-M",
|
|
1879
|
-
];
|
|
1880
|
-
const [dependencyResults, linker, assembler, compilerInvocation] = await Promise.all([
|
|
1881
|
-
Promise.all(sources.map((source) => execFileAsync(compiler, [...probeArgs, source], {
|
|
1882
|
-
cwd: probeDir,
|
|
1883
|
-
maxBuffer: 16 * 1024 * 1024,
|
|
1884
|
-
}))),
|
|
1885
|
-
execFileAsync(compiler, [...prefix, "-print-prog-name=ld"], { cwd: probeDir }),
|
|
1886
|
-
execFileAsync(compiler, [...prefix, "-print-prog-name=as"], { cwd: probeDir }),
|
|
1887
|
-
// `-###` exposes the effective cc1 invocation after compiler-driver
|
|
1888
|
-
// config and ordinary wrappers have injected their implicit flags. A
|
|
1889
|
-
// wrapper can read environment variables unknown to scriptc; hashing
|
|
1890
|
-
// this trace keeps those flags from hiding behind an unchanged wrapper
|
|
1891
|
-
// executable/version and dependency set.
|
|
1892
|
-
execFileAsync(compiler, [...prefix, "-std=c11", "-###", "-c", driverSource, "-o", driverOutput], { cwd: probeDir, maxBuffer: 16 * 1024 * 1024 }),
|
|
1893
|
-
]);
|
|
1894
|
-
const assemblerSpelling = assembler.stdout.trim();
|
|
1895
|
-
const toolSpellings = [linker.stdout.trim(), assemblerSpelling].filter((value, index, all) => value !== "" && all.indexOf(value) === index);
|
|
1896
|
-
const sourceSet = new Set(sources);
|
|
1897
|
-
const dependencyPaths = [
|
|
1898
|
-
...new Set(dependencyResults
|
|
1899
|
-
.flatMap((dependencies) => parseMakeDependencies(dependencies.stdout))
|
|
1900
|
-
.filter((path) => !sourceSet.has(path))),
|
|
1901
|
-
].sort();
|
|
1902
|
-
probe = {
|
|
1903
|
-
compilerIdentity: compilerIdentity ?? `<unresolved>\0${compiler}`,
|
|
1904
|
-
compilerInvocation: normalizedProbeInvocation(compilerInvocation, probeDir),
|
|
1905
|
-
dependencies: dependencyPaths,
|
|
1906
|
-
dependencyFingerprint: await fingerprintDependencyFiles(dependencyPaths),
|
|
1907
|
-
invocationPaths: await existingDriverTracePaths(`${compilerInvocation.stdout}\n${compilerInvocation.stderr}`, probeDir, probeDir),
|
|
1908
|
-
tools: await Promise.all(toolSpellings.map(async (spelling) => {
|
|
1909
|
-
const resolved = await resolvedTool(spelling);
|
|
1910
|
-
return {
|
|
1911
|
-
spelling,
|
|
1912
|
-
identity: resolved?.cacheIdentity ?? null,
|
|
1913
|
-
path: resolved?.canonicalPath ?? null,
|
|
1914
|
-
};
|
|
1915
|
-
})),
|
|
1916
|
-
compileToolSpellings: assemblerSpelling === "" ? [] : [assemblerSpelling],
|
|
1917
|
-
};
|
|
1918
|
-
}
|
|
1919
|
-
finally {
|
|
1920
|
-
await rm(probeDir, { recursive: true, force: true }).catch(() => undefined);
|
|
1921
|
-
}
|
|
1922
|
-
}
|
|
1923
|
-
const tools = await Promise.all(probe.tools.map(async (tool) => ({
|
|
1924
|
-
...tool,
|
|
1925
|
-
currentIdentity: await resolvedToolIdentity(tool.spelling),
|
|
1926
|
-
})));
|
|
1927
|
-
const fingerprint = (domain, selectedTools) => {
|
|
1928
|
-
const hash = createHash("sha256")
|
|
1929
|
-
.update(domain)
|
|
1930
|
-
.update(environmentFingerprint)
|
|
1931
|
-
.update("\0")
|
|
1932
|
-
.update(probe.compilerIdentity)
|
|
1933
|
-
.update("\0")
|
|
1934
|
-
.update(probe.compilerInvocation)
|
|
1935
|
-
.update("\0")
|
|
1936
|
-
.update(driver.argv.join("\x1f"))
|
|
1937
|
-
.update("\0")
|
|
1938
|
-
.update(driver.targetArgs.join("\x1f"))
|
|
1939
|
-
.update("\0")
|
|
1940
|
-
.update(probe.dependencies.join("\x1f"))
|
|
1941
|
-
.update("\0")
|
|
1942
|
-
.update(probe.dependencyFingerprint)
|
|
1943
|
-
.update("\0");
|
|
1944
|
-
for (const tool of selectedTools) {
|
|
1945
|
-
hash
|
|
1946
|
-
.update(tool.spelling)
|
|
1947
|
-
.update("\0")
|
|
1948
|
-
.update(tool.currentIdentity ?? tool.identity ?? "<unresolved>")
|
|
1949
|
-
.update("\0");
|
|
1950
|
-
}
|
|
1951
|
-
return rememberFingerprintDependencies(hash.digest("hex"), [
|
|
1952
|
-
...probe.dependencies,
|
|
1953
|
-
...probe.invocationPaths,
|
|
1954
|
-
...selectedTools.flatMap((tool) => tool.path === null ? [] : [tool.path]),
|
|
1955
|
-
], probe.dependencies, probe.dependencyFingerprint);
|
|
1956
|
-
};
|
|
1957
|
-
const compileSpellingSet = new Set(probe.compileToolSpellings);
|
|
1958
|
-
return {
|
|
1959
|
-
// Preserve the established complete fingerprint domain and byte stream.
|
|
1960
|
-
complete: fingerprint("implicit-toolchain-v2\0", tools),
|
|
1961
|
-
compile: fingerprint("implicit-compile-toolchain-v1\0", tools.filter((tool) => compileSpellingSet.has(tool.spelling))),
|
|
1962
|
-
};
|
|
1963
|
-
}
|
|
1964
|
-
const stableImplicitToolchainMemos = new Map();
|
|
1965
|
-
function implicitToolchainFingerprints(driver, environmentFingerprint) {
|
|
1966
|
-
const key = [
|
|
1967
|
-
environmentFingerprint,
|
|
1968
|
-
driver.argv.join("\x1f"),
|
|
1969
|
-
driver.target ?? "<native>",
|
|
1970
|
-
driver.targetArgs.join("\x1f"),
|
|
1971
|
-
runtimeSrcDir(),
|
|
1972
|
-
].join("\0");
|
|
1973
|
-
return stableTestMemo(stableImplicitToolchainMemos, key, () => implicitToolchainFingerprintsFresh(driver, environmentFingerprint));
|
|
1974
|
-
}
|
|
1975
|
-
function implicitToolchainFingerprint(driver, environmentFingerprint) {
|
|
1976
|
-
return implicitToolchainFingerprints(driver, environmentFingerprint).then((fingerprints) => fingerprints.complete);
|
|
1977
|
-
}
|
|
1978
|
-
async function existingDriverTracePaths(output, cwd, excludedRoot) {
|
|
1979
|
-
const candidates = new Set();
|
|
1980
|
-
for (const line of output.split(/\r?\n/)) {
|
|
1981
|
-
const tokens = driverTraceCandidates(line);
|
|
1982
|
-
for (let index = 0; index < tokens.length; index++) {
|
|
1983
|
-
const token = tokens[index];
|
|
1984
|
-
const joinedPathOption = ["-I", "-L", "-F"].find((option) => token.startsWith(option) && token.length > option.length);
|
|
1985
|
-
const equalsPathOption = ["--sysroot=", "-resource-dir="].find((option) => token.startsWith(option));
|
|
1986
|
-
const separatePathOptions = [
|
|
1987
|
-
"-I",
|
|
1988
|
-
"-L",
|
|
1989
|
-
"-F",
|
|
1990
|
-
"--sysroot",
|
|
1991
|
-
"-isysroot",
|
|
1992
|
-
"-resource-dir",
|
|
1993
|
-
"-isystem",
|
|
1994
|
-
"-iquote",
|
|
1995
|
-
"-internal-isystem",
|
|
1996
|
-
"-internal-externc-isystem",
|
|
1997
|
-
"-internal-iframework",
|
|
1998
|
-
];
|
|
1999
|
-
const optionPath = joinedPathOption !== undefined
|
|
2000
|
-
? token.slice(joinedPathOption.length)
|
|
2001
|
-
: equalsPathOption !== undefined
|
|
2002
|
-
? token.slice(equalsPathOption.length)
|
|
2003
|
-
: separatePathOptions.includes(token)
|
|
2004
|
-
? tokens[index + 1] ?? ""
|
|
2005
|
-
: token;
|
|
2006
|
-
if (!isAbsolute(optionPath))
|
|
2007
|
-
continue;
|
|
2008
|
-
const path = resolve(cwd, optionPath);
|
|
2009
|
-
if (path === excludedRoot ||
|
|
2010
|
-
path.startsWith(`${excludedRoot}/`) ||
|
|
2011
|
-
path.startsWith(`${excludedRoot}\\`)) {
|
|
2012
|
-
continue;
|
|
2013
|
-
}
|
|
2014
|
-
candidates.add(path);
|
|
2015
|
-
}
|
|
2016
|
-
}
|
|
2017
|
-
const existing = await Promise.all([...candidates].map(async (path) => [path, await lstat(path).catch(() => null)]));
|
|
2018
|
-
return existing
|
|
2019
|
-
.filter((entry) => entry[1] !== null)
|
|
2020
|
-
.map(([path]) => path)
|
|
2021
|
-
.sort();
|
|
2022
|
-
}
|
|
2023
|
-
export async function parseLinkTraceFiles(output, cwd, excludedRoot, driverDryRun = false) {
|
|
2024
|
-
const files = new Set();
|
|
2025
|
-
for (const line of output.split(/\r?\n/)) {
|
|
2026
|
-
const candidates = driverDryRun ? driverTraceCandidates(line) : linkTraceCandidate(line);
|
|
2027
|
-
for (const candidate of candidates) {
|
|
2028
|
-
const path = isAbsolute(candidate) ? candidate : resolve(cwd, candidate);
|
|
2029
|
-
if (path === excludedRoot || path.startsWith(`${excludedRoot}/`) || path.startsWith(`${excludedRoot}\\`)) {
|
|
2030
|
-
continue;
|
|
2031
|
-
}
|
|
2032
|
-
const info = await stat(path).catch(() => null);
|
|
2033
|
-
if (info?.isFile()) {
|
|
2034
|
-
files.add(path);
|
|
2035
|
-
// Traditional `-Wl,-t` output has one dependency per line, while
|
|
2036
|
-
// Zig's COFF `-###` fallback prints the entire lld-link argv on one
|
|
2037
|
-
// line. Keep scanning every dry-run token so CRT/import/runtime
|
|
2038
|
-
// libraries after the first path also join the fingerprint.
|
|
2039
|
-
if (!driverDryRun)
|
|
2040
|
-
break;
|
|
2041
|
-
}
|
|
2042
|
-
}
|
|
2043
|
-
}
|
|
2044
|
-
return [...files].sort();
|
|
2045
|
-
}
|
|
2046
|
-
/** Exact files selected by the compiler driver's implicit link. A tiny
|
|
2047
|
-
* target object plus the linker's trace mode resolves CRT objects, compiler
|
|
2048
|
-
* runtimes, linker scripts, SDK stubs/import libraries, and every ambient
|
|
2049
|
-
* default library without guessing platform search layouts. The caller adds
|
|
2050
|
-
* build-flavor flags such as ASan and scriptc's own fixed `-l` arguments.
|
|
2051
|
-
* Every cache-enabled invocation performs a fresh trace; a prior resolved path
|
|
2052
|
-
* list cannot reveal a newly selected higher-priority linker input. */
|
|
2053
|
-
async function implicitLinkerFingerprintFresh(driver, environmentFingerprint, linkArgs, effectiveInvocationArgs, traceInvocationArgs) {
|
|
2054
|
-
const invocationArgs = effectiveInvocationArgs ?? [...driver.targetArgs, ...linkArgs];
|
|
2055
|
-
const traceArgs = traceInvocationArgs ?? [...driver.targetArgs, ...linkArgs];
|
|
2056
|
-
const compiler = driver.argv[0] ?? "clang";
|
|
2057
|
-
const compilerIdentity = await resolvedToolIdentity(compiler);
|
|
2058
|
-
if (compilerIdentity === null) {
|
|
2059
|
-
throw new Error("compiler unavailable before implicit linker identity was established");
|
|
2060
|
-
}
|
|
2061
|
-
let probe;
|
|
2062
|
-
{
|
|
2063
|
-
const probeDir = await mkdtemp(join(tmpdir(), "scriptc-linker-probe-"));
|
|
2064
|
-
try {
|
|
2065
|
-
const source = join(probeDir, "empty.c");
|
|
2066
|
-
const object = join(probeDir, "empty.o");
|
|
2067
|
-
const output = join(probeDir, process.platform === "win32" ? "empty.exe" : "empty");
|
|
2068
|
-
await writeFile(source, "int main(void) { return 0; }\n");
|
|
2069
|
-
const prefix = [...driver.argv.slice(1), ...driver.targetArgs];
|
|
2070
|
-
const [, linker] = await Promise.all([
|
|
2071
|
-
execFileAsync(compiler, [...prefix, "-std=c11", "-c", source, "-o", object], { cwd: probeDir }),
|
|
2072
|
-
execFileAsync(compiler, [...prefix, "-print-prog-name=ld"], { cwd: probeDir }),
|
|
2073
|
-
]);
|
|
2074
|
-
// Unlike the compile-only toolchain trace, this exposes options a
|
|
2075
|
-
// compiler wrapper/config injects only for link invocations (rpaths,
|
|
2076
|
-
// subsystem/stack settings, --defsym, and peers). Resolved input files
|
|
2077
|
-
// alone cannot represent those output-affecting flags.
|
|
2078
|
-
const driverInvocation = await execFileAsync(compiler, [...driver.argv.slice(1), ...invocationArgs, object, "-###", "-o", output], { cwd: probeDir, maxBuffer: 32 * 1024 * 1024 });
|
|
2079
|
-
// The dry run exposes absolute files a wrapper injects for this exact
|
|
2080
|
-
// build flavor. Its unresolved -l spellings are supplemented by the
|
|
2081
|
-
// real trace below, which runs with the same flavor flags but omits
|
|
2082
|
-
// not-yet-materialized scriptc-owned vendor prerequisites.
|
|
2083
|
-
const driverDependencies = await parseLinkTraceFiles(`${driverInvocation.stdout}\n${driverInvocation.stderr}`, probeDir, probeDir, true);
|
|
2084
|
-
let tracedDependencies = [];
|
|
2085
|
-
try {
|
|
2086
|
-
const trace = await execFileAsync(compiler, [...driver.argv.slice(1), ...traceArgs, object, "-Wl,-t", "-o", output], { cwd: probeDir, maxBuffer: 32 * 1024 * 1024 });
|
|
2087
|
-
tracedDependencies = await parseLinkTraceFiles(`${trace.stdout}\n${trace.stderr}`, probeDir, probeDir);
|
|
2088
|
-
}
|
|
2089
|
-
catch (error) {
|
|
2090
|
-
// Zig's COFF linker deliberately rejects GNU ld's `-t`, but `zig cc
|
|
2091
|
-
// -###` prints its fully resolved lld-link input list (CRT and every
|
|
2092
|
-
// import/compiler-runtime library as absolute cache paths). Other
|
|
2093
|
-
// drivers' dry runs commonly leave `-lc`/`-lSystem` unresolved, so
|
|
2094
|
-
// they must not use this fallback as a complete artifact identity.
|
|
2095
|
-
if (driver.argv[0] !== "zig" || driver.argv[1] !== "cc")
|
|
2096
|
-
throw error;
|
|
2097
|
-
}
|
|
2098
|
-
const dependencies = [...new Set([...driverDependencies, ...tracedDependencies])].sort();
|
|
2099
|
-
if (dependencies.length === 0) {
|
|
2100
|
-
throw new Error("linker trace reported no resolved input files");
|
|
2101
|
-
}
|
|
2102
|
-
const linkerSpelling = linker.stdout.trim();
|
|
2103
|
-
probe = {
|
|
2104
|
-
compilerIdentity,
|
|
2105
|
-
linkerInvocation: normalizedProbeInvocation(driverInvocation, probeDir),
|
|
2106
|
-
dependencies,
|
|
2107
|
-
dependencyFingerprint: await fingerprintDependencyFiles(dependencies),
|
|
2108
|
-
invocationPaths: await existingDriverTracePaths(`${driverInvocation.stdout}\n${driverInvocation.stderr}`, probeDir, probeDir),
|
|
2109
|
-
linker: {
|
|
2110
|
-
spelling: linkerSpelling,
|
|
2111
|
-
identity: linkerSpelling === ""
|
|
2112
|
-
? null
|
|
2113
|
-
: (await resolvedTool(linkerSpelling))?.cacheIdentity ?? null,
|
|
2114
|
-
path: linkerSpelling === ""
|
|
2115
|
-
? null
|
|
2116
|
-
: (await resolvedTool(linkerSpelling))?.canonicalPath ?? null,
|
|
2117
|
-
},
|
|
2118
|
-
};
|
|
2119
|
-
}
|
|
2120
|
-
finally {
|
|
2121
|
-
await rm(probeDir, { recursive: true, force: true }).catch(() => undefined);
|
|
2122
|
-
}
|
|
2123
|
-
}
|
|
2124
|
-
const linkerIdentity = probe.linker.spelling === ""
|
|
2125
|
-
? null
|
|
2126
|
-
: await resolvedToolIdentity(probe.linker.spelling);
|
|
2127
|
-
const fingerprint = createHash("sha256")
|
|
2128
|
-
.update("implicit-linker-v3\0")
|
|
2129
|
-
.update(environmentFingerprint)
|
|
2130
|
-
.update("\0")
|
|
2131
|
-
.update(probe.compilerIdentity)
|
|
2132
|
-
.update("\0")
|
|
2133
|
-
.update(probe.linkerInvocation)
|
|
2134
|
-
.update("\0")
|
|
2135
|
-
.update(driver.argv.join("\x1f"))
|
|
2136
|
-
.update("\0")
|
|
2137
|
-
.update(driver.targetArgs.join("\x1f"))
|
|
2138
|
-
.update("\0")
|
|
2139
|
-
.update(linkArgs.join("\x1f"))
|
|
2140
|
-
.update("\0")
|
|
2141
|
-
.update(invocationArgs.join("\x1f"))
|
|
2142
|
-
.update("\0")
|
|
2143
|
-
.update(traceArgs.join("\x1f"))
|
|
2144
|
-
.update("\0")
|
|
2145
|
-
.update(probe.dependencies.join("\x1f"))
|
|
2146
|
-
.update("\0")
|
|
2147
|
-
.update(probe.dependencyFingerprint)
|
|
2148
|
-
.update("\0")
|
|
2149
|
-
.update(probe.linker.spelling)
|
|
2150
|
-
.update("\0")
|
|
2151
|
-
.update(linkerIdentity ?? probe.linker.identity ?? "<unresolved>")
|
|
2152
|
-
.digest("hex");
|
|
2153
|
-
return rememberFingerprintDependencies(fingerprint, [
|
|
2154
|
-
...probe.dependencies,
|
|
2155
|
-
...probe.invocationPaths,
|
|
2156
|
-
...(probe.linker.path === null ? [] : [probe.linker.path]),
|
|
2157
|
-
], probe.dependencies, probe.dependencyFingerprint);
|
|
2158
|
-
}
|
|
2159
|
-
const stableImplicitLinkerMemos = new Map();
|
|
2160
|
-
function implicitLinkerFingerprint(driver, environmentFingerprint, linkArgs, effectiveInvocationArgs, traceInvocationArgs) {
|
|
2161
|
-
const invocationArgs = effectiveInvocationArgs ?? [...driver.targetArgs, ...linkArgs];
|
|
2162
|
-
const traceArgs = traceInvocationArgs ?? [...driver.targetArgs, ...linkArgs];
|
|
2163
|
-
const key = [
|
|
2164
|
-
environmentFingerprint,
|
|
2165
|
-
driver.argv.join("\x1f"),
|
|
2166
|
-
driver.target ?? "<native>",
|
|
2167
|
-
driver.targetArgs.join("\x1f"),
|
|
2168
|
-
linkArgs.join("\x1f"),
|
|
2169
|
-
invocationArgs.join("\x1f"),
|
|
2170
|
-
traceArgs.join("\x1f"),
|
|
2171
|
-
].join("\0");
|
|
2172
|
-
return stableTestMemo(stableImplicitLinkerMemos, key, () => implicitLinkerFingerprintFresh(driver, environmentFingerprint, linkArgs, effectiveInvocationArgs, traceInvocationArgs));
|
|
2173
|
-
}
|
|
2174
|
-
/** Resolve the exact files consumed by one compiler-driver link invocation.
|
|
2175
|
-
* Runtime packs reuse the native toolchain's strict dry-run plus real linker
|
|
2176
|
-
* trace so a PATH-selected Clang carries its own linker, SDK, compiler
|
|
2177
|
-
* runtime, and injected inputs into the executable cache proof. */
|
|
2178
|
-
export async function nativeLinkerDependencyPaths(linker, linkArgs) {
|
|
2179
|
-
const fingerprint = await implicitLinkerFingerprint({ argv: [linker], targetArgs: [], target: null }, toolchainEnvironmentFingerprint(), linkArgs);
|
|
2180
|
-
return fingerprintDependencyPaths(fingerprint);
|
|
2181
|
-
}
|
|
2182
|
-
let ccacheMemo = null;
|
|
2183
|
-
/** Reset process observations whose validity is bounded to one public native
|
|
2184
|
-
* build. A long-lived caller may change PATH or install/remove ccache between
|
|
2185
|
-
* invocations; the next build must probe that environment again. */
|
|
2186
|
-
export function clearCcCaches() {
|
|
2187
|
-
ccacheMemo = null;
|
|
2188
|
-
}
|
|
2189
|
-
function ccacheAvailable() {
|
|
2190
|
-
ccacheMemo ??= execFileAsync("ccache", ["--version"]).then(() => true, () => false);
|
|
2191
|
-
return ccacheMemo;
|
|
2192
|
-
}
|
|
2193
|
-
/** Content hash of every owned native source/header plus this backend's build
|
|
2194
|
-
* recipe implementation. It keys complete artifacts, runtime objects, and the
|
|
2195
|
-
* separately built vendor prerequisites, so two installed scriptc versions or
|
|
2196
|
-
* worktrees can share a user cache without exchanging outputs produced from
|
|
2197
|
-
* different vendored bytes or compile/archive recipes. Recursive enumeration
|
|
2198
|
-
* also catches a newly added nested header that begins shadowing a system
|
|
2199
|
-
* include, while content hashing catches same-size timestamp-preserving edits. */
|
|
2200
|
-
async function runtimeFingerprintFresh(rtDir) {
|
|
2201
|
-
const groups = await runtimeFingerprintInputGroups(rtDir);
|
|
2202
|
-
const h = createHash("sha256")
|
|
2203
|
-
.update("native-owned-inputs-v2\0")
|
|
2204
|
-
.update(QJS_COMMIT).update(MBEDTLS_VERSION).update(ZLIB_VERSION)
|
|
2205
|
-
.update("\0backend-recipe\0");
|
|
2206
|
-
for (const path of NATIVE_RECIPE_IMPLEMENTATION_PATHS) {
|
|
2207
|
-
h.update(basename(path)).update("\0").update(await readFile(path)).update("\0");
|
|
2208
|
-
}
|
|
2209
|
-
for (const group of groups) {
|
|
2210
|
-
for (const n of group.names) {
|
|
2211
|
-
h.update(group.label).update("/").update(n).update("\0").update(await readFile(join(group.dir, n))).update("\0");
|
|
2212
|
-
}
|
|
2213
|
-
}
|
|
2214
|
-
return h.digest("hex");
|
|
2215
|
-
}
|
|
2216
|
-
async function runtimeFingerprintInputGroups(rtDir) {
|
|
2217
|
-
return Promise.all([
|
|
2218
|
-
{ label: "runtime", dir: rtDir },
|
|
2219
|
-
{ label: "ryu", dir: join(rtDir, "..", "vendor", "ryu") },
|
|
2220
|
-
{ label: "quickjs-ng", dir: join(rtDir, "..", "vendor", "quickjs-ng") },
|
|
2221
|
-
{ label: "mbedtls", dir: join(rtDir, "..", "vendor", "mbedtls") },
|
|
2222
|
-
{ label: "zlib", dir: join(rtDir, "..", "vendor", "zlib") },
|
|
2223
|
-
{ label: "curl", dir: join(rtDir, "..", "vendor", "curl") },
|
|
2224
|
-
].map(async (group) => {
|
|
2225
|
-
const names = (await nativeSourceFiles(group.dir, true))
|
|
2226
|
-
.map((path) => relative(group.dir, path))
|
|
2227
|
-
.sort();
|
|
2228
|
-
return { ...group, names };
|
|
2229
|
-
}));
|
|
2230
|
-
}
|
|
2231
|
-
async function runtimeFingerprintInputPaths(rtDir) {
|
|
2232
|
-
return [
|
|
2233
|
-
...(await runtimeFingerprintInputGroups(rtDir)).flatMap((group) => group.names.map((name) => join(group.dir, name))),
|
|
2234
|
-
...NATIVE_RECIPE_IMPLEMENTATION_PATHS,
|
|
2235
|
-
];
|
|
2236
|
-
}
|
|
2237
|
-
const stableRuntimeFingerprintMemos = new Map();
|
|
2238
|
-
export function runtimeFingerprint(rtDir) {
|
|
2239
|
-
const key = resolve(rtDir);
|
|
2240
|
-
return stableTestMemo(stableRuntimeFingerprintMemos, key, () => runtimeFingerprintFresh(rtDir));
|
|
2241
|
-
}
|
|
2242
|
-
const { vendorEngineDir, vendorTlsDir, vendorCurlDir, vendorBuildCacheRoot, vendorCacheTargetFlavor, vendorCacheBuildIdentity, currentVendorCacheBuildIdentity, engineArchivePath, stageVendorInputs, ensureEngineArchive, lreObjectPaths, ensureLreObjects, vendorZlibDir, zlibObjectPaths, ensureZlibObjects, curlStubDirPath, ensureCurlStub, tlsArchivePath, ensureTlsArchive, QJS_ENGINE_SOURCES, LRE_SOURCES, ZLIB_SOURCES, } = createVendorArchives({
|
|
2243
|
-
runtimeSrcDir,
|
|
2244
|
-
targetPlatform,
|
|
2245
|
-
isZigDriver,
|
|
2246
|
-
resolvedToolIdentity,
|
|
2247
|
-
runtimeFingerprint,
|
|
2248
|
-
});
|
|
2249
|
-
export { vendorCacheBuildIdentity, vendorCacheTargetFlavor };
|
|
2250
|
-
class CacheInputsChangedError extends Error {
|
|
2251
|
-
constructor() {
|
|
2252
|
-
super("runtime inputs changed while populating the native object cache");
|
|
2253
|
-
this.name = "CacheInputsChangedError";
|
|
2254
|
-
}
|
|
2255
|
-
}
|
|
2256
|
-
async function directoryTreeDigest(root, excludedPaths = []) {
|
|
2257
|
-
const hash = createHash("sha256").update("native-dependency-tree-v1\0");
|
|
2258
|
-
const visited = new Set();
|
|
2259
|
-
const excluded = excludedPaths.map((path) => resolve(path));
|
|
2260
|
-
const walk = async (directory, relative) => {
|
|
2261
|
-
const canonical = await realpath(directory);
|
|
2262
|
-
if (visited.has(canonical)) {
|
|
2263
|
-
hash.update(relative).update("\0cycle\0").update(canonical).update("\0");
|
|
2264
|
-
return;
|
|
2265
|
-
}
|
|
2266
|
-
visited.add(canonical);
|
|
2267
|
-
const entries = await readdir(directory, { withFileTypes: true });
|
|
2268
|
-
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
2269
|
-
for (const entry of entries) {
|
|
2270
|
-
const path = join(directory, entry.name);
|
|
2271
|
-
const absolute = resolve(path);
|
|
2272
|
-
if (excluded.some((candidate) => absolute === candidate ||
|
|
2273
|
-
absolute.startsWith(`${candidate}/`) ||
|
|
2274
|
-
absolute.startsWith(`${candidate}\\`)))
|
|
2275
|
-
continue;
|
|
2276
|
-
const child = relative === "" ? entry.name : `${relative}/${entry.name}`;
|
|
2277
|
-
const info = await lstat(path);
|
|
2278
|
-
const kind = info.isDirectory()
|
|
2279
|
-
? "directory"
|
|
2280
|
-
: info.isFile()
|
|
2281
|
-
? "file"
|
|
2282
|
-
: info.isSymbolicLink()
|
|
2283
|
-
? "symlink"
|
|
2284
|
-
: "other";
|
|
2285
|
-
hash.update(child).update("\0").update(kind).update("\0");
|
|
2286
|
-
if (kind === "symlink") {
|
|
2287
|
-
const target = await realpath(path).catch(() => "<missing>");
|
|
2288
|
-
hash.update(target).update("\0");
|
|
2289
|
-
const targetInfo = await stat(path).catch(() => null);
|
|
2290
|
-
if (targetInfo?.isDirectory())
|
|
2291
|
-
await walk(path, child);
|
|
2292
|
-
}
|
|
2293
|
-
if (kind === "directory")
|
|
2294
|
-
await walk(path, child);
|
|
2295
|
-
}
|
|
2296
|
-
visited.delete(canonical);
|
|
2297
|
-
};
|
|
2298
|
-
await walk(root, "");
|
|
2299
|
-
return hash.digest("hex");
|
|
2300
|
-
}
|
|
2301
|
-
function localDependencyKind(info) {
|
|
2302
|
-
return info.isFile()
|
|
2303
|
-
? "file"
|
|
2304
|
-
: info.isDirectory()
|
|
2305
|
-
? "directory"
|
|
2306
|
-
: info.isSymbolicLink()
|
|
2307
|
-
? "symlink"
|
|
2308
|
-
: null;
|
|
2309
|
-
}
|
|
2310
|
-
async function snapshotLocalArtifactDependency(path, treeExclusions = null) {
|
|
2311
|
-
const info = await lstat(path);
|
|
2312
|
-
const kind = localDependencyKind(info);
|
|
2313
|
-
if (kind === null)
|
|
2314
|
-
throw new Error(`unsupported local artifact dependency: ${path}`);
|
|
2315
|
-
const dependency = {
|
|
2316
|
-
path,
|
|
2317
|
-
kind,
|
|
2318
|
-
dev: info.dev,
|
|
2319
|
-
ino: info.ino,
|
|
2320
|
-
size: info.size,
|
|
2321
|
-
mtimeMs: info.mtimeMs,
|
|
2322
|
-
ctimeMs: info.ctimeMs,
|
|
2323
|
-
};
|
|
2324
|
-
if (kind === "directory" && treeExclusions !== null) {
|
|
2325
|
-
dependency.treeExclusions = [...new Set(treeExclusions.map((entry) => resolve(entry)))].sort();
|
|
2326
|
-
dependency.treeDigest = await directoryTreeDigest(path, dependency.treeExclusions);
|
|
2327
|
-
}
|
|
2328
|
-
if (kind === "symlink") {
|
|
2329
|
-
const targetPath = await realpath(path);
|
|
2330
|
-
const target = await stat(path);
|
|
2331
|
-
const targetKind = target.isFile() ? "file" : target.isDirectory() ? "directory" : null;
|
|
2332
|
-
if (targetKind === null)
|
|
2333
|
-
throw new Error(`unsupported symlink target dependency: ${path}`);
|
|
2334
|
-
dependency.targetPath = targetPath;
|
|
2335
|
-
dependency.targetKind = targetKind;
|
|
2336
|
-
dependency.targetDev = target.dev;
|
|
2337
|
-
dependency.targetIno = target.ino;
|
|
2338
|
-
dependency.targetSize = target.size;
|
|
2339
|
-
dependency.targetMtimeMs = target.mtimeMs;
|
|
2340
|
-
dependency.targetCtimeMs = target.ctimeMs;
|
|
2341
|
-
if (targetKind === "directory" && treeExclusions !== null) {
|
|
2342
|
-
dependency.treeExclusions = [...new Set(treeExclusions.map((entry) => resolve(entry)))].sort();
|
|
2343
|
-
dependency.treeDigest = await directoryTreeDigest(path, dependency.treeExclusions);
|
|
2344
|
-
}
|
|
2345
|
-
}
|
|
2346
|
-
return dependency;
|
|
2347
|
-
}
|
|
2348
|
-
async function snapshotLocalArtifactDependencies(dependencyPaths, recursiveDirectories = [], recursiveExclusions = []) {
|
|
2349
|
-
const recursive = new Set(recursiveDirectories.map((path) => resolve(path)));
|
|
2350
|
-
return Promise.all([...new Set(dependencyPaths)].sort().map((path) => snapshotLocalArtifactDependency(path, recursive.has(resolve(path)) ? recursiveExclusions : null)));
|
|
2351
|
-
}
|
|
2352
|
-
/** Capture exact filesystem identities for inputs produced outside the C
|
|
2353
|
-
* toolchain but consumed by its cache proofs. Callers carry this snapshot
|
|
2354
|
-
* forward so later stages can prove the same inputs remained installed. */
|
|
2355
|
-
export async function snapshotNativeArtifactDependencies(dependencyPaths) {
|
|
2356
|
-
return snapshotLocalArtifactDependencies(dependencyPaths);
|
|
2357
|
-
}
|
|
2358
|
-
export async function nativeArtifactDependenciesStillMatch(dependencies) {
|
|
2359
|
-
if (!dependencies.every((dependency) => dependency !== null && typeof dependency === "object" &&
|
|
2360
|
-
typeof dependency.path === "string" &&
|
|
2361
|
-
(dependency.kind === "file" || dependency.kind === "directory" || dependency.kind === "symlink") &&
|
|
2362
|
-
typeof dependency.dev === "number" && typeof dependency.ino === "number" &&
|
|
2363
|
-
typeof dependency.size === "number" && typeof dependency.mtimeMs === "number" &&
|
|
2364
|
-
typeof dependency.ctimeMs === "number" &&
|
|
2365
|
-
(dependency.treeDigest === undefined || typeof dependency.treeDigest === "string") &&
|
|
2366
|
-
(dependency.treeExclusions === undefined || (Array.isArray(dependency.treeExclusions) &&
|
|
2367
|
-
dependency.treeExclusions.every((path) => typeof path === "string"))) &&
|
|
2368
|
-
(dependency.kind !== "symlink" || (typeof dependency.targetPath === "string" &&
|
|
2369
|
-
(dependency.targetKind === "file" || dependency.targetKind === "directory") &&
|
|
2370
|
-
typeof dependency.targetDev === "number" && typeof dependency.targetIno === "number" &&
|
|
2371
|
-
typeof dependency.targetSize === "number" && typeof dependency.targetMtimeMs === "number" &&
|
|
2372
|
-
typeof dependency.targetCtimeMs === "number"))))
|
|
2373
|
-
return false;
|
|
2374
|
-
return (await Promise.all(dependencies.map(async (dependency) => {
|
|
2375
|
-
const current = await snapshotLocalArtifactDependency(dependency.path, dependency.treeDigest === undefined ? null : dependency.treeExclusions ?? []).catch(() => null);
|
|
2376
|
-
return current !== null && JSON.stringify(current) === JSON.stringify(dependency);
|
|
2377
|
-
}))).every(Boolean);
|
|
2378
|
-
}
|
|
2379
|
-
function nativeMetadataStampPath(root, key) {
|
|
2380
|
-
return join(root, "meta", createHash("sha256").update(key).digest("hex"));
|
|
2381
|
-
}
|
|
2382
|
-
function nativeMetadataStampIntegrity(stamp) {
|
|
2383
|
-
return createHash("sha256")
|
|
2384
|
-
.update("native-metadata-stamp-v2\0")
|
|
2385
|
-
.update(JSON.stringify(stamp))
|
|
2386
|
-
.digest("hex");
|
|
2387
|
-
}
|
|
2388
|
-
async function readNativeMetadataStamp(root, key) {
|
|
2389
|
-
try {
|
|
2390
|
-
const stamp = JSON.parse(await readFile(nativeMetadataStampPath(root, key), "utf8"));
|
|
2391
|
-
if (stamp.version !== 2 ||
|
|
2392
|
-
stamp.key !== key ||
|
|
2393
|
-
stamp.values === null ||
|
|
2394
|
-
typeof stamp.values !== "object" ||
|
|
2395
|
-
!Array.isArray(stamp.dependencies) ||
|
|
2396
|
-
!/^[0-9a-f]{64}$/.test(stamp.integrity) ||
|
|
2397
|
-
nativeMetadataStampIntegrity({
|
|
2398
|
-
version: stamp.version,
|
|
2399
|
-
key: stamp.key,
|
|
2400
|
-
values: stamp.values,
|
|
2401
|
-
dependencies: stamp.dependencies,
|
|
2402
|
-
}) !== stamp.integrity ||
|
|
2403
|
-
!(await nativeArtifactDependenciesStillMatch(stamp.dependencies))) {
|
|
2404
|
-
return null;
|
|
2405
|
-
}
|
|
2406
|
-
return stamp;
|
|
2407
|
-
}
|
|
2408
|
-
catch {
|
|
2409
|
-
return null;
|
|
2410
|
-
}
|
|
2411
|
-
}
|
|
2412
|
-
async function publishNativeMetadataStamp(root, key, values, dependencyPaths, fingerprints = []) {
|
|
2413
|
-
const destination = nativeMetadataStampPath(root, key);
|
|
2414
|
-
await mkdir(dirname(destination), { recursive: true });
|
|
2415
|
-
const dependencies = await snapshotLocalArtifactDependencies(dependencyPaths);
|
|
2416
|
-
if (!(await fingerprintDependenciesStillMatch(fingerprints))) {
|
|
2417
|
-
throw new CacheInputsChangedError();
|
|
2418
|
-
}
|
|
2419
|
-
const unsigned = { version: 2, key, values, dependencies };
|
|
2420
|
-
const stamp = {
|
|
2421
|
-
...unsigned,
|
|
2422
|
-
integrity: nativeMetadataStampIntegrity(unsigned),
|
|
2423
|
-
};
|
|
2424
|
-
const tmp = `${destination}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
2425
|
-
try {
|
|
2426
|
-
await writeFile(tmp, `${JSON.stringify(stamp)}\n`, { mode: 0o600 });
|
|
2427
|
-
await rename(tmp, destination);
|
|
2428
|
-
}
|
|
2429
|
-
finally {
|
|
2430
|
-
await rm(tmp, { force: true }).catch(() => undefined);
|
|
2431
|
-
}
|
|
2432
|
-
return stamp;
|
|
2433
|
-
}
|
|
2434
|
-
function nativeMetadataKey(kind, parts) {
|
|
2435
|
-
const hash = createHash("sha256").update(`native-metadata-${kind}-v2\0`);
|
|
2436
|
-
for (const part of parts) {
|
|
2437
|
-
hash.update(typeof part === "string" ? part : part.join("\x1f")).update("\0");
|
|
2438
|
-
}
|
|
2439
|
-
return `${kind}-${hash.digest("hex")}`;
|
|
2440
|
-
}
|
|
2441
|
-
/** The caller-visible output is itself the cheapest safe cache tier. Once a
|
|
2442
|
-
* generated TU has produced this exact binary, an unchanged rebuild need not
|
|
2443
|
-
* rediscover every SDK header and linker input merely to copy equivalent bytes
|
|
2444
|
-
* back onto the same path. This tier is deliberately narrower than the CAS:
|
|
2445
|
-
* only frontend-generated programs with no caller-owned native inputs opt in.
|
|
2446
|
-
* The generated TU bytes, every scriptc runtime source, the selected direct
|
|
2447
|
-
* compiler inode, target/options/environment, and the output path all join the
|
|
2448
|
-
* key. A digest rejects a modified/truncated output before the no-op hit. */
|
|
2449
|
-
function localArtifactIdentity(opts, driver, environmentFingerprint, compilerIdentity, runtimeHash, programBytes, programShardMerge) {
|
|
2450
|
-
const normalizedOptions = Object.fromEntries(Object.entries(opts)
|
|
2451
|
-
// Shard source can be tens of megabytes. Hash it incrementally below
|
|
2452
|
-
// instead of materializing a second giant JSON string solely for this
|
|
2453
|
-
// output-local fast-path identity.
|
|
2454
|
-
.filter(([key, value]) => value !== undefined && key !== "programShards" && key !== "programPublicSymbols")
|
|
2455
|
-
.sort(([a], [b]) => a.localeCompare(b)));
|
|
2456
|
-
const executableSectionFlags = executableSectionEliminationFlags(targetPlatform(driver));
|
|
2457
|
-
const executableOptimizationFlags = executableOptimizationLinkerArgs(targetPlatform(driver), opts.optimization ?? "release");
|
|
2458
|
-
const hash = createHash("sha256")
|
|
2459
|
-
.update("local-artifact-v2\0")
|
|
2460
|
-
.update(cacheTargetIdentity(driver)).update("\0")
|
|
2461
|
-
.update(environmentFingerprint).update("\0")
|
|
2462
|
-
.update(compilerIdentity).update("\0")
|
|
2463
|
-
.update(runtimeHash).update("\0")
|
|
2464
|
-
.update(driver.argv.join("\x1f")).update("\0")
|
|
2465
|
-
.update(driver.targetArgs.join("\x1f")).update("\0")
|
|
2466
|
-
.update(driver.linkArgs.join("\x1f")).update("\0")
|
|
2467
|
-
.update(executableSectionFlags.compile.join("\x1f")).update("\0")
|
|
2468
|
-
.update(executableSectionFlags.link.join("\x1f")).update("\0");
|
|
2469
|
-
// Only release WASI currently adds an optimization-specific link flag.
|
|
2470
|
-
// Salt that identity so a pre-fix DWARF-bearing output cannot hit, without
|
|
2471
|
-
// invalidating unchanged native and dev artifacts on upgrade.
|
|
2472
|
-
if (executableOptimizationFlags.length > 0) {
|
|
2473
|
-
hash.update("optimization-linker-flags\0")
|
|
2474
|
-
.update(executableOptimizationFlags.join("\x1f")).update("\0");
|
|
2475
|
-
}
|
|
2476
|
-
if (opts.sanitize && opts.optimization === "dev")
|
|
2477
|
-
hash.update("sanitized-runtime-o1-v1\0");
|
|
2478
|
-
if (programShardMerge !== null) {
|
|
2479
|
-
updateProgramShardCacheIdentity(hash, opts.programShards, opts.programPublicSymbols, programShardMerge);
|
|
2480
|
-
}
|
|
2481
|
-
return hash
|
|
2482
|
-
.update(process.env["SCRIPTC_FETCH_CURL"] === "1" ? "fetch-curl" : "fetch-native").update("\0")
|
|
2483
|
-
.update(JSON.stringify(normalizedOptions)).update("\0")
|
|
2484
|
-
.update(resolve(opts.cPath)).update("\0")
|
|
2485
|
-
.update(resolve(opts.outPath)).update("\0")
|
|
2486
|
-
.update(programBytes)
|
|
2487
|
-
.digest("hex");
|
|
2488
|
-
}
|
|
2489
|
-
function localArtifactStampPath(root, outPath) {
|
|
2490
|
-
const outputKey = createHash("sha256").update(resolve(outPath)).digest("hex");
|
|
2491
|
-
return join(root, "local", outputKey);
|
|
2492
|
-
}
|
|
2493
|
-
function localArtifactStampIntegrity(stamp) {
|
|
2494
|
-
return createHash("sha256")
|
|
2495
|
-
.update("local-artifact-stamp-v2\0")
|
|
2496
|
-
.update(JSON.stringify(stamp))
|
|
2497
|
-
.digest("hex");
|
|
2498
|
-
}
|
|
2499
|
-
async function localArtifactHit(stampPath, outPath, key, darwinDebugSymbols = false) {
|
|
2500
|
-
try {
|
|
2501
|
-
const stamp = JSON.parse(await readFile(stampPath, "utf8"));
|
|
2502
|
-
const output = await lstat(outPath);
|
|
2503
|
-
const expectedMode = 0o777 & ~process.umask();
|
|
2504
|
-
if (stamp.version !== 2 ||
|
|
2505
|
-
stamp.key !== key ||
|
|
2506
|
-
!/^[0-9a-f]{64}$/.test(stamp.digest ?? "") ||
|
|
2507
|
-
(darwinDebugSymbols !== (stamp.debugSymbolsDigest !== undefined)) ||
|
|
2508
|
-
(stamp.debugSymbolsDigest !== undefined && !/^[0-9a-f]{64}$/.test(stamp.debugSymbolsDigest)) ||
|
|
2509
|
-
!Array.isArray(stamp.dependencies) ||
|
|
2510
|
-
!/^[0-9a-f]{64}$/.test(stamp.integrity ?? "") ||
|
|
2511
|
-
localArtifactStampIntegrity({
|
|
2512
|
-
version: stamp.version,
|
|
2513
|
-
key: stamp.key,
|
|
2514
|
-
digest: stamp.digest,
|
|
2515
|
-
dependencies: stamp.dependencies,
|
|
2516
|
-
...(stamp.debugSymbolsDigest === undefined ? {} : { debugSymbolsDigest: stamp.debugSymbolsDigest }),
|
|
2517
|
-
}) !== stamp.integrity ||
|
|
2518
|
-
!output.isFile() ||
|
|
2519
|
-
(output.mode & 0o777) !== expectedMode ||
|
|
2520
|
-
stamp.dependencies.some((dependency) => dependency === null ||
|
|
2521
|
-
typeof dependency !== "object" ||
|
|
2522
|
-
typeof dependency.path !== "string" ||
|
|
2523
|
-
dependency.kind !== "file" &&
|
|
2524
|
-
dependency.kind !== "directory" &&
|
|
2525
|
-
dependency.kind !== "symlink" ||
|
|
2526
|
-
typeof dependency.dev !== "number" ||
|
|
2527
|
-
typeof dependency.ino !== "number" ||
|
|
2528
|
-
typeof dependency.size !== "number" ||
|
|
2529
|
-
typeof dependency.mtimeMs !== "number" ||
|
|
2530
|
-
typeof dependency.ctimeMs !== "number" ||
|
|
2531
|
-
dependency.treeDigest !== undefined && typeof dependency.treeDigest !== "string" ||
|
|
2532
|
-
dependency.kind === "symlink" && (typeof dependency.targetPath !== "string" ||
|
|
2533
|
-
dependency.targetKind !== "file" && dependency.targetKind !== "directory" ||
|
|
2534
|
-
typeof dependency.targetDev !== "number" ||
|
|
2535
|
-
typeof dependency.targetIno !== "number" ||
|
|
2536
|
-
typeof dependency.targetSize !== "number" ||
|
|
2537
|
-
typeof dependency.targetMtimeMs !== "number" ||
|
|
2538
|
-
typeof dependency.targetCtimeMs !== "number")) ||
|
|
2539
|
-
!(await nativeArtifactDependenciesStillMatch(stamp.dependencies)) ||
|
|
2540
|
-
await fileDigest(outPath) !== stamp.digest ||
|
|
2541
|
-
darwinDebugSymbols && createHash("sha256").update(await readDarwinDebugSymbols(outPath)).digest("hex") !== stamp.debugSymbolsDigest) {
|
|
2542
|
-
return null;
|
|
2543
|
-
}
|
|
2544
|
-
const now = new Date();
|
|
2545
|
-
await utimes(stampPath, now, now).catch(() => undefined);
|
|
2546
|
-
return stamp;
|
|
2547
|
-
}
|
|
2548
|
-
catch {
|
|
2549
|
-
return null;
|
|
2550
|
-
}
|
|
2551
|
-
}
|
|
2552
|
-
async function publishLocalArtifactStamp(stampPath, outPath, key, dependencyPaths, recursiveDirectories = [], recursiveExclusions = [], darwinDebugSymbols = false) {
|
|
2553
|
-
await mkdir(dirname(stampPath), { recursive: true });
|
|
2554
|
-
const tmp = `${stampPath}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
2555
|
-
try {
|
|
2556
|
-
const dependencies = await snapshotLocalArtifactDependencies(dependencyPaths, recursiveDirectories, recursiveExclusions);
|
|
2557
|
-
const unsigned = {
|
|
2558
|
-
version: 2,
|
|
2559
|
-
key,
|
|
2560
|
-
digest: await fileDigest(outPath),
|
|
2561
|
-
dependencies,
|
|
2562
|
-
...(darwinDebugSymbols
|
|
2563
|
-
? { debugSymbolsDigest: createHash("sha256").update(await readDarwinDebugSymbols(outPath)).digest("hex") }
|
|
2564
|
-
: {}),
|
|
2565
|
-
};
|
|
2566
|
-
const stamp = {
|
|
2567
|
-
...unsigned,
|
|
2568
|
-
integrity: localArtifactStampIntegrity(unsigned),
|
|
2569
|
-
};
|
|
2570
|
-
await writeFile(tmp, `${JSON.stringify(stamp)}\n`, { mode: 0o600 });
|
|
2571
|
-
await rename(tmp, stampPath);
|
|
2572
|
-
return stamp;
|
|
2573
|
-
}
|
|
2574
|
-
finally {
|
|
2575
|
-
await rm(tmp, { force: true }).catch(() => undefined);
|
|
2576
|
-
}
|
|
2577
|
-
}
|
|
2578
|
-
/** The cached .o set for one flag flavor, compiled on first need. Concurrent
|
|
2579
|
-
* first builds (parallel test workers on a cold cache) may duplicate work;
|
|
2580
|
-
* per-file atomic renames make every winner equivalent. Publication is held
|
|
2581
|
-
* until verifyInputs confirms that the source/header fingerprint used by the
|
|
2582
|
-
* key still describes the bytes clang just read. */
|
|
2583
|
-
async function ensureRuntimeObjects(root, ccArgv, cflags, sources, keyPrefix, verifyInputs, protectedPaths) {
|
|
2584
|
-
const setKey = createHash("sha256")
|
|
2585
|
-
.update(keyPrefix)
|
|
2586
|
-
.update(cflags.join("\x1f"))
|
|
2587
|
-
.digest("hex")
|
|
2588
|
-
.slice(0, 24);
|
|
2589
|
-
const objDir = join(root, "obj", setKey);
|
|
2590
|
-
const objOf = (src) => join(objDir, `${basename(src, ".c")}.o`);
|
|
2591
|
-
if (protectedPaths !== undefined) {
|
|
2592
|
-
for (const source of sources) {
|
|
2593
|
-
const object = objOf(source);
|
|
2594
|
-
protectedPaths.add(object);
|
|
2595
|
-
protectedPaths.add(cacheDigestPath(object));
|
|
2596
|
-
}
|
|
2597
|
-
}
|
|
2598
|
-
const present = await Promise.all(sources.map((s) => validCachedFile(objOf(s))));
|
|
2599
|
-
const missing = sources.filter((_, i) => !present[i]);
|
|
2600
|
-
if (missing.length > 0) {
|
|
2601
|
-
// ccache wraps only the default clang driver — multi-word drivers
|
|
2602
|
-
// (`zig cc`) run bare; their object sets are keyed apart anyway.
|
|
2603
|
-
const useCcache = process.env["SCRIPTC_TEST_DISABLE_CCACHE"] !== "1" &&
|
|
2604
|
-
ccArgv.length === 1 &&
|
|
2605
|
-
ccArgv[0] === "clang" &&
|
|
2606
|
-
(await ccacheAvailable());
|
|
2607
|
-
await mkdir(objDir, { recursive: true });
|
|
2608
|
-
const tmpDir = await mkdtemp(join(tmpdir(), "scriptc-cache-obj-"));
|
|
2609
|
-
try {
|
|
2610
|
-
const compiled = new Map();
|
|
2611
|
-
// Modest parallelism: a flavor's objects build once, but several cold
|
|
2612
|
-
// workers can race here — keep each build's CPU footprint small.
|
|
2613
|
-
const width = 4;
|
|
2614
|
-
for (let i = 0; i < missing.length; i += width) {
|
|
2615
|
-
await Promise.all(missing.slice(i, i + width).map(async (src) => {
|
|
2616
|
-
const tmpObj = join(tmpDir, `${basename(src, ".c")}.o`);
|
|
2617
|
-
const argv = [...(useCcache ? ["ccache"] : []), ...ccArgv, ...cflags, "-c", src, "-o", tmpObj];
|
|
2618
|
-
await execFileAsync(argv[0] ?? "clang", argv.slice(1), useCcache
|
|
2619
|
-
? {
|
|
2620
|
-
// ccache direct mode remembers only the headers selected by
|
|
2621
|
-
// its previous manifest and can miss a newly created,
|
|
2622
|
-
// higher-priority header. The scriptc object-set key already
|
|
2623
|
-
// includes the recursive runtime namespace fingerprint, so
|
|
2624
|
-
// carry it into ccache's own keyspace as well.
|
|
2625
|
-
env: {
|
|
2626
|
-
...process.env,
|
|
2627
|
-
CCACHE_NAMESPACE: [
|
|
2628
|
-
process.env["CCACHE_NAMESPACE"],
|
|
2629
|
-
`scriptc-${setKey}`,
|
|
2630
|
-
].filter((value) => value !== undefined && value !== "").join(":"),
|
|
2631
|
-
},
|
|
2632
|
-
}
|
|
2633
|
-
: undefined);
|
|
2634
|
-
compiled.set(src, tmpObj);
|
|
2635
|
-
}));
|
|
2636
|
-
}
|
|
2637
|
-
// The fingerprint was computed before these subprocesses started. Do
|
|
2638
|
-
// not place their outputs under that key if a checkout/package update
|
|
2639
|
-
// changed any runtime source or included header while clang was reading.
|
|
2640
|
-
if (!(await verifyInputs()))
|
|
2641
|
-
throw new CacheInputsChangedError();
|
|
2642
|
-
for (const [src, built] of compiled) {
|
|
2643
|
-
const destination = objOf(src);
|
|
2644
|
-
await publishCachedFile(built, destination);
|
|
2645
|
-
}
|
|
2646
|
-
}
|
|
2647
|
-
finally {
|
|
2648
|
-
await rm(tmpDir, { recursive: true, force: true });
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
if (!(await verifyInputs()))
|
|
2652
|
-
throw new CacheInputsChangedError();
|
|
2653
|
-
const objects = new Map(sources.map((s) => [s, objOf(s)]));
|
|
2654
|
-
if (!(await Promise.all([...objects.values()].map(validCachedFile))).every(Boolean)) {
|
|
2655
|
-
throw new Error("native object cache integrity check failed");
|
|
2656
|
-
}
|
|
2657
|
-
return objects;
|
|
2658
|
-
}
|
|
2659
|
-
/** Give one active link/archive operation private names for its cached
|
|
2660
|
-
* runtime objects. A hard link keeps the inode alive if another process's LRU
|
|
2661
|
-
* sweep unlinks the cache entry; filesystems that cannot hard-link across the
|
|
2662
|
-
* cache/tmp boundary fall back to a copy. If eviction wins before staging,
|
|
2663
|
-
* the caller catches the read failure and performs a fully fresh compile. */
|
|
2664
|
-
export async function stageRuntimeObjects(objects, stageDir) {
|
|
2665
|
-
await mkdir(stageDir, { recursive: true });
|
|
2666
|
-
const now = new Date();
|
|
2667
|
-
const staged = await Promise.all([...objects].map(async ([source, object]) => {
|
|
2668
|
-
const destination = join(stageDir, basename(object));
|
|
2669
|
-
try {
|
|
2670
|
-
await link(object, destination);
|
|
2671
|
-
}
|
|
2672
|
-
catch {
|
|
2673
|
-
await copyFile(object, destination);
|
|
2674
|
-
}
|
|
2675
|
-
// Object files participate in the same mtime-based LRU as complete
|
|
2676
|
-
// artifacts. A successful stage is a cache read, so promote the source
|
|
2677
|
-
// name best-effort (it may have raced an eviction after the hard link).
|
|
2678
|
-
await utimes(object, now, now).catch(() => undefined);
|
|
2679
|
-
return [source, destination];
|
|
2680
|
-
}));
|
|
2681
|
-
return new Map(staged);
|
|
2682
|
-
}
|
|
2683
|
-
/** Compiles one C program together with the runtime sources.
|
|
2684
|
-
* With caching disabled, the runtime (a dozen small files) is recompiled on
|
|
2685
|
-
* every build — no cached-archive staleness bugs. Sanitized dev builds compile
|
|
2686
|
-
* the program separately so its O0 setting does not slow the O1 runtime.
|
|
2687
|
-
* Other builds use one clang invocation. --dynamic additionally compiles
|
|
2688
|
-
* scr_island.c under SCR_DYNAMIC and links the cached engine archive (built
|
|
2689
|
-
* lazily, see above); regex-using programs additionally compile scr_regex.c
|
|
2690
|
-
* and link libregexp (the cached objects, or the archive's own copy under
|
|
2691
|
-
* --dynamic). Executable links use the target's section-elimination recipe
|
|
2692
|
-
* independently of those feature gates.
|
|
2693
|
-
*
|
|
2694
|
-
* With a caller-supplied dependency identity and an enabled cache root,
|
|
2695
|
-
* unchanged programs skip payload code generation/linking via the binary
|
|
2696
|
-
* cache after lightweight metadata probes, and misses link the program's own
|
|
2697
|
-
* TU against cached per-flavor runtime objects. */
|
|
2698
|
-
async function compileCInternal(opts, cacheWarmOnly, cacheWarmPaths) {
|
|
2699
|
-
const rtDir = runtimeSrcDir();
|
|
2700
|
-
const sanitize = opts.sanitize ?? false;
|
|
2701
|
-
const optimization = opts.optimization ?? "release";
|
|
2702
|
-
const dynamic = opts.dynamic ?? false;
|
|
2703
|
-
const regex = opts.regex ?? false;
|
|
2704
|
-
// fetch's implementation switch: the default is the NATIVE bridge
|
|
2705
|
-
// (scr_fetch.c over scr_net + scr_tls + scr_http's client parser +
|
|
2706
|
-
// zlib — no libcurl anywhere), which implies the socket units into the
|
|
2707
|
-
// link. SCRIPTC_FETCH_CURL=1 keeps the retired curl reference
|
|
2708
|
-
// (scr_fetch_curl.c + system libcurl / the linux soname stub)
|
|
2709
|
-
// compilable for one release as the flip's reference.
|
|
2710
|
-
const fetchOn = opts.fetch ?? false;
|
|
2711
|
-
// The retired curl bridge has only a SCR_DYNAMIC implementation.
|
|
2712
|
-
// Static fetch always keeps the native runtime even when a developer
|
|
2713
|
-
// has the comparison switch exported in their shell.
|
|
2714
|
-
const curlFetch = dynamic && fetchOn && process.env["SCRIPTC_FETCH_CURL"] === "1";
|
|
2715
|
-
const nativeFetch = fetchOn && !curlFetch;
|
|
2716
|
-
// The island's node:http/https client bridge: embedded graphs that
|
|
2717
|
-
// import those builtins get working clients over the same socket units
|
|
2718
|
-
// (native-fetch builds always carry it — scr_fetch_install registers it).
|
|
2719
|
-
const netIsland = dynamic && ((opts.netIsland ?? false) || nativeFetch);
|
|
2720
|
-
const net = (opts.net ?? false) || nativeFetch || netIsland;
|
|
2721
|
-
const http = (opts.http ?? false) || nativeFetch || netIsland;
|
|
2722
|
-
const tls = (opts.tls ?? false) || nativeFetch || netIsland;
|
|
2723
|
-
const tlsCa = (opts.tlsCa ?? false) || tls;
|
|
2724
|
-
const driver = resolveCc();
|
|
2725
|
-
const darwinDebugSymbols = needsDarwinDebugSymbols(targetPlatform(driver), optimization, opts.strip);
|
|
2726
|
-
if (opts.frameworks?.length && targetPlatform(driver) !== "darwin")
|
|
2727
|
-
throw new Error("FFI frameworks require a Darwin target");
|
|
2728
|
-
if (opts.frameworks?.some(name => !/^[A-Za-z][A-Za-z0-9_]*$/.test(name)))
|
|
2729
|
-
throw new Error("Invalid FFI framework name");
|
|
2730
|
-
const debugFlags = optimization === "dev" && !opts.strip
|
|
2731
|
-
? ["-gline-tables-only", ...(opts.cPath.endsWith(".ll") ? [] : ["-gno-column-info"])]
|
|
2732
|
-
: [];
|
|
2733
|
-
const shardNames = new Set();
|
|
2734
|
-
const programShardsValid = opts.programShards?.every((shard) => {
|
|
2735
|
-
if (basename(shard.name) !== shard.name || !shard.name.endsWith(".ll") ||
|
|
2736
|
-
shardNames.has(shard.name))
|
|
2737
|
-
return false;
|
|
2738
|
-
shardNames.add(shard.name);
|
|
2739
|
-
return true;
|
|
2740
|
-
}) === true;
|
|
2741
|
-
const programShardsRequested = optimization === "dev" && !sanitize && opts.cPath.endsWith(".ll") &&
|
|
2742
|
-
programShardsValid && opts.programShards !== undefined && opts.programShards.length > 1 &&
|
|
2743
|
-
opts.programPublicSymbols !== undefined
|
|
2744
|
-
? opts.programShards
|
|
2745
|
-
: null;
|
|
2746
|
-
const programShardMergeIdentity = programShardsRequested === null
|
|
2747
|
-
? null
|
|
2748
|
-
: await resolveProgramShardMergeIdentity(driver);
|
|
2749
|
-
const programShards = programShardMergeIdentity === null ? null : programShardsRequested;
|
|
2750
|
-
const programPublicSymbols = programShards === null ? undefined : opts.programPublicSymbols;
|
|
2751
|
-
// Mobile triples produce library archives, never standalone executables:
|
|
2752
|
-
// the executable-lane runtime (event loop, sockets, child processes) is
|
|
2753
|
-
// not verified on those device classes. compile() reports the SC3002
|
|
2754
|
-
// diagnostic first; this is the backstop for direct compileC callers.
|
|
2755
|
-
if (isMobileTarget(driver.target)) {
|
|
2756
|
-
throw new Error(`SCRIPTC_TARGET=${driver.target} builds library-mode static archives only — ` +
|
|
2757
|
-
`compile with a library profile (SCRIPTC_CC=zigcc scriptc build --lib --profile <profile.json>) and link the archive from the app project.`);
|
|
2758
|
-
}
|
|
2759
|
-
const runtimeSources = targetPlatform(driver) === "wasi"
|
|
2760
|
-
? EXECUTABLE_RUNTIME_SOURCES.filter((source) => source !== "scr_child.c")
|
|
2761
|
-
: EXECUTABLE_RUNTIME_SOURCES;
|
|
2762
|
-
const executableSectionFlags = executableSectionEliminationFlags(targetPlatform(driver));
|
|
2763
|
-
const windowsSubsystemArgs = windowsSubsystemLinkerArgs(targetPlatform(driver), opts.windowsSubsystem);
|
|
2764
|
-
const executableLinkFlags = [
|
|
2765
|
-
...executableSectionFlags.link,
|
|
2766
|
-
...executableOptimizationLinkerArgs(targetPlatform(driver), optimization),
|
|
2767
|
-
...executableStripLinkerArgs(targetPlatform(driver), opts.strip ?? false),
|
|
2768
|
-
...windowsSubsystemArgs,
|
|
2769
|
-
];
|
|
2770
|
-
// scr_async.c submits callback-style filesystem work to a native worker.
|
|
2771
|
-
// POSIX drivers need the thread compile/link mode; win32 uses CreateThread.
|
|
2772
|
-
const threadArgs = targetPlatform(driver) === "win32" || targetPlatform(driver) === "wasi"
|
|
2773
|
-
? []
|
|
2774
|
-
: ["-pthread"];
|
|
2775
|
-
if (driver.target !== null) {
|
|
2776
|
-
// See the resolveCc block: these inputs are built on and for the HOST
|
|
2777
|
-
// (vendored archives, system libs). Regex, zlib, and the engine archive
|
|
2778
|
-
// are NOT here: their vendored sources are plain C that ensureLreObjects
|
|
2779
|
-
// / ensureZlibObjects / buildEngineArchiveDirect compile per target with
|
|
2780
|
-
// the driver itself — win32 included (the Windows lane runs the
|
|
2781
|
-
// @dynamic corpus and the zlib program against the box's Node). The
|
|
2782
|
-
// NATIVE fetch rides the socket units and cross-compiles with them —
|
|
2783
|
-
// no gate; only the retired curl REFERENCE (SCRIPTC_FETCH_CURL=1)
|
|
2784
|
-
// keeps a linux-only arm (the vendored curl headers + the generated
|
|
2785
|
-
// soname stub, ensureCurlStub — win32 has no system libcurl contract
|
|
2786
|
-
// to bind at load time). The event-loop units, tls, and dgram cross-compile to Linux
|
|
2787
|
-
// AND Windows (scr_platform.h poller + per-target mbedTLS; the
|
|
2788
|
-
// loop's win32 arm is WSAPoll, scr_loop_wsapoll.c, and the socket
|
|
2789
|
-
// units' win32 arms respell winsock behind POSIX-errno wrappers —
|
|
2790
|
-
// scr_net.c/scr_dgram.c/scr_tls.c). The events unit cross-compiles
|
|
2791
|
-
// everywhere: its win32 arm is CRT signal() +
|
|
2792
|
-
// PeekNamedPipe/WaitForSingleObject probes (scr_events.c), served by
|
|
2793
|
-
// the loop's capped win32 idle sleep (scr_async.c).
|
|
2794
|
-
const unsupported = [
|
|
2795
|
-
// The NATIVE fetch cross-compiles wherever the socket units do
|
|
2796
|
-
// (linux and win32 both); only the retired curl reference keeps
|
|
2797
|
-
// its linux-only soname-stub arm.
|
|
2798
|
-
["fetch (SCRIPTC_FETCH_CURL)", curlFetch && targetPlatform(driver) !== "linux"],
|
|
2799
|
-
]
|
|
2800
|
-
.filter(([, on]) => on)
|
|
2801
|
-
.map(([name]) => name);
|
|
2802
|
-
if (unsupported.length > 0) {
|
|
2803
|
-
throw new Error(`SCRIPTC_TARGET=${driver.target}: ${unsupported.join(", ")} not supported under a cross target yet ` +
|
|
2804
|
-
`(host-built vendor archives / system libs — see docs/linux-port.md).`);
|
|
2805
|
-
}
|
|
2806
|
-
}
|
|
2807
|
-
const cachePolicy = toolchainEnvironmentCachePolicy();
|
|
2808
|
-
const configuredCacheRoot = cacheRootDir();
|
|
2809
|
-
const toolchainEnv = toolchainEnvironmentFingerprint();
|
|
2810
|
-
const persistentDriverCache = cachePolicy.runtimeObjects &&
|
|
2811
|
-
configuredCacheRoot !== null &&
|
|
2812
|
-
await compilerDriverSupportsPersistentCache(driver, toolchainEnv);
|
|
2813
|
-
// Only compiler-generated TUs opt in. Arbitrary `compileC` inputs
|
|
2814
|
-
// inputs may include caller-owned headers whose contents are not otherwise
|
|
2815
|
-
// represented in this key, so they retain the fully uncached historical
|
|
2816
|
-
// path unless the caller supplies its own complete dependency identity.
|
|
2817
|
-
const cacheIdentity = opts.cacheIdentity;
|
|
2818
|
-
let persistentCache = cacheIdentity === undefined || configuredCacheRoot === null || !persistentDriverCache
|
|
2819
|
-
? null
|
|
2820
|
-
: { root: configuredCacheRoot, identity: cacheIdentity };
|
|
2821
|
-
if (cacheWarmOnly && persistentCache === null) {
|
|
2822
|
-
throw new Error("native cache warming requires a persistently cacheable compiler environment");
|
|
2823
|
-
}
|
|
2824
|
-
if (persistentCache !== null) {
|
|
2825
|
-
try {
|
|
2826
|
-
await ensurePrivateCacheRoot(persistentCache.root, process.env["SCRIPTC_CACHE_DIR"] === undefined);
|
|
2827
|
-
}
|
|
2828
|
-
catch (error) {
|
|
2829
|
-
if (cacheWarmOnly) {
|
|
2830
|
-
throw new Error("native cache warming could not prepare the persistent cache root", {
|
|
2831
|
-
cause: error,
|
|
2832
|
-
});
|
|
2833
|
-
}
|
|
2834
|
-
persistentCache = null;
|
|
2835
|
-
}
|
|
2836
|
-
}
|
|
2837
|
-
let localArtifact = null;
|
|
2838
|
-
// Generated executable TUs are closed over scriptc's own runtime tree. A
|
|
2839
|
-
// same-output rebuild can therefore check those bytes directly before the
|
|
2840
|
-
// broader cross-output CAS performs its compiler/SDK/linker rediscovery.
|
|
2841
|
-
// FFI/native-input builds and the public arbitrary-C cache API stay on the
|
|
2842
|
-
// strict path because their dependency graphs are caller-owned.
|
|
2843
|
-
if (!cacheWarmOnly &&
|
|
2844
|
-
persistentCache !== null &&
|
|
2845
|
-
cachePolicy.completeArtifacts &&
|
|
2846
|
-
persistentCache.identity === "scriptc-generated-v1" &&
|
|
2847
|
-
(opts.linkInputs?.length ?? 0) === 0 &&
|
|
2848
|
-
(opts.frameworks?.length ?? 0) === 0 &&
|
|
2849
|
-
(opts.systemLibraries?.length ?? 0) === 0 &&
|
|
2850
|
-
process.env["SCRIPTC_TEST_TRUST_COMPILER_WRAPPER"] !== "1") {
|
|
2851
|
-
try {
|
|
2852
|
-
const [compiler, runtimeHash, programBytes] = await Promise.all([
|
|
2853
|
-
resolvedTool(driver.argv[0] ?? "clang"),
|
|
2854
|
-
runtimeFingerprint(rtDir),
|
|
2855
|
-
readFile(opts.cPath),
|
|
2856
|
-
]);
|
|
2857
|
-
if (compiler !== null) {
|
|
2858
|
-
const effectiveCompiler = directCompilerSelections.get(compilerDriverProbeKey(driver, toolchainEnv)) ?? compiler;
|
|
2859
|
-
const key = localArtifactIdentity(opts, driver, toolchainEnv, `${compiler.cacheIdentity}\0${effectiveCompiler.cacheIdentity}`, runtimeHash, programBytes, programShardMergeIdentity);
|
|
2860
|
-
const stampPath = localArtifactStampPath(persistentCache.root, opts.outPath);
|
|
2861
|
-
localArtifact = {
|
|
2862
|
-
stampPath,
|
|
2863
|
-
key,
|
|
2864
|
-
runtimeHash,
|
|
2865
|
-
programBytes,
|
|
2866
|
-
compilerPath: effectiveCompiler.canonicalPath,
|
|
2867
|
-
};
|
|
2868
|
-
const hit = await localArtifactHit(stampPath, opts.outPath, key, darwinDebugSymbols);
|
|
2869
|
-
if (hit !== null) {
|
|
2870
|
-
await opts.onArtifactReady?.({ dependencies: hit.dependencies }).catch(() => undefined);
|
|
2871
|
-
return;
|
|
2872
|
-
}
|
|
2873
|
-
}
|
|
2874
|
-
}
|
|
2875
|
-
catch {
|
|
2876
|
-
// The output-local tier is only an optimization; the fully validated
|
|
2877
|
-
// CAS below remains the source of truth on any metadata trouble.
|
|
2878
|
-
localArtifact = null;
|
|
2879
|
-
}
|
|
2880
|
-
}
|
|
2881
|
-
let implicitToolchain = null;
|
|
2882
|
-
let implicitCompileToolchain = null;
|
|
2883
|
-
let toolchainMetadataStamp = null;
|
|
2884
|
-
const metadataCompiler = persistentCache === null
|
|
2885
|
-
? null
|
|
2886
|
-
: await resolvedTool(driver.argv[0] ?? "clang");
|
|
2887
|
-
const metadataEffectiveCompiler = directCompilerSelections.get(compilerDriverProbeKey(driver, toolchainEnv)) ?? metadataCompiler;
|
|
2888
|
-
const toolchainMetadataKey = persistentCache === null ||
|
|
2889
|
-
metadataCompiler === null ||
|
|
2890
|
-
metadataEffectiveCompiler === null ||
|
|
2891
|
-
process.env["SCRIPTC_TEST_TRUST_COMPILER_WRAPPER"] === "1"
|
|
2892
|
-
? null
|
|
2893
|
-
: nativeMetadataKey("toolchain", [
|
|
2894
|
-
cacheTargetIdentity(driver),
|
|
2895
|
-
toolchainEnv,
|
|
2896
|
-
driver.argv,
|
|
2897
|
-
driver.targetArgs,
|
|
2898
|
-
metadataCompiler.cacheIdentity,
|
|
2899
|
-
metadataEffectiveCompiler.cacheIdentity,
|
|
2900
|
-
rtDir,
|
|
2901
|
-
]);
|
|
2902
|
-
if (persistentDriverCache) {
|
|
2903
|
-
try {
|
|
2904
|
-
toolchainMetadataStamp =
|
|
2905
|
-
persistentCache === null || toolchainMetadataKey === null
|
|
2906
|
-
? null
|
|
2907
|
-
: await readNativeMetadataStamp(persistentCache.root, toolchainMetadataKey);
|
|
2908
|
-
implicitToolchain = toolchainMetadataStamp?.values["implicitToolchain"] ?? null;
|
|
2909
|
-
implicitCompileToolchain =
|
|
2910
|
-
toolchainMetadataStamp?.values["implicitCompileToolchain"] ?? null;
|
|
2911
|
-
if (implicitToolchain === null ||
|
|
2912
|
-
(programShards !== null && implicitCompileToolchain === null)) {
|
|
2913
|
-
const fingerprints = await implicitToolchainFingerprints(driver, toolchainEnv);
|
|
2914
|
-
implicitToolchain = fingerprints.complete;
|
|
2915
|
-
implicitCompileToolchain = fingerprints.compile;
|
|
2916
|
-
toolchainMetadataStamp = null;
|
|
2917
|
-
}
|
|
2918
|
-
let compilerVersion = toolchainMetadataStamp?.values["compilerVersion"];
|
|
2919
|
-
if (compilerVersion === undefined) {
|
|
2920
|
-
compilerVersion = await ccVersion(driver.argv, toolchainEnv, true);
|
|
2921
|
-
}
|
|
2922
|
-
if (toolchainMetadataStamp === null &&
|
|
2923
|
-
persistentCache !== null &&
|
|
2924
|
-
toolchainMetadataKey !== null) {
|
|
2925
|
-
if (metadataCompiler !== null && metadataEffectiveCompiler !== null) {
|
|
2926
|
-
toolchainMetadataStamp = await publishNativeMetadataStamp(persistentCache.root, toolchainMetadataKey, {
|
|
2927
|
-
implicitToolchain,
|
|
2928
|
-
...(implicitCompileToolchain === null ? {} : { implicitCompileToolchain }),
|
|
2929
|
-
compilerVersion,
|
|
2930
|
-
}, [
|
|
2931
|
-
metadataCompiler.canonicalPath,
|
|
2932
|
-
metadataEffectiveCompiler.canonicalPath,
|
|
2933
|
-
...fingerprintDependencyPaths(implicitToolchain),
|
|
2934
|
-
...(implicitCompileToolchain === null
|
|
2935
|
-
? []
|
|
2936
|
-
: fingerprintDependencyPaths(implicitCompileToolchain)),
|
|
2937
|
-
], [
|
|
2938
|
-
implicitToolchain,
|
|
2939
|
-
...(implicitCompileToolchain === null ? [] : [implicitCompileToolchain]),
|
|
2940
|
-
]);
|
|
2941
|
-
}
|
|
2942
|
-
}
|
|
2943
|
-
}
|
|
2944
|
-
catch (error) {
|
|
2945
|
-
// Cache discovery is best-effort. In particular, a compiler wrapper can
|
|
2946
|
-
// compile successfully without implementing the metadata probes.
|
|
2947
|
-
if (cacheWarmOnly) {
|
|
2948
|
-
throw new Error("native cache warming could not validate the compiler toolchain", {
|
|
2949
|
-
cause: error,
|
|
2950
|
-
});
|
|
2951
|
-
}
|
|
2952
|
-
persistentCache = null;
|
|
2953
|
-
}
|
|
2954
|
-
}
|
|
2955
|
-
const vendorBuildIdentity = await currentVendorCacheBuildIdentity(driver, `${toolchainEnv}\0${implicitToolchain ?? "<uncached>"}`);
|
|
2956
|
-
// Mutable include/SDK/config inputs can change behind a stable environment
|
|
2957
|
-
// spelling. The complete/runtime cache is disabled below; vendor objects
|
|
2958
|
-
// must follow the same rule instead of silently surviving in the user cache.
|
|
2959
|
-
// A private root gives this invocation the usual vendor build recipe
|
|
2960
|
-
// without publishing or reusing those prerequisites.
|
|
2961
|
-
const transientVendorRoot = persistentCache !== null && implicitToolchain !== null
|
|
2962
|
-
? null
|
|
2963
|
-
: join(tmpdir(), `scriptc-vendor-${process.pid}-${Math.random().toString(36).slice(2)}`);
|
|
2964
|
-
const vendorCacheRoot = transientVendorRoot ?? vendorBuildCacheRoot(persistentCache?.root);
|
|
2965
|
-
// Every vendor output path is deterministic from pins, flags, driver, and
|
|
2966
|
-
// target. Build the command/key from those paths now, but do not materialize
|
|
2967
|
-
// them until a complete-binary lookup has missed.
|
|
2968
|
-
let engineArchive = dynamic
|
|
2969
|
-
? engineArchivePath(sanitize, driver, vendorBuildIdentity, vendorCacheRoot)
|
|
2970
|
-
: null;
|
|
2971
|
-
let tlsArchive = tls
|
|
2972
|
-
? tlsArchivePath(sanitize, driver, vendorBuildIdentity, vendorCacheRoot)
|
|
2973
|
-
: null;
|
|
2974
|
-
// --dynamic + regex shares the archive's libregexp (its host hooks and
|
|
2975
|
-
// ours would collide; see scr_regex.c) — the standalone objects are for
|
|
2976
|
-
// static builds only.
|
|
2977
|
-
let lreObjects = regex && !dynamic
|
|
2978
|
-
? lreObjectPaths(sanitize, driver, vendorBuildIdentity, vendorCacheRoot)
|
|
2979
|
-
: [];
|
|
2980
|
-
// Vendored zlib is the Zig story — default host-clang builds keep the exact
|
|
2981
|
-
// historical `-lz` system link (see CcOptions.zlib). The native fetch's
|
|
2982
|
-
// gzip decoder rides the same objects/link.
|
|
2983
|
-
let zlibObjects = ((opts.zlib ?? false) || nativeFetch) && isZigDriver(driver)
|
|
2984
|
-
? zlibObjectPaths(sanitize, driver, vendorBuildIdentity, vendorCacheRoot)
|
|
2985
|
-
: [];
|
|
2986
|
-
// The libcurl import stub is likewise CROSS-only — host builds keep the
|
|
2987
|
-
// exact historical system `-lcurl` link (see CcOptions.fetch). Curl
|
|
2988
|
-
// reference builds only.
|
|
2989
|
-
let curlStubDir = curlFetch && driver.target !== null
|
|
2990
|
-
? curlStubDirPath(driver, vendorBuildIdentity, vendorCacheRoot)
|
|
2991
|
-
: null;
|
|
2992
|
-
const materializeVendorPrerequisites = async (stageRoot, materializeCacheRoot = vendorCacheRoot) => {
|
|
2993
|
-
// Preserve source order to avoid multiplying first-build resource
|
|
2994
|
-
// pressure when several large vendor sets are cold simultaneously.
|
|
2995
|
-
if (dynamic) {
|
|
2996
|
-
const cachedArchive = engineArchivePath(sanitize, driver, vendorBuildIdentity, materializeCacheRoot);
|
|
2997
|
-
protectCachedArtifact(cacheWarmPaths, cachedArchive);
|
|
2998
|
-
const materialize = async () => [
|
|
2999
|
-
await ensureEngineArchive(sanitize, driver, vendorBuildIdentity, materializeCacheRoot),
|
|
3000
|
-
];
|
|
3001
|
-
const paths = stageRoot === undefined
|
|
3002
|
-
? await materialize()
|
|
3003
|
-
: await stageVendorInputs(materialize, join(stageRoot, "engine"));
|
|
3004
|
-
engineArchive = paths[0];
|
|
3005
|
-
}
|
|
3006
|
-
if (tls) {
|
|
3007
|
-
const cachedArchive = tlsArchivePath(sanitize, driver, vendorBuildIdentity, materializeCacheRoot);
|
|
3008
|
-
protectCachedArtifact(cacheWarmPaths, cachedArchive);
|
|
3009
|
-
const materialize = async () => [
|
|
3010
|
-
await ensureTlsArchive(sanitize, driver, vendorBuildIdentity, materializeCacheRoot),
|
|
3011
|
-
];
|
|
3012
|
-
const paths = stageRoot === undefined
|
|
3013
|
-
? await materialize()
|
|
3014
|
-
: await stageVendorInputs(materialize, join(stageRoot, "tls"));
|
|
3015
|
-
tlsArchive = paths[0];
|
|
3016
|
-
}
|
|
3017
|
-
if (regex && !dynamic) {
|
|
3018
|
-
for (const object of lreObjectPaths(sanitize, driver, vendorBuildIdentity, materializeCacheRoot))
|
|
3019
|
-
protectCachedArtifact(cacheWarmPaths, object);
|
|
3020
|
-
const materialize = async () => await ensureLreObjects(sanitize, driver, vendorBuildIdentity, materializeCacheRoot);
|
|
3021
|
-
lreObjects = stageRoot === undefined
|
|
3022
|
-
? await materialize()
|
|
3023
|
-
: await stageVendorInputs(materialize, join(stageRoot, "lre"));
|
|
3024
|
-
}
|
|
3025
|
-
if (zlibObjects.length > 0) {
|
|
3026
|
-
for (const object of zlibObjectPaths(sanitize, driver, vendorBuildIdentity, materializeCacheRoot))
|
|
3027
|
-
protectCachedArtifact(cacheWarmPaths, object);
|
|
3028
|
-
const materialize = async () => await ensureZlibObjects(sanitize, driver, vendorBuildIdentity, materializeCacheRoot);
|
|
3029
|
-
zlibObjects = stageRoot === undefined
|
|
3030
|
-
? await materialize()
|
|
3031
|
-
: await stageVendorInputs(materialize, join(stageRoot, "zlib"));
|
|
3032
|
-
}
|
|
3033
|
-
if (curlStubDir !== null) {
|
|
3034
|
-
protectCachedArtifact(cacheWarmPaths, join(curlStubDirPath(driver, vendorBuildIdentity, materializeCacheRoot), "libcurl.so"));
|
|
3035
|
-
const materialize = async () => [
|
|
3036
|
-
join(await ensureCurlStub(driver, vendorBuildIdentity, materializeCacheRoot), "libcurl.so"),
|
|
3037
|
-
];
|
|
3038
|
-
const paths = stageRoot === undefined
|
|
3039
|
-
? await materialize()
|
|
3040
|
-
: await stageVendorInputs(materialize, join(stageRoot, "curl"));
|
|
3041
|
-
curlStubDir = dirname(paths[0]);
|
|
3042
|
-
}
|
|
3043
|
-
};
|
|
3044
|
-
// rt() maps each runtime source's path on the command line: identity for
|
|
3045
|
-
// the historical single invocation, cached-.o substitution on cache misses.
|
|
3046
|
-
const buildArgs = (rt, build = {}) => [
|
|
3047
|
-
"-std=c11",
|
|
3048
|
-
...debugFlags,
|
|
3049
|
-
...driver.targetArgs,
|
|
3050
|
-
...threadArgs,
|
|
3051
|
-
...(sanitize
|
|
3052
|
-
? ["-O1", "-fsanitize=address", "-DSCR_RC_AUDIT"]
|
|
3053
|
-
: [optimization === "dev" ? "-O0" : "-O2"]),
|
|
3054
|
-
...executableSectionFlags.compile,
|
|
3055
|
-
...(opts.textDecoderLegacy ? ["-DSCR_TEXT_DECODER_LEGACY"] : []),
|
|
3056
|
-
"-fno-math-errno",
|
|
3057
|
-
// The runtime object model uses type-punned C: a hierarchy
|
|
3058
|
-
// upcast is a raw pointer cast, so one object's header (rc, vt) and
|
|
3059
|
-
// fields are read and written through BOTH the base and derived struct
|
|
3060
|
-
// types (sc_retain_Derived vs sc_release_Base on the same object).
|
|
3061
|
-
// C's effective-type rule calls that UB, and clang's TBAA at -O2
|
|
3062
|
-
// reorders/elides the rc updates once everything inlines — an upcast
|
|
3063
|
-
// identity compare frees the object while a global still owns it.
|
|
3064
|
-
// The LLVM backend emits no TBAA metadata; this flag preserves
|
|
3065
|
-
// matching memory semantics in the runtime. Mirrored in the
|
|
3066
|
-
// cache-miss cflags below and compileLibArchive — the three option
|
|
3067
|
-
// sets must stay in lockstep.
|
|
3068
|
-
"-fno-strict-aliasing",
|
|
3069
|
-
"-Wno-deprecated-declarations", // ucontext fibers (scr_async.c)
|
|
3070
|
-
"-I", rtDir,
|
|
3071
|
-
...runtimeSources.map((f) => rt(join(rtDir, f))),
|
|
3072
|
-
...(opts.copying ? [rt(join(rtDir, "scr_copying.c"))] : []),
|
|
3073
|
-
...(opts.fileHandle ? [rt(join(rtDir, "scr_file_handle.c"))] : []),
|
|
3074
|
-
// win32 targets compile the libc-shim TU (stpcpy, arc4random_buf,
|
|
3075
|
-
// gmtime_r, strcasestr — the _WIN32 block in scr_runtime.h declares
|
|
3076
|
-
// them) and link advapi32 (the CSPRNG RtlGenRandom/SystemFunction036,
|
|
3077
|
-
// GetUserNameA), iphlpapi (GetAdaptersAddresses behind
|
|
3078
|
-
// os.networkInterfaces), and ws2_32 (inet_ntop there; the socket
|
|
3079
|
-
// units ride the same import). Never present on the default path, so
|
|
3080
|
-
// the historical line cannot change.
|
|
3081
|
-
...(targetPlatform(driver) === "win32"
|
|
3082
|
-
? [rt(join(rtDir, "scr_win.c")), "-ladvapi32", "-liphlpapi", "-lws2_32"]
|
|
3083
|
-
: []),
|
|
3084
|
-
// musl deliberately has no libc-identification predefine; resolveCc's
|
|
3085
|
-
// SCR_MUSL flag and this target-selected TU travel together.
|
|
3086
|
-
...(isMuslTarget(driver) ? [rt(join(rtDir, "scr_musl.c"))] : []),
|
|
3087
|
-
...(regex
|
|
3088
|
-
? ["-I", vendorEngineDir(), rt(join(rtDir, "scr_regex.c")), ...lreObjects]
|
|
3089
|
-
: []),
|
|
3090
|
-
...(opts.assert || regex || opts.symbol ? [rt(join(rtDir, "scr_assert.c"))] : []),
|
|
3091
|
-
...(opts.inspect ? [rt(join(rtDir, "scr_inspect.c")), rt(join(rtDir, "scr_console_native.c"))] : []),
|
|
3092
|
-
...((opts.dynInvoke || nativeFetch) ? [rt(join(rtDir, "scr_dyn_invoke.c"))] : []),
|
|
3093
|
-
...(opts.dc ? [rt(join(rtDir, "scr_dc.c"))] : []),
|
|
3094
|
-
...(opts.dynAsync || opts.dynInvoke || opts.dc || opts.fileHandle || nativeFetch ? [rt(join(rtDir, "scr_async_dyn.c"))] : []),
|
|
3095
|
-
// The zlib UNIT (scr_zlib.c) gates on zlib.* IR use; the LINK (system
|
|
3096
|
-
// libz on the default host-clang build, vendored objects on Zig builds)
|
|
3097
|
-
// also serves the native fetch's gzip decoder — spread exactly once.
|
|
3098
|
-
...(opts.zlib
|
|
3099
|
-
? isZigDriver(driver)
|
|
3100
|
-
? ["-I", vendorZlibDir(), rt(join(rtDir, "scr_zlib.c")), ...zlibObjects]
|
|
3101
|
-
: [rt(join(rtDir, "scr_zlib.c"))]
|
|
3102
|
-
: nativeFetch
|
|
3103
|
-
? isZigDriver(driver)
|
|
3104
|
-
? ["-I", vendorZlibDir(), ...zlibObjects]
|
|
3105
|
-
: []
|
|
3106
|
-
: []),
|
|
3107
|
-
// The zlib ↔ island bridge: only when BOTH halves are in the build
|
|
3108
|
-
// (the scr_inspect_island.c pattern) — the emitted main calls its
|
|
3109
|
-
// installer exactly then.
|
|
3110
|
-
...(opts.zlib && opts.dynamic ? [rt(join(rtDir, "scr_zlib_island.c"))] : []),
|
|
3111
|
-
...(opts.events ? [rt(join(rtDir, "scr_events.c")), rt(join(rtDir, "scr_readline.c"))] : []),
|
|
3112
|
-
...(opts.emitter ? [rt(join(rtDir, "scr_events_emitter.c"))] : []),
|
|
3113
|
-
// The checked-dynamic HANDLE support unit (listener gate + runtime
|
|
3114
|
-
// adapter closures): every referencing unit is one of the emitter or
|
|
3115
|
-
// net families (http implies net), so handle-free binaries keep
|
|
3116
|
-
// their exact size class.
|
|
3117
|
-
...(opts.emitter || net ? [rt(join(rtDir, "scr_dyn_handle.c"))] : []),
|
|
3118
|
-
...(opts.symbol ? [rt(join(rtDir, "scr_symbol.c"))] : []),
|
|
3119
|
-
...(opts.assert && opts.bigint ? [rt(join(rtDir, "scr_bigint_assert.c"))] : []),
|
|
3120
|
-
...(opts.qs ? [rt(join(rtDir, "scr_qs.c"))] : []),
|
|
3121
|
-
...(opts.parseArgs ? [rt(join(rtDir, "scr_util.c"))] : []),
|
|
3122
|
-
...(opts.parseArgs && opts.symbol ? [rt(join(rtDir, "scr_util_compare.c"))] : []),
|
|
3123
|
-
...(opts.parseArgs && opts.inspect && opts.dynAsync ? [rt(join(rtDir, "scr_util_style.c"))] : []),
|
|
3124
|
-
...(opts.stream ? [rt(join(rtDir, "scr_stream.c"))] : []),
|
|
3125
|
-
// The readiness-poller backends (scr_platform.h): kqueue on macOS/BSD,
|
|
3126
|
-
// epoll on Linux, WSAPoll on Windows — each TU is empty off its
|
|
3127
|
-
// platform, so all three link whenever a poller-using unit does and
|
|
3128
|
-
// the others cost nothing (ws2_32 rides the unconditional win32 libs
|
|
3129
|
-
// above).
|
|
3130
|
-
...(net || opts.dgram
|
|
3131
|
-
? [
|
|
3132
|
-
rt(join(rtDir, "scr_loop_kqueue.c")),
|
|
3133
|
-
rt(join(rtDir, "scr_loop_epoll.c")),
|
|
3134
|
-
rt(join(rtDir, "scr_loop_wsapoll.c")),
|
|
3135
|
-
]
|
|
3136
|
-
: []),
|
|
3137
|
-
...(net ? [rt(join(rtDir, "scr_net.c"))] : []),
|
|
3138
|
-
...(http ? [rt(join(rtDir, "scr_http.c"))] : []),
|
|
3139
|
-
...(opts.http2 ?? false ? [rt(join(rtDir, "scr_http2.c"))] : []),
|
|
3140
|
-
...(opts.dgram ? [rt(join(rtDir, "scr_dgram.c"))] : []),
|
|
3141
|
-
...(opts.watch ? [rt(join(rtDir, "scr_watch.c"))] : []),
|
|
3142
|
-
...(opts.foreignFfi ? [rt(join(rtDir, "scr_ffi_queue.c"))] : []),
|
|
3143
|
-
...(opts.nodeTest ? [rt(join(rtDir, "scr_test.c"))] : []),
|
|
3144
|
-
// The CA-store unit rides its own gate OR the tls one: scr_tls.c
|
|
3145
|
-
// references its default-set override unconditionally.
|
|
3146
|
-
...(tlsCa ? [rt(join(rtDir, "scr_tls_ca.c"))] : []),
|
|
3147
|
-
...(tlsArchive
|
|
3148
|
-
? [
|
|
3149
|
-
"-I", join(vendorTlsDir(), "include"),
|
|
3150
|
-
rt(join(rtDir, "scr_tls.c")),
|
|
3151
|
-
tlsArchive,
|
|
3152
|
-
// mbedTLS's win32 entropy poll is BCryptGenRandom (bcrypt.h).
|
|
3153
|
-
// The unconditional win32 libs above do not carry it.
|
|
3154
|
-
// Never present on the default path, so the historical TLS
|
|
3155
|
-
// link line cannot change.
|
|
3156
|
-
...(targetPlatform(driver) === "win32" ? ["-lbcrypt"] : []),
|
|
3157
|
-
]
|
|
3158
|
-
: []),
|
|
3159
|
-
// scr_tls_ca.c enumerates and PEM-encodes Windows system-store entries.
|
|
3160
|
-
// This is independent of the mbedTLS archive: getCACertificates-only
|
|
3161
|
-
// programs need crypt32 too, while TLS programs imply the CA unit.
|
|
3162
|
-
...(tlsCa && targetPlatform(driver) === "win32" ? ["-lcrypt32"] : []),
|
|
3163
|
-
// Static fetch is the engine-free half of scr_fetch.c. Dynamic builds
|
|
3164
|
-
// compile the same source beside scr_island.c below, where its
|
|
3165
|
-
// SCR_DYNAMIC half installs the full web surface.
|
|
3166
|
-
...(nativeFetch && !dynamic ? [rt(join(rtDir, "scr_fetch.c"))] : []),
|
|
3167
|
-
...(engineArchive
|
|
3168
|
-
? [
|
|
3169
|
-
"-DSCR_DYNAMIC",
|
|
3170
|
-
"-I", vendorEngineDir(),
|
|
3171
|
-
rt(join(rtDir, "scr_island.c")),
|
|
3172
|
-
rt(join(rtDir, "scr_web.c")),
|
|
3173
|
-
// The one unit referencing BOTH the island and the inspect
|
|
3174
|
-
// engine (insp.jsval): linked exactly when both halves are.
|
|
3175
|
-
...(opts.inspect ? [rt(join(rtDir, "scr_inspect_island.c"))] : []),
|
|
3176
|
-
// fetch: the NATIVE bridge by default (its socket/tls/zlib
|
|
3177
|
-
// dependencies joined the link above); the curl REFERENCE
|
|
3178
|
-
// (SCRIPTC_FETCH_CURL=1) keeps the historical system -lcurl /
|
|
3179
|
-
// linux soname-stub arms for one release.
|
|
3180
|
-
...(nativeFetch ? [rt(join(rtDir, "scr_fetch.c"))] : []),
|
|
3181
|
-
// The island's node:http/https CLIENT bridge (scr_net_island.c):
|
|
3182
|
-
// the one TU referencing both the socket units and the engine —
|
|
3183
|
-
// compiled beside the native fetch (scr_fetch_install registers
|
|
3184
|
-
// it) and whenever the embedded graph imports node:http/https
|
|
3185
|
-
// (the emitted main calls scr_net_island_install exactly then).
|
|
3186
|
-
...(netIsland ? [rt(join(rtDir, "scr_net_island.c"))] : []),
|
|
3187
|
-
...(curlFetch
|
|
3188
|
-
? curlStubDir !== null
|
|
3189
|
-
? ["-I", join(vendorCurlDir(), "include"), rt(join(rtDir, "scr_fetch_curl.c")), `-L${curlStubDir}`, "-lcurl"]
|
|
3190
|
-
: [rt(join(rtDir, "scr_fetch_curl.c")), "-lcurl"]
|
|
3191
|
-
: []),
|
|
3192
|
-
engineArchive,
|
|
3193
|
-
// Linux's libm is appended after every input below for GNU ld's
|
|
3194
|
-
// left-to-right archive resolution. Other dynamic targets keep
|
|
3195
|
-
// the historical engine-adjacent spelling.
|
|
3196
|
-
...(driver.linkArgs.includes("-lm") ? [] : ["-lm"]),
|
|
3197
|
-
// The PE stack reserve, pinned to the 8MB POSIX main-stack
|
|
3198
|
-
// geometry ISL_MAIN_STACK_BUDGET is sized against (4MB engine
|
|
3199
|
-
// budget + 4MB excursion margin) — quickjs-ng's own CMake makes
|
|
3200
|
-
// the same 8MB choice on Windows. Not left to the driver:
|
|
3201
|
-
// classic mingw ld defaults to 2MB (which the budget would blow
|
|
3202
|
-
// straight past); zig's lld happens to default to 16MB today,
|
|
3203
|
-
// but that is nobody's contract.
|
|
3204
|
-
...(targetPlatform(driver) === "win32" ? ["-Wl,--stack,8388608"] : []),
|
|
3205
|
-
]
|
|
3206
|
-
: []),
|
|
3207
|
-
// A .ll program TU (the LLVM backend) deliberately carries no target
|
|
3208
|
-
// triple (byte-stable output; clang supplies the host/SCRIPTC_TARGET
|
|
3209
|
-
// triple exactly as it does for .c) — silence the -Woverride-module
|
|
3210
|
-
// note about that. Never present for .c inputs, so the historical C
|
|
3211
|
-
// command line cannot change by a byte.
|
|
3212
|
-
...(opts.cPath.endsWith(".ll") ? ["-Wno-override-module"] : []),
|
|
3213
|
-
...(build.compilerVisibleSource !== undefined && build.programPath !== undefined
|
|
3214
|
-
? [
|
|
3215
|
-
`-ffile-prefix-map=${build.programPath}=${build.compilerVisibleSource}`,
|
|
3216
|
-
"-iquote",
|
|
3217
|
-
dirname(resolve(build.compilerVisibleSource)),
|
|
3218
|
-
]
|
|
3219
|
-
: []),
|
|
3220
|
-
build.programPath ?? opts.cPath,
|
|
3221
|
-
...(opts.linkInputs ?? []),
|
|
3222
|
-
...(opts.systemLibraries ?? []).map((name) => `-l${name}`),
|
|
3223
|
-
...(opts.frameworks ?? []).flatMap(name => ["-framework", name]),
|
|
3224
|
-
// GNU ld resolves libraries from left to right and commonly enables
|
|
3225
|
-
// --as-needed: host-clang libz must follow scr_zlib.c/scr_fetch.c and every
|
|
3226
|
-
// generated/native input that references inflate symbols. Cross
|
|
3227
|
-
// and targetless Zig builds use vendored zlib objects in the input section above.
|
|
3228
|
-
...(((opts.zlib ?? false) || nativeFetch) && !isZigDriver(driver)
|
|
3229
|
-
? ["-lz"]
|
|
3230
|
-
: []),
|
|
3231
|
-
// glibc keeps libm separate from libc. This must trail the generated
|
|
3232
|
-
// program and every native FFI input because GNU ld resolves archives
|
|
3233
|
-
// from left to right.
|
|
3234
|
-
...driver.linkArgs,
|
|
3235
|
-
...executableLinkFlags,
|
|
3236
|
-
"-o", build.outPath ?? opts.outPath,
|
|
3237
|
-
];
|
|
3238
|
-
// Runtime-object flags. Program compilation and dependency discovery add
|
|
3239
|
-
// the dev sanitizer override below so both cache identities match reality.
|
|
3240
|
-
const cflags = [
|
|
3241
|
-
"-std=c11",
|
|
3242
|
-
...debugFlags,
|
|
3243
|
-
...driver.targetArgs,
|
|
3244
|
-
...threadArgs,
|
|
3245
|
-
...(sanitize
|
|
3246
|
-
? ["-O1", "-fsanitize=address", "-DSCR_RC_AUDIT"]
|
|
3247
|
-
: [optimization === "dev" ? "-O0" : "-O2"]),
|
|
3248
|
-
...executableSectionFlags.compile,
|
|
3249
|
-
...(opts.textDecoderLegacy ? ["-DSCR_TEXT_DECODER_LEGACY"] : []),
|
|
3250
|
-
"-fno-math-errno",
|
|
3251
|
-
"-fno-strict-aliasing", // the emitted object model type-puns — see buildArgs
|
|
3252
|
-
"-Wno-deprecated-declarations",
|
|
3253
|
-
"-I", rtDir,
|
|
3254
|
-
...(regex || dynamic ? ["-I", vendorEngineDir()] : []),
|
|
3255
|
-
...(zlibObjects.length > 0 ? ["-I", vendorZlibDir()] : []),
|
|
3256
|
-
...(curlStubDir !== null ? ["-I", join(vendorCurlDir(), "include")] : []),
|
|
3257
|
-
...(tlsArchive !== null ? ["-I", join(vendorTlsDir(), "include")] : []),
|
|
3258
|
-
...(dynamic ? ["-DSCR_DYNAMIC"] : []),
|
|
3259
|
-
];
|
|
3260
|
-
// Keep sanitizer runtime objects optimized in both postures. Large dev
|
|
3261
|
-
// programs still compile at O0, avoiding an expensive LLVM optimization
|
|
3262
|
-
// pass while retaining ASan and reference-count checks in every runtime unit.
|
|
3263
|
-
const separateSanitizedProgram = sanitize && optimization === "dev";
|
|
3264
|
-
const programCompilerArgs = [
|
|
3265
|
-
...cflags,
|
|
3266
|
-
...(separateSanitizedProgram ? ["-O0"] : []),
|
|
3267
|
-
...(opts.cPath.endsWith(".ll") ? ["-Wno-override-module"] : []),
|
|
3268
|
-
];
|
|
3269
|
-
const programSourceExtension = opts.cPath.endsWith(".ll") ? ".ll" : ".c";
|
|
3270
|
-
const ccName = driver.argv.join(" ");
|
|
3271
|
-
const runClang = async (args) => {
|
|
3272
|
-
try {
|
|
3273
|
-
await execFileAsync(driver.argv[0] ?? "clang", [...driver.argv.slice(1), ...args]);
|
|
3274
|
-
}
|
|
3275
|
-
catch (err) {
|
|
3276
|
-
const stderr = subprocessFailureDetail(err);
|
|
3277
|
-
const guidance = (opts.linkInputs?.length ?? 0) > 0 ||
|
|
3278
|
-
(opts.frameworks?.length ?? 0) > 0 ||
|
|
3279
|
-
(opts.systemLibraries?.length ?? 0) > 0
|
|
3280
|
-
? "This build includes native FFI link inputs. Check that every symbol and system library exists, " +
|
|
3281
|
-
"that archive/object ordering is correct, and that each input matches the selected target."
|
|
3282
|
-
: `Check the supplied native source and the selected ${ccName} toolchain.`;
|
|
3283
|
-
throw new CcCompileError(ccName, stderr, `${ccName} failed compiling ${opts.cPath}.\n` +
|
|
3284
|
-
`${guidance}\n\n${stderr}`);
|
|
3285
|
-
}
|
|
3286
|
-
};
|
|
3287
|
-
const buildExecutable = async (runtimeInput, build = {}) => {
|
|
3288
|
-
if (!separateSanitizedProgram) {
|
|
3289
|
-
await runClang(buildArgs(runtimeInput, build));
|
|
3290
|
-
return;
|
|
3291
|
-
}
|
|
3292
|
-
const stage = await mkdtemp(join(tmpdir(), "scriptc-sanitized-program-"));
|
|
3293
|
-
try {
|
|
3294
|
-
const object = join(stage, "program.o");
|
|
3295
|
-
await runClang([
|
|
3296
|
-
...programCompilerArgs,
|
|
3297
|
-
...(build.compilerVisibleSource === undefined ? [] : [
|
|
3298
|
-
`-ffile-prefix-map=${build.programPath}=${build.compilerVisibleSource}`,
|
|
3299
|
-
"-iquote", dirname(resolve(build.compilerVisibleSource)),
|
|
3300
|
-
]),
|
|
3301
|
-
"-c", build.programPath ?? opts.cPath, "-o", object,
|
|
3302
|
-
]);
|
|
3303
|
-
await runClang(buildArgs(runtimeInput, { programPath: object, outPath: build.outPath ?? opts.outPath }));
|
|
3304
|
-
if (darwinDebugSymbols)
|
|
3305
|
-
await createDarwinDebugSymbols(build.outPath ?? opts.outPath);
|
|
3306
|
-
}
|
|
3307
|
-
finally {
|
|
3308
|
-
await rm(stage, { recursive: true, force: true });
|
|
3309
|
-
}
|
|
3310
|
-
};
|
|
3311
|
-
const runUncachedBuild = async () => {
|
|
3312
|
-
const privateVendorRoot = transientVendorRoot ?? join(tmpdir(), `scriptc-vendor-fallback-${process.pid}-${Math.random().toString(36).slice(2)}`);
|
|
3313
|
-
try {
|
|
3314
|
-
await materializeVendorPrerequisites(undefined, privateVendorRoot);
|
|
3315
|
-
await buildExecutable((p) => p);
|
|
3316
|
-
}
|
|
3317
|
-
finally {
|
|
3318
|
-
await rm(privateVendorRoot, { recursive: true, force: true }).catch(() => undefined);
|
|
3319
|
-
}
|
|
3320
|
-
};
|
|
3321
|
-
let runtimeCompilerInvocation = null;
|
|
3322
|
-
let programCompilerInvocation = null;
|
|
3323
|
-
let payloadMetadata = null;
|
|
3324
|
-
let compileMetadataStamp = null;
|
|
3325
|
-
const compileMetadataKey = persistentCache === null || process.env["SCRIPTC_TEST_TRUST_COMPILER_WRAPPER"] === "1"
|
|
3326
|
-
? null
|
|
3327
|
-
: nativeMetadataKey("compile", [
|
|
3328
|
-
cacheTargetIdentity(driver),
|
|
3329
|
-
toolchainEnv,
|
|
3330
|
-
implicitToolchain ?? "<uncached>",
|
|
3331
|
-
driver.argv,
|
|
3332
|
-
cflags,
|
|
3333
|
-
programCompilerArgs,
|
|
3334
|
-
programSourceExtension,
|
|
3335
|
-
]);
|
|
3336
|
-
if (persistentCache !== null) {
|
|
3337
|
-
try {
|
|
3338
|
-
// These probes inspect disjoint inputs. Start the payload reads here as
|
|
3339
|
-
// well so runtime hashing and clang's dry-run traces overlap instead of
|
|
3340
|
-
// forming a serial prelude before every cache lookup.
|
|
3341
|
-
payloadMetadata = Promise.all([
|
|
3342
|
-
Promise.resolve(toolchainMetadataStamp?.values["compilerVersion"] ??
|
|
3343
|
-
ccVersion(driver.argv, toolchainEnv, true)),
|
|
3344
|
-
localArtifact === null
|
|
3345
|
-
? runtimeFingerprint(rtDir)
|
|
3346
|
-
: Promise.resolve(localArtifact.runtimeHash),
|
|
3347
|
-
localArtifact === null
|
|
3348
|
-
? readFile(opts.cPath)
|
|
3349
|
-
: Promise.resolve(localArtifact.programBytes),
|
|
3350
|
-
]);
|
|
3351
|
-
compileMetadataStamp = compileMetadataKey === null
|
|
3352
|
-
? null
|
|
3353
|
-
: await readNativeMetadataStamp(persistentCache.root, compileMetadataKey);
|
|
3354
|
-
if (compileMetadataStamp !== null) {
|
|
3355
|
-
runtimeCompilerInvocation = compileMetadataStamp.values["runtimeInvocation"] ?? null;
|
|
3356
|
-
programCompilerInvocation = compileMetadataStamp.values["programInvocation"] ?? null;
|
|
3357
|
-
if (runtimeCompilerInvocation === null || programCompilerInvocation === null) {
|
|
3358
|
-
compileMetadataStamp = null;
|
|
3359
|
-
}
|
|
3360
|
-
}
|
|
3361
|
-
if (compileMetadataStamp === null) {
|
|
3362
|
-
const [runtimeInvocation, programInvocation] = await Promise.all([
|
|
3363
|
-
effectiveCompilerInvocationFingerprint(driver, toolchainEnv, cflags),
|
|
3364
|
-
programSourceExtension === ".ll" || separateSanitizedProgram
|
|
3365
|
-
? effectiveCompilerInvocationFingerprint(driver, toolchainEnv, programCompilerArgs, programSourceExtension)
|
|
3366
|
-
: Promise.resolve(null),
|
|
3367
|
-
]);
|
|
3368
|
-
runtimeCompilerInvocation = runtimeInvocation;
|
|
3369
|
-
programCompilerInvocation = programInvocation ?? runtimeInvocation;
|
|
3370
|
-
if (compileMetadataKey !== null) {
|
|
3371
|
-
compileMetadataStamp = await publishNativeMetadataStamp(persistentCache.root, compileMetadataKey, {
|
|
3372
|
-
runtimeInvocation: runtimeCompilerInvocation,
|
|
3373
|
-
programInvocation: programCompilerInvocation,
|
|
3374
|
-
}, [
|
|
3375
|
-
...fingerprintDependencyPaths(runtimeCompilerInvocation),
|
|
3376
|
-
...fingerprintDependencyPaths(programCompilerInvocation),
|
|
3377
|
-
], [runtimeCompilerInvocation, programCompilerInvocation]);
|
|
3378
|
-
}
|
|
3379
|
-
}
|
|
3380
|
-
}
|
|
3381
|
-
catch (error) {
|
|
3382
|
-
// Preserve the uncached build for wrappers that compile successfully but
|
|
3383
|
-
// cannot provide a dry-run trace for the real build flavor.
|
|
3384
|
-
if (cacheWarmOnly) {
|
|
3385
|
-
throw new Error("native cache warming could not validate the compiler invocation", {
|
|
3386
|
-
cause: error,
|
|
3387
|
-
});
|
|
3388
|
-
}
|
|
3389
|
-
persistentCache = null;
|
|
3390
|
-
}
|
|
3391
|
-
}
|
|
3392
|
-
if (persistentCache === null) {
|
|
3393
|
-
// The direct uncached command is the source-of-truth executable recipe.
|
|
3394
|
-
await runUncachedBuild();
|
|
3395
|
-
return;
|
|
3396
|
-
}
|
|
3397
|
-
let cv;
|
|
3398
|
-
let fingerprint;
|
|
3399
|
-
let cBytes;
|
|
3400
|
-
try {
|
|
3401
|
-
[cv, fingerprint, cBytes] = await (payloadMetadata ?? Promise.all([
|
|
3402
|
-
ccVersion(driver.argv, toolchainEnv, true),
|
|
3403
|
-
runtimeFingerprint(rtDir),
|
|
3404
|
-
readFile(opts.cPath),
|
|
3405
|
-
]));
|
|
3406
|
-
}
|
|
3407
|
-
catch (error) {
|
|
3408
|
-
// A version/fingerprint probe is an optimization boundary. If the compiler
|
|
3409
|
-
// itself can still compile, preserve the pre-cache behavior instead of
|
|
3410
|
-
// surfacing a metadata command's failure as the build result.
|
|
3411
|
-
if (cacheWarmOnly) {
|
|
3412
|
-
throw new Error("native cache warming could not validate cache inputs", {
|
|
3413
|
-
cause: error,
|
|
3414
|
-
});
|
|
3415
|
-
}
|
|
3416
|
-
await runUncachedBuild();
|
|
3417
|
-
return;
|
|
3418
|
-
}
|
|
3419
|
-
// Caller-supplied native inputs can all hide mutable dependencies: `-l`
|
|
3420
|
-
// resolves through ambient search paths, while a thin archive or linker
|
|
3421
|
-
// script can retain identical top-level bytes as its referenced files are
|
|
3422
|
-
// rebuilt. Keep the safe runtime-object cache, but force a fresh final link
|
|
3423
|
-
// whenever the caller supplies either form.
|
|
3424
|
-
let cacheCompleteArtifact = !cacheWarmOnly &&
|
|
3425
|
-
cachePolicy.completeArtifacts &&
|
|
3426
|
-
(opts.linkInputs?.length ?? 0) === 0 &&
|
|
3427
|
-
(opts.frameworks?.length ?? 0) === 0 &&
|
|
3428
|
-
(opts.systemLibraries?.length ?? 0) === 0;
|
|
3429
|
-
let programDependencies = null;
|
|
3430
|
-
const linkProbeArgs = [
|
|
3431
|
-
...(sanitize ? ["-fsanitize=address"] : []),
|
|
3432
|
-
...threadArgs,
|
|
3433
|
-
...(targetPlatform(driver) === "win32"
|
|
3434
|
-
? ["-ladvapi32", "-liphlpapi", "-lws2_32"]
|
|
3435
|
-
: []),
|
|
3436
|
-
...(tls && targetPlatform(driver) === "win32" ? ["-lbcrypt"] : []),
|
|
3437
|
-
...(tlsCa && targetPlatform(driver) === "win32" ? ["-lcrypt32"] : []),
|
|
3438
|
-
...(curlFetch && driver.target === null ? ["-lcurl"] : []),
|
|
3439
|
-
...(dynamic && !driver.linkArgs.includes("-lm") ? ["-lm"] : []),
|
|
3440
|
-
...(((opts.zlib ?? false) || nativeFetch) && !isZigDriver(driver) ? ["-lz"] : []),
|
|
3441
|
-
...driver.linkArgs,
|
|
3442
|
-
...executableLinkFlags,
|
|
3443
|
-
];
|
|
3444
|
-
// Both the wrapper dry run and dependency trace need the real build's
|
|
3445
|
-
// compile/link flag shape: wrappers commonly inject flags or native inputs
|
|
3446
|
-
// conditionally on optimization, sanitizer, dynamic, or platform switches.
|
|
3447
|
-
const effectiveLinkInvocationArgs = [
|
|
3448
|
-
...(separateSanitizedProgram ? cflags : programCompilerArgs),
|
|
3449
|
-
...(targetPlatform(driver) === "win32"
|
|
3450
|
-
? ["-ladvapi32", "-liphlpapi", "-lws2_32"]
|
|
3451
|
-
: []),
|
|
3452
|
-
...(tls && targetPlatform(driver) === "win32" ? ["-lbcrypt"] : []),
|
|
3453
|
-
...(tlsCa && targetPlatform(driver) === "win32" ? ["-lcrypt32"] : []),
|
|
3454
|
-
...(curlStubDir !== null ? [`-L${curlStubDir}`] : []),
|
|
3455
|
-
...(curlFetch && driver.target === null ? ["-lcurl"] : []),
|
|
3456
|
-
...(dynamic && !driver.linkArgs.includes("-lm") ? ["-lm"] : []),
|
|
3457
|
-
...(dynamic && targetPlatform(driver) === "win32"
|
|
3458
|
-
? ["-Wl,--stack,8388608"]
|
|
3459
|
-
: []),
|
|
3460
|
-
...(((opts.zlib ?? false) || nativeFetch) && !isZigDriver(driver) ? ["-lz"] : []),
|
|
3461
|
-
...driver.linkArgs,
|
|
3462
|
-
...executableLinkFlags,
|
|
3463
|
-
];
|
|
3464
|
-
// A complete hit is checked before cross-target curl's generated import stub
|
|
3465
|
-
// is materialized. Its -L spelling still joins the dry-run identity, while
|
|
3466
|
-
// the real trace omits only that not-yet-existing scriptc-owned directory.
|
|
3467
|
-
const linkTraceInvocationArgs = curlStubDir === null
|
|
3468
|
-
? effectiveLinkInvocationArgs
|
|
3469
|
-
: effectiveLinkInvocationArgs.filter((arg) => arg !== `-L${curlStubDir}`);
|
|
3470
|
-
let implicitLinker = null;
|
|
3471
|
-
let preBuildDependencies = null;
|
|
3472
|
-
let localArtifactDependencyPaths = null;
|
|
3473
|
-
let linkMetadataStamp = null;
|
|
3474
|
-
const linkMetadataKey = process.env["SCRIPTC_TEST_TRUST_COMPILER_WRAPPER"] === "1"
|
|
3475
|
-
? null
|
|
3476
|
-
: nativeMetadataKey("link", [
|
|
3477
|
-
cacheTargetIdentity(driver),
|
|
3478
|
-
toolchainEnv,
|
|
3479
|
-
implicitToolchain ?? "<uncached>",
|
|
3480
|
-
runtimeCompilerInvocation ?? "<uncached>",
|
|
3481
|
-
programCompilerInvocation ?? "<uncached>",
|
|
3482
|
-
driver.argv,
|
|
3483
|
-
linkProbeArgs,
|
|
3484
|
-
effectiveLinkInvocationArgs,
|
|
3485
|
-
linkTraceInvocationArgs,
|
|
3486
|
-
...(programShardMergeIdentity === null ? [] : [programShardMergeIdentity]),
|
|
3487
|
-
]);
|
|
3488
|
-
if (cacheCompleteArtifact) {
|
|
3489
|
-
try {
|
|
3490
|
-
// Header discovery and linker tracing are independent subprocess trees.
|
|
3491
|
-
// Running them together removes one complete probe round-trip from both
|
|
3492
|
-
// cache hits and ordinary edit/build misses without changing either key.
|
|
3493
|
-
linkMetadataStamp = linkMetadataKey === null
|
|
3494
|
-
? null
|
|
3495
|
-
: await readNativeMetadataStamp(persistentCache.root, linkMetadataKey);
|
|
3496
|
-
if (linkMetadataStamp !== null) {
|
|
3497
|
-
implicitLinker = linkMetadataStamp.values["implicitLinker"] ?? null;
|
|
3498
|
-
if (implicitLinker === null)
|
|
3499
|
-
linkMetadataStamp = null;
|
|
3500
|
-
}
|
|
3501
|
-
[programDependencies, implicitLinker] = await Promise.all([
|
|
3502
|
-
translationUnitDependencyFingerprint(driver, programCompilerArgs, opts.cPath, cBytes, toolchainEnv),
|
|
3503
|
-
linkMetadataStamp === null
|
|
3504
|
-
? implicitLinkerFingerprint(driver, toolchainEnv, linkProbeArgs, effectiveLinkInvocationArgs, linkTraceInvocationArgs)
|
|
3505
|
-
: Promise.resolve(implicitLinker),
|
|
3506
|
-
]);
|
|
3507
|
-
if (linkMetadataStamp === null && linkMetadataKey !== null) {
|
|
3508
|
-
linkMetadataStamp = await publishNativeMetadataStamp(persistentCache.root, linkMetadataKey, { implicitLinker }, fingerprintDependencyPaths(implicitLinker), [implicitLinker]);
|
|
3509
|
-
}
|
|
3510
|
-
preBuildDependencies = await snapshotLocalArtifactDependencies([
|
|
3511
|
-
...(await runtimeFingerprintInputPaths(rtDir)),
|
|
3512
|
-
...(toolchainMetadataStamp?.dependencies.map((dependency) => dependency.path) ??
|
|
3513
|
-
fingerprintDependencyPaths(implicitToolchain)),
|
|
3514
|
-
...(compileMetadataStamp?.dependencies.map((dependency) => dependency.path) ?? []),
|
|
3515
|
-
...(linkMetadataStamp?.dependencies.map((dependency) => dependency.path) ??
|
|
3516
|
-
fingerprintDependencyPaths(implicitLinker)),
|
|
3517
|
-
...fingerprintDependencyPaths(programDependencies),
|
|
3518
|
-
...(programShardMergeIdentity === null
|
|
3519
|
-
? []
|
|
3520
|
-
: fingerprintDependencyPaths(programShardMergeIdentity)),
|
|
3521
|
-
]);
|
|
3522
|
-
// Every content-bearing fingerprint was computed before this metadata
|
|
3523
|
-
// snapshot. Re-read those exact files now so the snapshot cannot certify
|
|
3524
|
-
// bytes that changed after hashing but before the final compile starts.
|
|
3525
|
-
if (!(await fingerprintDependenciesStillMatch([
|
|
3526
|
-
implicitToolchain,
|
|
3527
|
-
runtimeCompilerInvocation,
|
|
3528
|
-
programCompilerInvocation,
|
|
3529
|
-
implicitLinker,
|
|
3530
|
-
programDependencies,
|
|
3531
|
-
...(programShardMergeIdentity === null ? [] : [programShardMergeIdentity]),
|
|
3532
|
-
])) ||
|
|
3533
|
-
await runtimeFingerprint(rtDir).catch(() => null) !== fingerprint ||
|
|
3534
|
-
!(await Promise.all([toolchainMetadataStamp, compileMetadataStamp, linkMetadataStamp]
|
|
3535
|
-
.filter((stamp) => stamp !== null)
|
|
3536
|
-
.map((stamp) => nativeArtifactDependenciesStillMatch(stamp.dependencies)))).every(Boolean)) {
|
|
3537
|
-
throw new CacheInputsChangedError();
|
|
3538
|
-
}
|
|
3539
|
-
if (localArtifact !== null) {
|
|
3540
|
-
// Native metadata stamps persist their dependency paths across CLI
|
|
3541
|
-
// processes; fingerprintDependencyPaths deliberately does not. Build
|
|
3542
|
-
// the output-local stamp from this complete validated snapshot so a
|
|
3543
|
-
// source edit in a fresh process cannot replace it with only the two
|
|
3544
|
-
// process-local fallback paths.
|
|
3545
|
-
localArtifactDependencyPaths = [
|
|
3546
|
-
localArtifact.compilerPath,
|
|
3547
|
-
dirname(resolve(opts.cPath)),
|
|
3548
|
-
...preBuildDependencies.map((dependency) => dependency.path),
|
|
3549
|
-
];
|
|
3550
|
-
}
|
|
3551
|
-
}
|
|
3552
|
-
catch {
|
|
3553
|
-
// Program-header discovery and linker tracing are both required for a
|
|
3554
|
-
// complete hit. Runtime objects remain safely cacheable if either probe
|
|
3555
|
-
// is unavailable.
|
|
3556
|
-
cacheCompleteArtifact = false;
|
|
3557
|
-
preBuildDependencies = null;
|
|
3558
|
-
localArtifactDependencyPaths = null;
|
|
3559
|
-
}
|
|
3560
|
-
}
|
|
3561
|
-
// Warm-only builds deliberately do not need linker/header identity for a
|
|
3562
|
-
// complete executable, but they still need a stable runtime-object key.
|
|
3563
|
-
// The ordinary complete-artifact path computes these values above.
|
|
3564
|
-
if (cacheWarmOnly && preBuildDependencies === null) {
|
|
3565
|
-
try {
|
|
3566
|
-
const [dependencies, invocation] = await Promise.all([
|
|
3567
|
-
translationUnitDependencyFingerprint(driver, programCompilerArgs, opts.cPath, cBytes, toolchainEnv),
|
|
3568
|
-
effectiveCompilerInvocationFingerprint(driver, toolchainEnv, cflags),
|
|
3569
|
-
]);
|
|
3570
|
-
programDependencies = dependencies;
|
|
3571
|
-
if (runtimeCompilerInvocation === null)
|
|
3572
|
-
runtimeCompilerInvocation = invocation;
|
|
3573
|
-
}
|
|
3574
|
-
catch (error) {
|
|
3575
|
-
throw new Error("native cache warming could not validate runtime-object inputs", {
|
|
3576
|
-
cause: error,
|
|
3577
|
-
});
|
|
3578
|
-
}
|
|
3579
|
-
}
|
|
3580
|
-
const binDir = join(persistentCache.root, "bin");
|
|
3581
|
-
let keyHex = null;
|
|
3582
|
-
let cachedBin = null;
|
|
3583
|
-
if (cacheCompleteArtifact) {
|
|
3584
|
-
// The key sees the full command line with the two program-specific paths
|
|
3585
|
-
// normalized out. The C bytes are hashed separately; Darwin additionally
|
|
3586
|
-
// keys the output basename because ld embeds it in the ad-hoc signature.
|
|
3587
|
-
// Runtime and vendor paths stay verbatim — their contents are covered by
|
|
3588
|
-
// the fingerprint and the pin.
|
|
3589
|
-
const identityArgs = buildArgs((p) => p).map((a) => a === opts.cPath ? "<program.c>" : a === opts.outPath ? "<out>" : a);
|
|
3590
|
-
const key = createHash("sha256")
|
|
3591
|
-
// Sharded dev builds need the v10 identity below. Canonical single-TU
|
|
3592
|
-
// builds retain v9 so adding the opt-in mode does not evict release
|
|
3593
|
-
// binaries compiled by an earlier scriptc.
|
|
3594
|
-
.update(programShards === null ? "bin-v9\0" : "bin-v10\0")
|
|
3595
|
-
.update(cacheTargetIdentity(driver)).update("\0")
|
|
3596
|
-
.update(toolchainEnv).update("\0")
|
|
3597
|
-
.update(implicitToolchain).update("\0")
|
|
3598
|
-
.update(runtimeCompilerInvocation).update("\0")
|
|
3599
|
-
.update(programCompilerInvocation).update("\0")
|
|
3600
|
-
.update(implicitLinker).update("\0")
|
|
3601
|
-
.update(programDependencies).update("\0")
|
|
3602
|
-
.update(persistentCache.identity).update("\0")
|
|
3603
|
-
// Preserve both the spelling clang sees (__FILE__) and the location used
|
|
3604
|
-
// to resolve relative includes. The top-level bytes are not sufficient.
|
|
3605
|
-
.update(opts.cPath).update("\0")
|
|
3606
|
-
.update(resolve(opts.cPath)).update("\0")
|
|
3607
|
-
.update(targetPlatform(driver) === "darwin" ? basename(opts.outPath) : "<out>").update("\0")
|
|
3608
|
-
// The driver spelling joins the version string: `zig cc --version`
|
|
3609
|
-
// reports the clang underneath and could otherwise collide with a
|
|
3610
|
-
// same-version host clang.
|
|
3611
|
-
.update(ccName).update("\0")
|
|
3612
|
-
.update(cv).update("\0")
|
|
3613
|
-
.update(fingerprint).update("\0")
|
|
3614
|
-
.update(identityArgs.join("\x1f")).update("\0")
|
|
3615
|
-
.update(cBytes);
|
|
3616
|
-
if (programShards !== null) {
|
|
3617
|
-
updateProgramShardCacheIdentity(key, programShards, programPublicSymbols, programShardMergeIdentity ?? undefined);
|
|
3618
|
-
}
|
|
3619
|
-
keyHex = key.digest("hex");
|
|
3620
|
-
cachedBin = join(binDir, keyHex);
|
|
3621
|
-
const tmpOut = privateSiblingPath(opts.outPath, "bin-hit");
|
|
3622
|
-
try {
|
|
3623
|
-
// NEVER copy over outPath in place: overwriting an already-executed
|
|
3624
|
-
// signed binary invalidates the kernel's per-vnode code-signature cache
|
|
3625
|
-
// on macOS and the next exec dies with SIGKILL. Copy to a fresh inode
|
|
3626
|
-
// and rename it into place instead.
|
|
3627
|
-
if (!(await copyValidCachedFile(cachedBin, tmpOut))) {
|
|
3628
|
-
throw new Error("invalid cached executable");
|
|
3629
|
-
}
|
|
3630
|
-
if (darwinDebugSymbols) {
|
|
3631
|
-
const cachedSymbols = `${cachedBin}.dsym`;
|
|
3632
|
-
const symbolsCopy = privateSiblingPath(opts.outPath, "dsym-hit");
|
|
3633
|
-
try {
|
|
3634
|
-
if (!(await copyValidCachedFile(cachedSymbols, symbolsCopy)))
|
|
3635
|
-
throw new Error("invalid cached dSYM");
|
|
3636
|
-
const bytes = await readFile(symbolsCopy);
|
|
3637
|
-
// A concurrent publisher may replace either payload. The sidecar
|
|
3638
|
-
// also names its binary digest so a mixed pair always misses.
|
|
3639
|
-
if (bytes.subarray(0, 32).toString("hex") !== await fileDigest(tmpOut))
|
|
3640
|
-
throw new Error("mismatched cached dSYM");
|
|
3641
|
-
await installDarwinDebugSymbols(bytes.subarray(32), opts.outPath);
|
|
3642
|
-
}
|
|
3643
|
-
finally {
|
|
3644
|
-
await rm(symbolsCopy, { force: true });
|
|
3645
|
-
}
|
|
3646
|
-
}
|
|
3647
|
-
// Match a fresh linker output under the caller's current umask. Reusing a
|
|
3648
|
-
// cache entry populated by a less restrictive shell must not widen access.
|
|
3649
|
-
await chmod(tmpOut, 0o777 & ~process.umask());
|
|
3650
|
-
await rename(tmpOut, opts.outPath);
|
|
3651
|
-
if (localArtifact !== null && localArtifactDependencyPaths !== null) {
|
|
3652
|
-
const stamp = await publishLocalArtifactStamp(localArtifact.stampPath, opts.outPath, localArtifact.key, localArtifactDependencyPaths, [dirname(resolve(opts.cPath))], [persistentCache.root], darwinDebugSymbols).catch(() => null);
|
|
3653
|
-
if (stamp !== null) {
|
|
3654
|
-
await opts.onArtifactReady?.({ dependencies: stamp.dependencies }).catch(() => undefined);
|
|
3655
|
-
}
|
|
3656
|
-
}
|
|
3657
|
-
return; // hit: the program/runtime payload compile and link were skipped
|
|
3658
|
-
}
|
|
3659
|
-
catch {
|
|
3660
|
-
await rm(tmpOut, { force: true }).catch(() => undefined);
|
|
3661
|
-
/* miss — build below, then publish */
|
|
3662
|
-
}
|
|
3663
|
-
}
|
|
3664
|
-
// Miss: link the program's own TU against cached per-flavor runtime objects.
|
|
3665
|
-
// Collect the runtime sources this build actually compiles (the same
|
|
3666
|
-
// conditionals as the command line, by construction).
|
|
3667
|
-
const rtInputs = [];
|
|
3668
|
-
buildArgs((p) => {
|
|
3669
|
-
rtInputs.push(p);
|
|
3670
|
-
return p;
|
|
3671
|
-
});
|
|
3672
|
-
const buildDir = await mkdtemp(join(tmpdir(), "scriptc-cache-build-"));
|
|
3673
|
-
try {
|
|
3674
|
-
await materializeVendorPrerequisites(join(buildDir, "vendor-inputs"));
|
|
3675
|
-
// Freeze the exact bytes used for the key. Generated TUs live at stable
|
|
3676
|
-
// paths under .scriptc/, so two builds can otherwise overwrite that path
|
|
3677
|
-
// between hashing and clang and publish one invocation's code under the
|
|
3678
|
-
// other's key. The prefix map preserves the original __FILE__/debug-file
|
|
3679
|
-
// spelling while clang reads this invocation-private snapshot.
|
|
3680
|
-
const programPath = join(buildDir, `program${programSourceExtension}`);
|
|
3681
|
-
// Preserve the caller-visible basename while keeping Darwin builds on a
|
|
3682
|
-
// private inode: ld uses this spelling as the embedded ad-hoc signing
|
|
3683
|
-
// identifier. Other targets retain the basename-independent cache key.
|
|
3684
|
-
const privateOut = join(buildDir, targetPlatform(driver) === "darwin"
|
|
3685
|
-
? basename(opts.outPath)
|
|
3686
|
-
: process.platform === "win32"
|
|
3687
|
-
? "artifact.exe"
|
|
3688
|
-
: "artifact");
|
|
3689
|
-
await writeFile(programPath, cBytes);
|
|
3690
|
-
let objects = null;
|
|
3691
|
-
let cacheInputsStable = true;
|
|
3692
|
-
let strictObjectVerification = null;
|
|
3693
|
-
const objectImplicitToolchainStillMatches = () => {
|
|
3694
|
-
if (preBuildDependencies !== null) {
|
|
3695
|
-
return nativeArtifactDependenciesStillMatch(preBuildDependencies);
|
|
3696
|
-
}
|
|
3697
|
-
// Complete-artifact caching can be disabled by caller-owned native
|
|
3698
|
-
// inputs while the safe runtime-object tier remains active. Preserve its
|
|
3699
|
-
// strict discovery fallback in that posture.
|
|
3700
|
-
strictObjectVerification ??= Promise.all([
|
|
3701
|
-
runtimeFingerprint(rtDir),
|
|
3702
|
-
implicitToolchainFingerprint(driver, toolchainEnv),
|
|
3703
|
-
effectiveCompilerInvocationFingerprint(driver, toolchainEnv, cflags),
|
|
3704
|
-
]).then(([currentRuntime, currentImplicit, currentInvocation]) => currentRuntime === fingerprint &&
|
|
3705
|
-
currentImplicit === implicitToolchain &&
|
|
3706
|
-
currentInvocation === runtimeCompilerInvocation, () => false);
|
|
3707
|
-
return strictObjectVerification;
|
|
3708
|
-
};
|
|
3709
|
-
try {
|
|
3710
|
-
const cached = await ensureRuntimeObjects(persistentCache.root, driver.argv, cflags, rtInputs, `obj-v5\0${cacheTargetIdentity(driver)}\0${toolchainEnv}\0${implicitToolchain}\0${runtimeCompilerInvocation}\0${ccName}\0${cv}\0${fingerprint}\0`, async () => await objectImplicitToolchainStillMatches(), cacheWarmPaths);
|
|
3711
|
-
objects = await stageRuntimeObjects(cached, join(buildDir, "runtime-objects"));
|
|
3712
|
-
}
|
|
3713
|
-
catch (err) {
|
|
3714
|
-
if (err instanceof CacheInputsChangedError)
|
|
3715
|
-
cacheInputsStable = false;
|
|
3716
|
-
if (cacheWarmOnly) {
|
|
3717
|
-
throw new Error("native cache warming could not persist runtime objects", {
|
|
3718
|
-
cause: err,
|
|
3719
|
-
});
|
|
3720
|
-
}
|
|
3721
|
-
objects = null; // cache trouble is never a build failure
|
|
3722
|
-
}
|
|
3723
|
-
const compileShardedProgram = async () => {
|
|
3724
|
-
if (programShards === null || programPublicSymbols === undefined ||
|
|
3725
|
-
programCompilerInvocation === null || implicitCompileToolchain === null)
|
|
3726
|
-
return null;
|
|
3727
|
-
try {
|
|
3728
|
-
const programDependencyHash = programDependencies ??
|
|
3729
|
-
await translationUnitDependencyFingerprint(driver, programCompilerArgs, opts.cPath, cBytes, toolchainEnv);
|
|
3730
|
-
const stem = basename(opts.cPath, ".ll");
|
|
3731
|
-
const entries = programShards.map((shard, index) => {
|
|
3732
|
-
const sourcePath = join(buildDir, shard.name);
|
|
3733
|
-
const staged = join(buildDir, `${stem}.program-${index.toString().padStart(3, "0")}.o`);
|
|
3734
|
-
const key = createHash("sha256")
|
|
3735
|
-
.update("exe-program-shard-v1\0")
|
|
3736
|
-
.update(cacheTargetIdentity(driver)).update("\0")
|
|
3737
|
-
.update(toolchainEnv).update("\0")
|
|
3738
|
-
.update(implicitCompileToolchain).update("\0")
|
|
3739
|
-
.update(programCompilerInvocation).update("\0")
|
|
3740
|
-
.update(persistentCache.identity).update("\0")
|
|
3741
|
-
.update(driver.argv.join("\x1f")).update("\0")
|
|
3742
|
-
.update(cv).update("\0")
|
|
3743
|
-
.update(fingerprint).update("\0")
|
|
3744
|
-
.update(programDependencyHash).update("\0")
|
|
3745
|
-
.update(programCompilerArgs.join("\x1f")).update("\0")
|
|
3746
|
-
.update(opts.cPath).update("\0")
|
|
3747
|
-
.update(resolve(opts.cPath)).update("\0")
|
|
3748
|
-
.update(shard.name).update("\0")
|
|
3749
|
-
.update(shard.source)
|
|
3750
|
-
.digest("hex");
|
|
3751
|
-
return {
|
|
3752
|
-
...shard,
|
|
3753
|
-
sourcePath,
|
|
3754
|
-
staged,
|
|
3755
|
-
cachePath: join(persistentCache.root, "program-shard", key),
|
|
3756
|
-
missed: false,
|
|
3757
|
-
};
|
|
3758
|
-
});
|
|
3759
|
-
const shardWidth = Math.min(8, availableParallelism());
|
|
3760
|
-
for (let i = 0; i < entries.length; i += shardWidth) {
|
|
3761
|
-
await Promise.all(entries.slice(i, i + shardWidth).map(async (entry) => {
|
|
3762
|
-
await writeFile(entry.sourcePath, entry.source);
|
|
3763
|
-
if (await copyValidCachedFile(entry.cachePath, entry.staged))
|
|
3764
|
-
return;
|
|
3765
|
-
entry.missed = true;
|
|
3766
|
-
await runClang([
|
|
3767
|
-
...programCompilerArgs,
|
|
3768
|
-
`-ffile-prefix-map=${entry.sourcePath}=${opts.cPath}`,
|
|
3769
|
-
"-c",
|
|
3770
|
-
entry.sourcePath,
|
|
3771
|
-
"-o",
|
|
3772
|
-
entry.staged,
|
|
3773
|
-
]);
|
|
3774
|
-
}));
|
|
3775
|
-
}
|
|
3776
|
-
const arArgv = isZigDriver(driver) ? [driver.argv[0], "ar"] : ["ar"];
|
|
3777
|
-
const merged = await localizeLibraryObjects(driver, arArgv, buildDir, entries.map((entry) => entry.staged), [], programPublicSymbols, `${stem}.program`);
|
|
3778
|
-
const publishable = entries.filter((entry) => entry.missed);
|
|
3779
|
-
if (publishable.length > 0) {
|
|
3780
|
-
try {
|
|
3781
|
-
const [currentRuntime, currentFingerprints, currentInvocation, currentDependencies, currentCompiler, currentMerge] = await Promise.all([
|
|
3782
|
-
runtimeFingerprint(rtDir),
|
|
3783
|
-
implicitToolchainFingerprints(driver, toolchainEnv),
|
|
3784
|
-
effectiveCompilerInvocationFingerprint(driver, toolchainEnv, programCompilerArgs, ".ll"),
|
|
3785
|
-
translationUnitDependencyFingerprint(driver, programCompilerArgs, opts.cPath, cBytes, toolchainEnv),
|
|
3786
|
-
ccVersion(driver.argv, toolchainEnv, true),
|
|
3787
|
-
resolveProgramShardMergeIdentity(driver),
|
|
3788
|
-
]);
|
|
3789
|
-
if (currentRuntime === fingerprint &&
|
|
3790
|
-
currentFingerprints.compile === implicitCompileToolchain &&
|
|
3791
|
-
currentInvocation === programCompilerInvocation &&
|
|
3792
|
-
currentDependencies === programDependencyHash &&
|
|
3793
|
-
currentCompiler === cv &&
|
|
3794
|
-
currentMerge === programShardMergeIdentity) {
|
|
3795
|
-
await Promise.all(publishable.map((entry) => publishCachedFile(entry.staged, entry.cachePath)));
|
|
3796
|
-
}
|
|
3797
|
-
}
|
|
3798
|
-
catch {
|
|
3799
|
-
// The merged object is valid for this invocation; publication is
|
|
3800
|
-
// only an optimization for later edits.
|
|
3801
|
-
}
|
|
3802
|
-
}
|
|
3803
|
-
return merged;
|
|
3804
|
-
}
|
|
3805
|
-
catch {
|
|
3806
|
-
// Sharding is an optimization. Compile/link the canonical TU below if
|
|
3807
|
-
// a target tool, one shard, or relocatable merge is unavailable.
|
|
3808
|
-
return null;
|
|
3809
|
-
}
|
|
3810
|
-
};
|
|
3811
|
-
const shardedProgramObject = await compileShardedProgram();
|
|
3812
|
-
if (programShards !== null && shardedProgramObject === null) {
|
|
3813
|
-
// The canonical TU fallback is valid output, but it must not populate a
|
|
3814
|
-
// cache key describing a successful shard projection/merge.
|
|
3815
|
-
cacheInputsStable = false;
|
|
3816
|
-
}
|
|
3817
|
-
await buildExecutable((p) => objects?.get(p) ?? p, shardedProgramObject === null
|
|
3818
|
-
? { programPath, outPath: privateOut, compilerVisibleSource: opts.cPath }
|
|
3819
|
-
: { programPath: shardedProgramObject, outPath: privateOut });
|
|
3820
|
-
if (darwinDebugSymbols) {
|
|
3821
|
-
// clang invokes dsymutil when compiling a source TU while linking.
|
|
3822
|
-
// A sharded program is already an object, so run it explicitly while
|
|
3823
|
-
// the merged object and staged runtime inputs still exist.
|
|
3824
|
-
if (shardedProgramObject !== null)
|
|
3825
|
-
await createDarwinDebugSymbols(privateOut);
|
|
3826
|
-
await installDarwinDebugSymbols(await readDarwinDebugSymbols(privateOut), opts.outPath);
|
|
3827
|
-
}
|
|
3828
|
-
await installArtifact(privateOut, opts.outPath);
|
|
3829
|
-
if (cachedBin !== null && keyHex !== null) {
|
|
3830
|
-
// Metadata comparison catches ordinary changes cheaply, but cannot by
|
|
3831
|
-
// itself prove the snapshot was taken from the same bytes hashed into
|
|
3832
|
-
// the key. Recompute every content-bearing identity after the final link
|
|
3833
|
-
// so a header/SDK/compiler change in either pre-build gap cannot publish
|
|
3834
|
-
// new output under an old key.
|
|
3835
|
-
const [currentRuntime, currentImplicit, currentRuntimeInvocation, currentProgramInvocation, currentProgramDependencies, currentLinker, currentCompiler, currentProgramShardMerge] = await Promise.all([
|
|
3836
|
-
runtimeFingerprint(rtDir).catch(() => null),
|
|
3837
|
-
implicitToolchainFingerprint(driver, toolchainEnv).catch(() => null),
|
|
3838
|
-
effectiveCompilerInvocationFingerprint(driver, toolchainEnv, cflags).catch(() => null),
|
|
3839
|
-
effectiveCompilerInvocationFingerprint(driver, toolchainEnv, programCompilerArgs, programSourceExtension).catch(() => null),
|
|
3840
|
-
translationUnitDependencyFingerprint(driver, programCompilerArgs, opts.cPath, cBytes, toolchainEnv).catch(() => null),
|
|
3841
|
-
implicitLinkerFingerprint(driver, toolchainEnv, linkProbeArgs, effectiveLinkInvocationArgs, linkTraceInvocationArgs).catch(() => null),
|
|
3842
|
-
ccVersion(driver.argv, toolchainEnv, true).catch(() => null),
|
|
3843
|
-
programShards === null
|
|
3844
|
-
? Promise.resolve(null)
|
|
3845
|
-
: resolveProgramShardMergeIdentity(driver).catch(() => null),
|
|
3846
|
-
]);
|
|
3847
|
-
cacheInputsStable =
|
|
3848
|
-
cacheInputsStable &&
|
|
3849
|
-
preBuildDependencies !== null &&
|
|
3850
|
-
await nativeArtifactDependenciesStillMatch(preBuildDependencies) &&
|
|
3851
|
-
currentRuntime === fingerprint &&
|
|
3852
|
-
currentImplicit === implicitToolchain &&
|
|
3853
|
-
currentRuntimeInvocation === runtimeCompilerInvocation &&
|
|
3854
|
-
currentProgramInvocation === programCompilerInvocation &&
|
|
3855
|
-
currentProgramDependencies === programDependencies &&
|
|
3856
|
-
currentLinker === implicitLinker &&
|
|
3857
|
-
currentCompiler === cv &&
|
|
3858
|
-
currentProgramShardMerge === programShardMergeIdentity;
|
|
3859
|
-
}
|
|
3860
|
-
if (cachedBin !== null && keyHex !== null && cacheInputsStable) {
|
|
3861
|
-
try {
|
|
3862
|
-
// Cache artifacts are data, never execution targets. Publication keeps
|
|
3863
|
-
// generated code and embedded literals private; the hit path reapplies
|
|
3864
|
-
// the caller's current executable mode to its destination copy.
|
|
3865
|
-
await publishCachedFile(privateOut, cachedBin);
|
|
3866
|
-
if (darwinDebugSymbols) {
|
|
3867
|
-
const symbols = privateSiblingPath(privateOut, "debug-symbols");
|
|
3868
|
-
await writeFile(symbols, Buffer.concat([
|
|
3869
|
-
Buffer.from(await fileDigest(privateOut), "hex"),
|
|
3870
|
-
await readDarwinDebugSymbols(privateOut),
|
|
3871
|
-
]));
|
|
3872
|
-
await publishCachedFile(symbols, `${cachedBin}.dsym`);
|
|
3873
|
-
}
|
|
3874
|
-
}
|
|
3875
|
-
catch {
|
|
3876
|
-
/* publishing is best-effort */
|
|
3877
|
-
}
|
|
3878
|
-
}
|
|
3879
|
-
if (localArtifact !== null &&
|
|
3880
|
-
localArtifactDependencyPaths !== null &&
|
|
3881
|
-
cacheCompleteArtifact &&
|
|
3882
|
-
cacheInputsStable) {
|
|
3883
|
-
const stamp = await publishLocalArtifactStamp(localArtifact.stampPath, opts.outPath, localArtifact.key, localArtifactDependencyPaths, [dirname(resolve(opts.cPath))], [persistentCache.root], darwinDebugSymbols).catch(() => null);
|
|
3884
|
-
if (stamp !== null) {
|
|
3885
|
-
await opts.onArtifactReady?.({ dependencies: stamp.dependencies }).catch(() => undefined);
|
|
3886
|
-
}
|
|
3887
|
-
}
|
|
3888
|
-
}
|
|
3889
|
-
finally {
|
|
3890
|
-
await rm(buildDir, { recursive: true, force: true }).catch(() => undefined);
|
|
3891
|
-
}
|
|
3892
|
-
// Runtime-object population is itself a cache write, including on builds
|
|
3893
|
-
// whose native link inputs disable complete-artifact publication.
|
|
3894
|
-
await pruneCache(persistentCache.root, cacheWarmPaths).catch(() => undefined);
|
|
3895
|
-
}
|
|
3896
|
-
export async function compileC(opts) {
|
|
3897
|
-
clearCcCaches();
|
|
3898
|
-
await compileCInternal(opts, false);
|
|
3899
|
-
if (targetPlatform(resolveCc()) === "darwin" && !needsDarwinDebugSymbols("darwin", opts.optimization, opts.strip)) {
|
|
3900
|
-
await rm(`${opts.outPath}.dSYM`, { recursive: true, force: true });
|
|
3901
|
-
}
|
|
3902
|
-
}
|
|
3903
|
-
export function supportedNativeCacheWarmProfiles(driver) {
|
|
3904
|
-
if (isMobileTarget(driver.target) || targetPlatform(driver) === "wasi")
|
|
3905
|
-
return [];
|
|
3906
|
-
return ["runtime", "tls", "dynamic"];
|
|
3907
|
-
}
|
|
3908
|
-
/** Populate expensive native prerequisites against the current compiler,
|
|
3909
|
-
* target, SDK, and environment. The resulting entries use the exact same
|
|
3910
|
-
* identities and validators as ordinary builds; this merely pays their cost
|
|
3911
|
-
* before a developer's first program asks for them. Synthetic link products
|
|
3912
|
-
* are discarded and never enter the complete-executable cache. */
|
|
3913
|
-
export async function warmNativeCaches(options = {}) {
|
|
3914
|
-
clearCcCaches();
|
|
3915
|
-
const cacheRoot = cacheRootDir();
|
|
3916
|
-
if (cacheRoot === null) {
|
|
3917
|
-
throw new Error("the native build cache is disabled (unset SCRIPTC_NO_CACHE and use a non-empty SCRIPTC_CACHE_DIR)");
|
|
3918
|
-
}
|
|
3919
|
-
await ensurePrivateCacheRoot(cacheRoot, process.env["SCRIPTC_CACHE_DIR"] === undefined);
|
|
3920
|
-
const known = new Set(["runtime", "tls", "dynamic"]);
|
|
3921
|
-
for (const profile of options.profiles ?? []) {
|
|
3922
|
-
if (!known.has(profile))
|
|
3923
|
-
throw new Error(`unknown native cache warm profile '${profile}'`);
|
|
3924
|
-
}
|
|
3925
|
-
if (options.profiles?.length === 0)
|
|
3926
|
-
return { cacheRoot, profiles: [] };
|
|
3927
|
-
const mobileTarget = mobileLibraryTarget();
|
|
3928
|
-
if (mobileTarget !== null) {
|
|
3929
|
-
throw new Error(`native cache warming targets executable builds and is unsupported for SCRIPTC_TARGET=${mobileTarget}`);
|
|
3930
|
-
}
|
|
3931
|
-
const driver = resolveCc();
|
|
3932
|
-
const supported = supportedNativeCacheWarmProfiles(driver);
|
|
3933
|
-
if (supported.length === 0) {
|
|
3934
|
-
throw new Error(`native cache warming targets persistently cached native executables and is unsupported for SCRIPTC_TARGET=${driver.target}`);
|
|
3935
|
-
}
|
|
3936
|
-
const profiles = [...new Set(options.profiles ?? supported)];
|
|
3937
|
-
for (const profile of profiles) {
|
|
3938
|
-
if (!supported.includes(profile)) {
|
|
3939
|
-
throw new Error(`native cache warm profile '${profile}' is unsupported for SCRIPTC_TARGET=${driver.target}`);
|
|
3940
|
-
}
|
|
3941
|
-
}
|
|
3942
|
-
const workDir = await mkdtemp(join(tmpdir(), "scriptc-cache-warm-"));
|
|
3943
|
-
const cPath = join(workDir, "warm.c");
|
|
3944
|
-
await writeFile(cPath, "int main(void) { return 0; }\n");
|
|
3945
|
-
const protectedPaths = new Set();
|
|
3946
|
-
try {
|
|
3947
|
-
const results = await Promise.all(profiles.map(async (profile) => {
|
|
3948
|
-
const started = performance.now();
|
|
3949
|
-
await compileCInternal({
|
|
3950
|
-
cPath,
|
|
3951
|
-
outPath: join(workDir, process.platform === "win32" ? `${profile}.exe` : profile),
|
|
3952
|
-
cacheIdentity: "scriptc-native-cache-warm-v1",
|
|
3953
|
-
optimization: options.optimization ?? "release",
|
|
3954
|
-
sanitize: options.sanitize ?? false,
|
|
3955
|
-
...(profile === "tls" ? { fetch: true } : {}),
|
|
3956
|
-
...(profile === "dynamic" ? { dynamic: true } : {}),
|
|
3957
|
-
}, true, protectedPaths);
|
|
3958
|
-
return {
|
|
3959
|
-
profile,
|
|
3960
|
-
elapsedMs: Math.round((performance.now() - started) * 10) / 10,
|
|
3961
|
-
};
|
|
3962
|
-
}));
|
|
3963
|
-
await pruneCache(cacheRoot).catch(() => undefined);
|
|
3964
|
-
if (!(await Promise.all([...protectedPaths].map(fileExists))).every(Boolean)) {
|
|
3965
|
-
throw new Error(`SCRIPTC_CACHE_MAX_MB is too small to retain the requested native cache warm profiles (${profiles.join(", ")})`);
|
|
3966
|
-
}
|
|
3967
|
-
return { cacheRoot, profiles: results };
|
|
3968
|
-
}
|
|
3969
|
-
finally {
|
|
3970
|
-
await rm(workDir, { recursive: true, force: true }).catch(() => undefined);
|
|
3971
|
-
}
|
|
3972
|
-
}
|
|
18
|
+
export { vendorCacheBuildIdentity, vendorCacheTargetFlavor } from "./native/vendor.js";
|
|
3973
19
|
//# sourceMappingURL=native-toolchain.js.map
|