peachsim 0.1.1625 → 0.1.1640
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/LICENSE +1 -1
- package/README.md +15 -2
- package/cli/app-config.ts +18 -6
- package/cli/app-fonts.ts +37 -17
- package/cli/app-stubs.ts +284 -20
- package/cli/bridge-flow-runner.ts +66 -45
- package/cli/cloud-client.ts +60 -40
- package/cli/command-registry.ts +1 -0
- package/cli/commands/control.ts +283 -49
- package/cli/commands/doctor.ts +30 -189
- package/cli/commands/flow.ts +79 -11
- package/cli/commands/inspect/core.ts +10 -5
- package/cli/commands/inspect/settling.ts +14 -2
- package/cli/commands/inspect/shared.ts +9 -4
- package/cli/commands/inspect/wait-ready.ts +12 -0
- package/cli/commands/inspect.ts +91 -50
- package/cli/commands/install-desktop.ts +1 -0
- package/cli/commands/maestro-compare.ts +97 -38
- package/cli/commands/maestro.ts +9 -1
- package/cli/commands/preview.ts +1 -1
- package/cli/commands/record.ts +118 -48
- package/cli/commands/screenshot.ts +23 -1
- package/cli/commands/serve.ts +7 -2
- package/cli/commands/setup.ts +19 -42
- package/cli/commands/swift.ts +14 -3
- package/cli/commands/upload.ts +93 -48
- package/cli/desktop-companion.ts +102 -17
- package/cli/drivers/desktop.ts +14 -1
- package/cli/drivers/playwright-sim-host.ts +103 -45
- package/cli/drivers/playwright.ts +9 -8
- package/cli/drivers/types.ts +5 -0
- package/cli/maestro-accessibility.ts +1 -1
- package/cli/maestro-compare.ts +84 -12
- package/cli/main.ts +3 -2
- package/cli/outbound-endpoints.ts +20 -6
- package/cli/pause-aware-timeout.ts +106 -0
- package/cli/scene-video.ts +89 -0
- package/cli/ws-bridge.ts +37 -19
- package/detox/attempt-events.cjs +66 -5
- package/detox/attempt-events.d.cts +7 -0
- package/detox/expectations.ts +7 -3
- package/detox/index.ts +269 -170
- package/detox/motion-change.cjs +206 -21
- package/detox/motion-change.d.cts +39 -2
- package/detox/navigation-lifecycle.ts +35 -3
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +8 -7
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +13 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +14 -8
- package/dist-lib/bridge-contract.mjs +13 -7
- package/dist-lib/bundle-channel.cjs +11887 -7900
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +6 -3
- package/dist-lib/config.mjs +6 -3
- package/dist-lib/detox/index.cjs +1379 -725
- package/dist-lib/dev-bundle-resolution.cjs +14 -2
- package/dist-lib/expo-swift-bundle.cjs +9341 -5462
- package/dist-lib/expo-swift-metadata.cjs +11 -7
- package/dist-lib/expo-swift-metadata.mjs +11 -7
- package/dist-lib/home-paths.cjs +32 -3
- package/dist-lib/host/bridge-host.cjs +50204 -7258
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +3541 -448
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/kotlin-host.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +2931 -104
- package/dist-lib/native-app-identity-project.cjs +289 -49
- package/dist-lib/native-app-identity-project.mjs +289 -49
- package/dist-lib/native-app-identity.cjs +77 -26
- package/dist-lib/native-app-identity.mjs +76 -26
- package/dist-lib/native-firebase-metadata.cjs +1 -1
- package/dist-lib/native-firebase-metadata.mjs +1 -1
- package/dist-lib/plist.cjs +1 -1
- package/dist-lib/plist.mjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +6 -2
- package/dist-lib/react-native-host-modules.mjs +6 -2
- package/dist-lib/scripts/dev-server-scanner.cjs +22 -8
- package/dist-lib/sdk.cjs +27 -11
- package/dist-lib/sdk.mjs +27 -10
- package/dist-lib/skills.cjs +72201 -26208
- package/dist-lib/swift-compile.cjs +1007 -140
- package/dist-lib/swift-compile.mjs +1049 -174
- package/dist-lib/swift-package.cjs +12 -3
- package/dist-lib/swift-package.mjs +12 -3
- package/dist-lib/swift-submit.cjs +13 -5
- package/dist-lib/swift-submit.mjs +11 -5
- package/dist-lib/swift-toolchain.cjs +423 -33
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +8628 -5268
- package/dist-lib/vite.cjs +2859 -12
- package/dist-lib/worklet-executable.cjs +14 -2
- package/dist-lib/worklet-executable.mjs +14 -2
- package/kotlin/AppSources/dev/peach/app/PeachDrawable.kt +21 -14
- package/kotlin/Sources/androidx/annotation/DrawableRes.kt +14 -0
- package/package.json +1 -1
- package/scripts/dev-server-scanner.ts +2 -1
- package/skills/peach-perf/SKILL.md +11 -5
- package/skills/peach-setup/SKILL.md +16 -19
- package/src/backend-origin.ts +13 -6
- package/src/bridge-constants.ts +11 -0
- package/src/bridge-contract.ts +22 -34
- package/src/config.ts +15 -0
- package/src/desktop-host-delivery.ts +5 -1
- package/src/dev-bundle-resolution.ts +2 -1
- package/src/expo-manifest-body.ts +13 -0
- package/src/expo-swift-host.ts +196 -49
- package/src/expo-swift-ready.ts +78 -0
- package/src/expoKotlinContract.ts +4 -7
- package/src/expoSwiftContract.ts +17 -6
- package/src/home-paths.ts +48 -2
- package/src/host/bridge-host.ts +5 -2
- package/src/host/staged-host-files.ts +9 -0
- package/src/native-app-identity-project.ts +27 -6
- package/src/native-app-identity.ts +91 -4
- package/src/native-firebase-config.ts +209 -13
- package/src/nativeAppIconContract.ts +1 -4
- package/src/output-lock-stdio.ts +36 -0
- package/src/process-group.ts +25 -2
- package/src/react-native-bridge-modules.ts +431 -0
- package/src/react-native-host-modules.ts +8 -1
- package/src/settings-bundle.ts +1 -4
- package/src/swift-binary-modules.ts +2 -2
- package/src/swift-compile.ts +918 -109
- package/src/swift-compiler-digest.ts +14 -0
- package/src/swift-distribution.lock.json +24 -12
- package/src/swift-distribution.ts +103 -4
- package/src/swift-host.ts +180 -55
- package/src/swift-icu.ts +27 -13
- package/src/swift-objc-compile.ts +355 -0
- package/src/swift-objc-sdk.ts +821 -27
- package/src/swift-objc-standin.ts +148 -14
- package/src/swift-package-manifest.ts +60 -7
- package/src/swift-package.ts +15 -1
- package/src/swift-packages.ts +154 -81
- package/src/swift-standin-emit.ts +96 -15
- package/src/swift-standins.ts +530 -116
- package/src/swift-submit.ts +13 -5
- package/src/swift-toolchain.ts +500 -42
- package/src/vite-plugin.ts +12 -1
- package/src/worker-system-chunks.ts +11 -12
- package/src/worklet-executable.ts +19 -1
- package/src/xcode-project.ts +270 -30
package/src/swift-submit.ts
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* Browser-safe: node-only submitters live beside their call sites.
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
+
import { isRecord } from './is-record'
|
|
23
24
|
import {
|
|
24
25
|
isSwiftMetalLibrarySources,
|
|
25
26
|
type SwiftMetalLibrarySource,
|
|
@@ -32,6 +33,10 @@ export const SWIFT_HMR_PATH = '/__peach/swift-hmr'
|
|
|
32
33
|
|
|
33
34
|
// shared with the compile service; collection enforces these before reading.
|
|
34
35
|
export const MAX_SWIFT_COMPILE_FILES = 256
|
|
36
|
+
/** how long one swift build may run when the caller asks for no budget. */
|
|
37
|
+
export const DEFAULT_SWIFT_COMPILE_BUILD_MS = 10 * 60_000
|
|
38
|
+
/** the longest build budget a caller may request. */
|
|
39
|
+
export const MAX_SWIFT_COMPILE_BUILD_MS = 60 * 60_000
|
|
35
40
|
export const MAX_SWIFT_COMPILE_SOURCE_BYTES = 2 * 1024 * 1024
|
|
36
41
|
export const MAX_SWIFT_COMPILE_RESOURCES = 128
|
|
37
42
|
export const MAX_SWIFT_COMPILE_RESOURCE_BYTES = 2 * 1024 * 1024
|
|
@@ -104,7 +109,8 @@ export const MAX_SWIFT_COMPILE_IMAGE_TOTAL_BYTES = 16 * 1024 * 1024
|
|
|
104
109
|
// 45: confirmation dialogs present from a measured `presentationSource`, and a
|
|
105
110
|
// refreshable's pull ends on the host's `refreshEnded` request.
|
|
106
111
|
// 46: ImageRenderer produces shared CoreGraphics pixels through the isolated raster host.
|
|
107
|
-
|
|
112
|
+
// 47: backing-layer masks composite the view, border and children through mask alpha.
|
|
113
|
+
export const SWIFT_HOST_ABI = 47
|
|
108
114
|
|
|
109
115
|
export interface SwiftImageUpload {
|
|
110
116
|
/** the raster file picked for the asset (has the extension). */
|
|
@@ -237,6 +243,11 @@ export type SwiftCompileSubmit = (work: {
|
|
|
237
243
|
entry: string
|
|
238
244
|
/** the directory-derived id of the One package. */
|
|
239
245
|
packageId: string
|
|
246
|
+
/**
|
|
247
|
+
* how long the build may run, up to `MAX_SWIFT_COMPILE_BUILD_MS`; absent means
|
|
248
|
+
* the default. hosted requests above the default require an internal compile key.
|
|
249
|
+
*/
|
|
250
|
+
buildBudgetMs?: number
|
|
240
251
|
}) => Promise<SwiftCompileOutcome>
|
|
241
252
|
|
|
242
253
|
/** the HTTP submitter, shared by the browser worker and the node build. */
|
|
@@ -285,10 +296,6 @@ export function httpSwiftCompileSubmit(
|
|
|
285
296
|
}
|
|
286
297
|
}
|
|
287
298
|
|
|
288
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
289
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
290
|
-
}
|
|
291
|
-
|
|
292
299
|
export function isSwiftNativeModules(value: unknown): value is SwiftNativeModules {
|
|
293
300
|
return (
|
|
294
301
|
isRecord(value) &&
|
|
@@ -394,6 +401,7 @@ export async function compileSwiftProject(
|
|
|
394
401
|
images?: Record<string, SwiftImageUpload>
|
|
395
402
|
entry: string
|
|
396
403
|
packageId: string
|
|
404
|
+
buildBudgetMs?: number
|
|
397
405
|
},
|
|
398
406
|
origin: string,
|
|
399
407
|
signal?: AbortSignal,
|
package/src/swift-toolchain.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// the one place that
|
|
1
|
+
// the one place that selects and verifies the toolchain app Swift compiles with.
|
|
2
2
|
// a binary .swiftmodule loads only in the compiler release that wrote it, so
|
|
3
3
|
// every caller names the release from packages/sootsim-swift/toolchain.json
|
|
4
4
|
// or the Swift distribution's lock (docs/swift-distribution.md).
|
|
@@ -6,21 +6,72 @@
|
|
|
6
6
|
// node builtins only: packages/sootsim-swift/scripts/build.sh imports this
|
|
7
7
|
// file straight from a checkout, with no install.
|
|
8
8
|
import { execFileSync } from 'node:child_process'
|
|
9
|
+
import { createHash } from 'node:crypto'
|
|
9
10
|
import fs from 'node:fs'
|
|
10
11
|
import os from 'node:os'
|
|
11
12
|
import path from 'node:path'
|
|
13
|
+
import { fileURLToPath } from 'node:url'
|
|
14
|
+
|
|
15
|
+
/** swiftpm's SDK directory option, shared by discovery and compilation. */
|
|
16
|
+
export function swiftSdkOptions(): string[] {
|
|
17
|
+
const directory = process.env.PEACH_SWIFT_SDKS_PATH
|
|
18
|
+
return directory ? ['--swift-sdks-path', path.resolve(directory)] : []
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// host archives include the official driver/runtime and the qualified patched frontend.
|
|
22
|
+
export const SWIFT_HOST_COMPILER_RELEASE = 'swift-6.4.0-RELEASE-custom-0d775d32d7c74eae'
|
|
23
|
+
export const SWIFT_HOST_COMPILER_ID = '0d775d32d7c74eae'
|
|
24
|
+
const compilerArchives: Record<string, { checksum: string; manifest: string }> = {
|
|
25
|
+
'darwin-arm64': {
|
|
26
|
+
checksum: '957e85486f92327ee5cf8d3c5bb9336d17ab80145c87645c637c49130a50477a',
|
|
27
|
+
manifest: 'c63e2f81694e8d8ad046d10c71d0638dbae4e5a36601294ed670988d1cb5b6f1',
|
|
28
|
+
},
|
|
29
|
+
'linux-x64': {
|
|
30
|
+
checksum: '6302230fb126b79c9af5fc2bc0208a936bb573c343da7ae949c5c1687e761cd2',
|
|
31
|
+
manifest: '31b507a6a8648a2266a187b9fed092e29a63cae99d57c5defdf2e82a7cbbc113',
|
|
32
|
+
},
|
|
33
|
+
'linux-arm64': {
|
|
34
|
+
checksum: '9b40d39ea5ba82fd9a417868f0a449f5e98e37cf83acaac9cedd3a8b8d91a218',
|
|
35
|
+
manifest: 'fe07a29d4f4374cad47b29de70e90aa2e3b7664e08c8b53d304585e65637da24',
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function swiftHostCompilerArchive(
|
|
40
|
+
release: string,
|
|
41
|
+
host = `${process.platform}-${process.arch}`,
|
|
42
|
+
) {
|
|
43
|
+
if (release !== SWIFT_HOST_COMPILER_RELEASE)
|
|
44
|
+
throw new Error(`unqualified host compiler ${release}`)
|
|
45
|
+
const archive = compilerArchives[host]
|
|
46
|
+
if (!archive) throw new Error(`unsupported Swift compiler host ${host}`)
|
|
47
|
+
return {
|
|
48
|
+
...archive,
|
|
49
|
+
identity: SWIFT_HOST_COMPILER_ID,
|
|
50
|
+
url: `https://r2.sootsim.com/swift-host-compiler/${SWIFT_HOST_COMPILER_ID}/${release}-${host}.tar.gz`,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// source archives and bootstrap images retain the official release name.
|
|
55
|
+
export function swiftUpstreamRelease(release: string): string {
|
|
56
|
+
return release.replace(/-custom-[a-f0-9]{16}$/, '')
|
|
57
|
+
}
|
|
12
58
|
|
|
13
59
|
// swift.org's x.y.0 archives report the compiler identity as x.y.
|
|
14
60
|
export function swiftCompilerRelease(release: string): string {
|
|
15
|
-
return release.replace(
|
|
61
|
+
return swiftUpstreamRelease(release).replace(
|
|
62
|
+
/^swift-(\d+\.\d+)\.0-RELEASE$/,
|
|
63
|
+
'swift-$1-RELEASE',
|
|
64
|
+
)
|
|
16
65
|
}
|
|
17
66
|
|
|
18
67
|
/**
|
|
19
68
|
* the bin directory of the toolchain for `release`, unverified. macOS: the
|
|
20
|
-
* Xcode Toolchains directory, the user's before the system's. Linux:
|
|
21
|
-
*
|
|
69
|
+
* Xcode Toolchains directory, the user's before the system's. Linux:
|
|
70
|
+
* `$CONTRAST_SWIFT_TOOLCHAINS/<release>` when set
|
|
22
71
|
* (packages/sootsim-swift/scripts/install-linux-toolchain.sh fills it), else
|
|
23
|
-
* the cf-build image's `/opt/swift/<release
|
|
72
|
+
* the cf-build image's `/opt/swift/<release>` when present, else a pinned
|
|
73
|
+
* compiler's user-writable `$PEACH_HOME/swift/toolchains/<release>`, else the
|
|
74
|
+
* `swift` first on PATH.
|
|
24
75
|
*/
|
|
25
76
|
export function swiftToolchainBin(release: string): string {
|
|
26
77
|
if (process.platform === 'darwin') {
|
|
@@ -34,6 +85,8 @@ export function swiftToolchainBin(release: string): string {
|
|
|
34
85
|
}
|
|
35
86
|
const image = path.join('/opt/swift', release, 'usr/bin')
|
|
36
87
|
if (fs.existsSync(path.join(image, 'swift'))) return image
|
|
88
|
+
if (release.includes('-custom-'))
|
|
89
|
+
return path.join(peachHome(), 'swift', 'toolchains', release, 'usr/bin')
|
|
37
90
|
return (
|
|
38
91
|
(process.env.PATH ?? '')
|
|
39
92
|
.split(path.delimiter)
|
|
@@ -41,6 +94,106 @@ export function swiftToolchainBin(release: string): string {
|
|
|
41
94
|
)
|
|
42
95
|
}
|
|
43
96
|
|
|
97
|
+
// home-paths.ts's rnxHomeDir, repeated because this file imports node builtins only.
|
|
98
|
+
function peachHome(): string {
|
|
99
|
+
const override = process.env.PEACH_HOME
|
|
100
|
+
return override ? path.resolve(override) : path.join(os.homedir(), '.peach')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let scratch: string | undefined
|
|
104
|
+
let swiftHome: string | undefined
|
|
105
|
+
/**
|
|
106
|
+
* keep the temporary files swift processes write (swiftpm's lock files and
|
|
107
|
+
* TemporaryDirectory folders, which it never removes) in a per-process
|
|
108
|
+
* directory under `root`, removed when this process exits, and on linux its
|
|
109
|
+
* home directory in `root/home`, kept so the caches persist. directories left
|
|
110
|
+
* by processes that no longer run are removed first. without this call swift
|
|
111
|
+
* inherits the caller's TMPDIR.
|
|
112
|
+
*/
|
|
113
|
+
export function confineSwiftScratch(root: string): void {
|
|
114
|
+
if (scratch) return
|
|
115
|
+
fs.mkdirSync(root, { recursive: true })
|
|
116
|
+
for (const name of fs.readdirSync(root)) {
|
|
117
|
+
const pid = Number(name)
|
|
118
|
+
if (Number.isInteger(pid) && pid > 0 && !processRuns(pid))
|
|
119
|
+
fs.rmSync(path.join(root, name), { recursive: true, force: true })
|
|
120
|
+
}
|
|
121
|
+
const dir = path.join(root, String(process.pid))
|
|
122
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
123
|
+
scratch = dir
|
|
124
|
+
// swiftpm and clang write ~/.swiftpm and ~/.cache even with every path flag
|
|
125
|
+
// given; on linux no tool needs the real home, so they get a kept one here.
|
|
126
|
+
if (process.platform === 'linux') {
|
|
127
|
+
swiftHome = path.join(root, 'home')
|
|
128
|
+
fs.mkdirSync(swiftHome, { recursive: true })
|
|
129
|
+
}
|
|
130
|
+
// swiftpm makes ~/.swiftpm in the account's passwd home whatever HOME says;
|
|
131
|
+
// remove at exit only the entries swiftpm seeds there that were absent now:
|
|
132
|
+
// a cache link into a swiftpm cache folder and empty configuration and
|
|
133
|
+
// security folders. anything else, a link or file a user made included, stays.
|
|
134
|
+
const seeded = path.join(os.homedir(), '.swiftpm')
|
|
135
|
+
const dirAbsentBefore = lstatIfKnown(seeded) === null
|
|
136
|
+
const absentBefore = swiftpmSeeds.filter(
|
|
137
|
+
(name) => lstatIfKnown(path.join(seeded, name)) === null,
|
|
138
|
+
)
|
|
139
|
+
process.once('exit', () => {
|
|
140
|
+
fs.rmSync(dir, { recursive: true, force: true })
|
|
141
|
+
for (const name of absentBefore) removeSwiftpmSeed(path.join(seeded, name), name)
|
|
142
|
+
if (dirAbsentBefore && lstatIfKnown(seeded)?.isDirectory())
|
|
143
|
+
try {
|
|
144
|
+
fs.rmdirSync(seeded)
|
|
145
|
+
} catch {}
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const swiftpmSeeds = ['cache', 'configuration', 'security']
|
|
150
|
+
|
|
151
|
+
// only a missing path establishes absence; other lookup failures stay unknown.
|
|
152
|
+
function lstatIfKnown(entry: string): fs.Stats | null | undefined {
|
|
153
|
+
try {
|
|
154
|
+
return fs.lstatSync(entry)
|
|
155
|
+
} catch (error) {
|
|
156
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return null
|
|
157
|
+
return undefined
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function removeSwiftpmSeed(entry: string, name: string): void {
|
|
162
|
+
const stat = lstatIfKnown(entry)
|
|
163
|
+
if (!stat) return
|
|
164
|
+
try {
|
|
165
|
+
if (name === 'cache') {
|
|
166
|
+
if (
|
|
167
|
+
stat.isSymbolicLink() &&
|
|
168
|
+
fs.readlinkSync(entry).endsWith('/.cache/org.swift.swiftpm')
|
|
169
|
+
)
|
|
170
|
+
fs.unlinkSync(entry)
|
|
171
|
+
} else if (stat.isDirectory()) {
|
|
172
|
+
fs.rmdirSync(entry)
|
|
173
|
+
}
|
|
174
|
+
} catch {}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function processRuns(pid: number): boolean {
|
|
178
|
+
try {
|
|
179
|
+
process.kill(pid, 0)
|
|
180
|
+
return true
|
|
181
|
+
} catch (error) {
|
|
182
|
+
return error instanceof Error && 'code' in error && error.code === 'EPERM'
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** the environment every swift process runs with: `base`, with TMPDIR in the
|
|
187
|
+
* confined scratch directory when the caller set one. */
|
|
188
|
+
export function swiftEnvironment(
|
|
189
|
+
base: NodeJS.ProcessEnv = process.env,
|
|
190
|
+
): NodeJS.ProcessEnv {
|
|
191
|
+
if (!scratch) return base
|
|
192
|
+
return swiftHome
|
|
193
|
+
? { ...base, TMPDIR: scratch, HOME: swiftHome }
|
|
194
|
+
: { ...base, TMPDIR: scratch }
|
|
195
|
+
}
|
|
196
|
+
|
|
44
197
|
/** the wasm SDK a release compiles with: its installed id, where it
|
|
45
198
|
* downloads from and its archive checksum (packages/sootsim-swift/toolchain.json). */
|
|
46
199
|
export interface SwiftWasmSdk {
|
|
@@ -49,67 +202,372 @@ export interface SwiftWasmSdk {
|
|
|
49
202
|
wasmSdkChecksum: string
|
|
50
203
|
}
|
|
51
204
|
|
|
52
|
-
const verified = new Map<string, string>()
|
|
53
|
-
|
|
54
205
|
/**
|
|
55
206
|
* swiftToolchainBin, checked: its swift is exactly `release` and has the
|
|
56
207
|
* pinned wasm SDK installed. anything else fails with one message naming the
|
|
57
208
|
* release and the commands that install it.
|
|
58
209
|
*/
|
|
59
210
|
export function swiftToolchain(release: string, sdk: SwiftWasmSdk): string {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
?
|
|
66
|
-
encoding: 'utf8',
|
|
67
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
68
|
-
})
|
|
69
|
-
: ''
|
|
70
|
-
if (!version.includes(`(${swiftCompilerRelease(release)})`)) {
|
|
71
|
-
const found = version
|
|
72
|
-
? `found ${version.split('\n')[0]?.trim()} at ${swift}`
|
|
73
|
-
: process.platform === 'darwin'
|
|
74
|
-
? `found no ${release}.xctoolchain in ~/Library/Developer/Toolchains or /Library/Developer/Toolchains`
|
|
75
|
-
: 'found no swift on PATH'
|
|
211
|
+
validateSwiftSdkIdentity(sdk)
|
|
212
|
+
let bin: string
|
|
213
|
+
try {
|
|
214
|
+
bin = swiftCompiler(release)
|
|
215
|
+
} catch (error) {
|
|
216
|
+
const reason = error instanceof Error ? error.message : String(error)
|
|
76
217
|
throw new Error(
|
|
77
|
-
`
|
|
218
|
+
`Swift compiler ${release} is unavailable or differs from its pin: ${reason}\n${swiftInstallSteps(release, sdk)}`,
|
|
78
219
|
)
|
|
79
220
|
}
|
|
80
|
-
const
|
|
221
|
+
const swift = path.join(bin, 'swift')
|
|
222
|
+
const sdks = execFileSync(swift, ['sdk', 'list', ...swiftSdkOptions()], {
|
|
81
223
|
encoding: 'utf8',
|
|
224
|
+
env: swiftEnvironment(),
|
|
82
225
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
83
226
|
})
|
|
84
227
|
if (!sdks.split('\n').some((line) => line.trim() === sdk.wasmSdkId)) {
|
|
85
228
|
throw new Error(
|
|
86
|
-
`peach compiles Swift with the ${sdk.wasmSdkId} SDK, which ${swift} does not have.\n${
|
|
229
|
+
`peach compiles Swift with the ${sdk.wasmSdkId} SDK, which ${swift} does not have.\n${swiftSdkInstallStep(release, sdk)}`,
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
verifySwiftSdk(swift, sdk)
|
|
234
|
+
} catch (error) {
|
|
235
|
+
const reason = error instanceof Error ? error.message : String(error)
|
|
236
|
+
throw new Error(`${reason}\n${swiftSdkInstallStep(release, sdk)}`)
|
|
237
|
+
}
|
|
238
|
+
return bin
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// swiftpm's info.json describes SDK selection, not the archive it came from.
|
|
242
|
+
function validateSwiftSdkIdentity(sdk: SwiftWasmSdk): void {
|
|
243
|
+
if (
|
|
244
|
+
!/^[a-zA-Z0-9._-]+$/.test(sdk.wasmSdkId) ||
|
|
245
|
+
!/^[a-f0-9]{64}$/.test(sdk.wasmSdkChecksum)
|
|
246
|
+
)
|
|
247
|
+
throw new Error('invalid Swift SDK identity or checksum')
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function swiftSdkBundle(swift: string, id: string, options: string[]): string {
|
|
251
|
+
const configuration = execFileSync(
|
|
252
|
+
swift,
|
|
253
|
+
['sdk', 'configure', ...options, '--show-configuration', id, 'wasm32-unknown-wasip1'],
|
|
254
|
+
{ encoding: 'utf8', env: swiftEnvironment(), stdio: ['ignore', 'pipe', 'pipe'] },
|
|
255
|
+
)
|
|
256
|
+
const sysroot = /^sdkRootPath: (.+)$/m.exec(configuration)?.[1]?.trim()
|
|
257
|
+
if (!sysroot || !path.isAbsolute(sysroot))
|
|
258
|
+
throw new Error(`Swift SDK ${id} reports no absolute sdkRootPath`)
|
|
259
|
+
let bundle = sysroot
|
|
260
|
+
while (!bundle.endsWith('.artifactbundle')) {
|
|
261
|
+
const parent = path.dirname(bundle)
|
|
262
|
+
if (parent === bundle)
|
|
263
|
+
throw new Error(`Swift SDK ${id} is outside an artifact bundle`)
|
|
264
|
+
bundle = parent
|
|
265
|
+
}
|
|
266
|
+
return fs.realpathSync(bundle)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// stat changes invalidate file hashes; every lookup enumerates the entire bundle,
|
|
270
|
+
// including additions, removals, executable modes and internal symlink targets.
|
|
271
|
+
const sdkFileHashes = new Map<string, { stamp: string; checksum: string }>()
|
|
272
|
+
function swiftSdkPayloadChecksum(bundle: string): string {
|
|
273
|
+
const hash = createHash('sha256')
|
|
274
|
+
function visit(relative: string): void {
|
|
275
|
+
const file = path.join(bundle, relative)
|
|
276
|
+
const stat = fs.lstatSync(file, { bigint: true })
|
|
277
|
+
if (stat.isSymbolicLink()) {
|
|
278
|
+
const resolved = path.relative(bundle, fs.realpathSync(file))
|
|
279
|
+
if (
|
|
280
|
+
resolved === '..' ||
|
|
281
|
+
resolved.startsWith(`..${path.sep}`) ||
|
|
282
|
+
path.isAbsolute(resolved)
|
|
283
|
+
)
|
|
284
|
+
throw new Error(`Swift SDK symlink escapes its bundle: ${relative}`)
|
|
285
|
+
hash.update(JSON.stringify([relative, 'link', fs.readlinkSync(file)]) + '\n')
|
|
286
|
+
} else if (stat.isDirectory()) {
|
|
287
|
+
hash.update(JSON.stringify([relative, 'directory']) + '\n')
|
|
288
|
+
for (const name of fs.readdirSync(file).sort()) visit(path.join(relative, name))
|
|
289
|
+
} else if (stat.isFile()) {
|
|
290
|
+
const stamp = [stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].join(':')
|
|
291
|
+
let cached = sdkFileHashes.get(file)
|
|
292
|
+
if (cached?.stamp !== stamp) {
|
|
293
|
+
cached = { stamp, checksum: swiftFileChecksum(file) }
|
|
294
|
+
sdkFileHashes.set(file, cached)
|
|
295
|
+
}
|
|
296
|
+
hash.update(
|
|
297
|
+
JSON.stringify([relative, 'file', Number(stat.mode & 0o111n), cached.checksum]) +
|
|
298
|
+
'\n',
|
|
299
|
+
)
|
|
300
|
+
} else throw new Error(`unsupported Swift SDK payload entry: ${relative}`)
|
|
301
|
+
}
|
|
302
|
+
visit('')
|
|
303
|
+
return hash.digest('hex')
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function swiftSdkVerificationFile(bundle: string, sdk: SwiftWasmSdk): string {
|
|
307
|
+
return path.join(
|
|
308
|
+
path.dirname(bundle),
|
|
309
|
+
'.peach-sdk-checksums',
|
|
310
|
+
`${sdk.wasmSdkId}-${sdk.wasmSdkChecksum}.json`,
|
|
311
|
+
)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function verifySwiftSdk(swift: string, sdk: SwiftWasmSdk): void {
|
|
315
|
+
const bundle = swiftSdkBundle(swift, sdk.wasmSdkId, swiftSdkOptions())
|
|
316
|
+
const file = swiftSdkVerificationFile(bundle, sdk)
|
|
317
|
+
if (!fs.existsSync(file))
|
|
318
|
+
throw new Error(
|
|
319
|
+
`Swift SDK ${sdk.wasmSdkId} has no verified archive binding for ${sdk.wasmSdkChecksum}. Hydrate it with installSwiftWasmSdk before compiling.`,
|
|
87
320
|
)
|
|
321
|
+
const receipt: unknown = JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
322
|
+
if (
|
|
323
|
+
!receipt ||
|
|
324
|
+
typeof receipt !== 'object' ||
|
|
325
|
+
!('wasmSdkId' in receipt) ||
|
|
326
|
+
receipt.wasmSdkId !== sdk.wasmSdkId ||
|
|
327
|
+
!('wasmSdkChecksum' in receipt) ||
|
|
328
|
+
receipt.wasmSdkChecksum !== sdk.wasmSdkChecksum ||
|
|
329
|
+
!('payloadChecksum' in receipt) ||
|
|
330
|
+
receipt.payloadChecksum !== swiftSdkPayloadChecksum(bundle)
|
|
331
|
+
)
|
|
332
|
+
throw new Error(
|
|
333
|
+
`Swift SDK ${sdk.wasmSdkId} installed payload differs from its verified archive ${sdk.wasmSdkChecksum}`,
|
|
334
|
+
)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** install or qualify existing bytes, serializing shared-store mutation without replacement. */
|
|
338
|
+
function installSwiftSdkArchive(swift: string, sdk: SwiftWasmSdk, archive: string): void {
|
|
339
|
+
validateSwiftSdkIdentity(sdk)
|
|
340
|
+
const staging = fs.realpathSync(
|
|
341
|
+
fs.mkdtempSync(path.join(scratch ?? os.tmpdir(), 'peach-sdk-verification-')),
|
|
342
|
+
)
|
|
343
|
+
try {
|
|
344
|
+
if (swiftFileChecksum(archive) !== sdk.wasmSdkChecksum)
|
|
345
|
+
throw new Error('declared Swift SDK archive checksum mismatch')
|
|
346
|
+
const store = path.join(staging, 'sdks')
|
|
347
|
+
fs.mkdirSync(store)
|
|
348
|
+
execFileSync(
|
|
349
|
+
swift,
|
|
350
|
+
[
|
|
351
|
+
'sdk',
|
|
352
|
+
'install',
|
|
353
|
+
archive,
|
|
354
|
+
'--checksum',
|
|
355
|
+
sdk.wasmSdkChecksum,
|
|
356
|
+
'--swift-sdks-path',
|
|
357
|
+
store,
|
|
358
|
+
],
|
|
359
|
+
{ env: swiftEnvironment(), stdio: 'inherit' },
|
|
360
|
+
)
|
|
361
|
+
const stagedBundle = swiftSdkBundle(swift, sdk.wasmSdkId, [
|
|
362
|
+
'--swift-sdks-path',
|
|
363
|
+
store,
|
|
364
|
+
])
|
|
365
|
+
const expected = swiftSdkPayloadChecksum(stagedBundle)
|
|
366
|
+
const directory = process.env.PEACH_SWIFT_SDKS_PATH
|
|
367
|
+
const locks = directory
|
|
368
|
+
? path.join(path.resolve(directory), '.peach-locks')
|
|
369
|
+
: path.join(os.homedir(), '.swiftpm', 'builder-locks')
|
|
370
|
+
fs.mkdirSync(locks, { recursive: true })
|
|
371
|
+
execFileSync(
|
|
372
|
+
'perl',
|
|
373
|
+
[
|
|
374
|
+
'-e',
|
|
375
|
+
'use Fcntl qw(:flock); my ($lock, $id, $bundle, $swift, @options) = @ARGV; open(my $fd, ">>", $lock) or die $!; flock($fd, LOCK_EX) or die $!; open(my $list, "-|", $swift, "sdk", "list", @options) or die $!; my $found = 0; while (<$list>) { s/^\\s+|\\s+$//g; $found = 1 if $_ eq $id; } close($list) or die "swift sdk list failed"; unless ($found) { system($swift, "sdk", "install", $bundle, @options); exit(($? & 127) ? 128 + ($? & 127) : $? >> 8); }',
|
|
376
|
+
path.join(locks, 'wasm-sdk.lock'),
|
|
377
|
+
sdk.wasmSdkId,
|
|
378
|
+
stagedBundle,
|
|
379
|
+
swift,
|
|
380
|
+
...swiftSdkOptions(),
|
|
381
|
+
],
|
|
382
|
+
{ env: swiftEnvironment(), stdio: 'inherit' },
|
|
383
|
+
)
|
|
384
|
+
const bundle = swiftSdkBundle(swift, sdk.wasmSdkId, swiftSdkOptions())
|
|
385
|
+
if (swiftSdkPayloadChecksum(bundle) !== expected)
|
|
386
|
+
throw new Error(
|
|
387
|
+
`Swift SDK ${sdk.wasmSdkId} already exists with different payload bytes; select a separate --swift-sdks-path. No installed SDK was replaced.`,
|
|
388
|
+
)
|
|
389
|
+
const file = swiftSdkVerificationFile(bundle, sdk)
|
|
390
|
+
fs.mkdirSync(path.dirname(file), { recursive: true })
|
|
391
|
+
const temporary = `${file}.${process.pid}.tmp`
|
|
392
|
+
try {
|
|
393
|
+
fs.writeFileSync(
|
|
394
|
+
temporary,
|
|
395
|
+
JSON.stringify({
|
|
396
|
+
wasmSdkId: sdk.wasmSdkId,
|
|
397
|
+
wasmSdkChecksum: sdk.wasmSdkChecksum,
|
|
398
|
+
payloadChecksum: expected,
|
|
399
|
+
}) + '\n',
|
|
400
|
+
)
|
|
401
|
+
fs.renameSync(temporary, file)
|
|
402
|
+
} finally {
|
|
403
|
+
fs.rmSync(temporary, { force: true })
|
|
404
|
+
}
|
|
405
|
+
} finally {
|
|
406
|
+
fs.rmSync(staging, { recursive: true, force: true })
|
|
407
|
+
for (const file of sdkFileHashes.keys())
|
|
408
|
+
if (file.startsWith(staging + path.sep)) sdkFileHashes.delete(file)
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** verify the compiler and install a declared SDK under the shared store lock. */
|
|
413
|
+
export function installSwiftWasmSdk(release: string, sdk: SwiftWasmSdk): void {
|
|
414
|
+
validateSwiftSdkIdentity(sdk)
|
|
415
|
+
try {
|
|
416
|
+
swiftToolchain(release, sdk)
|
|
417
|
+
return
|
|
418
|
+
} catch {
|
|
419
|
+
// an unverified existing SDK must be compared with the declared archive.
|
|
420
|
+
}
|
|
421
|
+
const swift = path.join(swiftCompiler(release), 'swift')
|
|
422
|
+
const staging = fs.mkdtempSync(path.join(scratch ?? os.tmpdir(), 'builder-swift-sdk-'))
|
|
423
|
+
const archive = path.join(staging, 'sdk.artifactbundle.tar.gz')
|
|
424
|
+
try {
|
|
425
|
+
if (sdk.wasmSdkUrl.startsWith('file:'))
|
|
426
|
+
fs.copyFileSync(fileURLToPath(sdk.wasmSdkUrl), archive)
|
|
427
|
+
else
|
|
428
|
+
execFileSync('curl', ['-fsSL', sdk.wasmSdkUrl, '-o', archive], { stdio: 'inherit' })
|
|
429
|
+
installSwiftSdkArchive(swift, sdk, archive)
|
|
430
|
+
swiftToolchain(release, sdk)
|
|
431
|
+
} finally {
|
|
432
|
+
fs.rmSync(staging, { recursive: true, force: true })
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// stream large compiler binaries without retaining a second copy in memory.
|
|
437
|
+
export function swiftFileChecksum(file: string): string {
|
|
438
|
+
const hash = createHash('sha256')
|
|
439
|
+
const fd = fs.openSync(file, 'r')
|
|
440
|
+
const buffer = Buffer.allocUnsafe(1024 * 1024)
|
|
441
|
+
try {
|
|
442
|
+
let size = fs.readSync(fd, buffer, 0, buffer.length, null)
|
|
443
|
+
while (size > 0) {
|
|
444
|
+
hash.update(buffer.subarray(0, size))
|
|
445
|
+
size = fs.readSync(fd, buffer, 0, buffer.length, null)
|
|
446
|
+
}
|
|
447
|
+
} finally {
|
|
448
|
+
fs.closeSync(fd)
|
|
449
|
+
}
|
|
450
|
+
return hash.digest('hex')
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/** validate the pinned manifest and every replaced binary, independently of --version. */
|
|
454
|
+
export function verifySwiftHostCompiler(release: string, root: string): void {
|
|
455
|
+
const pin = swiftHostCompilerArchive(release)
|
|
456
|
+
const file = path.join(root, 'compiler.json')
|
|
457
|
+
if (swiftFileChecksum(file) !== pin.manifest)
|
|
458
|
+
throw new Error(`Swift compiler manifest mismatch at ${root}`)
|
|
459
|
+
const manifest: unknown = JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
460
|
+
if (
|
|
461
|
+
!manifest ||
|
|
462
|
+
typeof manifest !== 'object' ||
|
|
463
|
+
!('identity' in manifest) ||
|
|
464
|
+
manifest.identity !== pin.identity ||
|
|
465
|
+
!('host' in manifest) ||
|
|
466
|
+
manifest.host !== `${process.platform}-${process.arch}` ||
|
|
467
|
+
!('files' in manifest) ||
|
|
468
|
+
!manifest.files ||
|
|
469
|
+
typeof manifest.files !== 'object'
|
|
470
|
+
) {
|
|
471
|
+
throw new Error(`Swift compiler identity mismatch at ${root}`)
|
|
88
472
|
}
|
|
89
|
-
|
|
473
|
+
const files = Object.entries(manifest.files)
|
|
474
|
+
if (files.length !== 16) throw new Error('incomplete Swift compiler manifest')
|
|
475
|
+
for (const [relative, checksum] of files) {
|
|
476
|
+
if (
|
|
477
|
+
relative.startsWith('/') ||
|
|
478
|
+
relative.split('/').includes('..') ||
|
|
479
|
+
typeof checksum !== 'string' ||
|
|
480
|
+
swiftFileChecksum(path.join(root, relative)) !== checksum
|
|
481
|
+
) {
|
|
482
|
+
throw new Error(`Swift compiler binary mismatch: ${relative}`)
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const verifiedCompilers = new Map<string, string>()
|
|
488
|
+
/** select the actual compiler, including its patched identity, before consulting SDKs. */
|
|
489
|
+
export function swiftCompiler(release: string): string {
|
|
490
|
+
const cached = verifiedCompilers.get(release)
|
|
491
|
+
if (cached) return cached
|
|
492
|
+
const bin = swiftToolchainBin(release)
|
|
493
|
+
if (release.includes('-custom-'))
|
|
494
|
+
verifySwiftHostCompiler(release, path.resolve(bin, '../..'))
|
|
495
|
+
const version = execFileSync(path.join(bin, 'swift'), ['--version'], {
|
|
496
|
+
encoding: 'utf8',
|
|
497
|
+
env: swiftEnvironment(),
|
|
498
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
499
|
+
})
|
|
500
|
+
if (!version.includes(`(${swiftCompilerRelease(release)})`))
|
|
501
|
+
throw new Error(`compiler release mismatch at ${bin}`)
|
|
502
|
+
verifiedCompilers.set(release, bin)
|
|
90
503
|
return bin
|
|
91
504
|
}
|
|
92
505
|
|
|
93
|
-
/** the
|
|
506
|
+
/** the command that installs and qualifies the pinned SDK for an installed compiler. */
|
|
507
|
+
export function swiftSdkInstallStep(release: string, wasmSdk: SwiftWasmSdk): string {
|
|
508
|
+
return `install the pinned SDK:\n ${swiftSdkInstallCommand(release, wasmSdk)}`
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function swiftSdkInstallCommand(release: string, wasmSdk: SwiftWasmSdk): string {
|
|
512
|
+
const { wasmSdkId, wasmSdkUrl, wasmSdkChecksum } = wasmSdk
|
|
513
|
+
const program = `import { installSwiftWasmSdk } from "peachsim/swift-toolchain"; installSwiftWasmSdk(${JSON.stringify(release)}, ${JSON.stringify({ wasmSdkId, wasmSdkUrl, wasmSdkChecksum })})`
|
|
514
|
+
const directory = process.env.PEACH_SWIFT_SDKS_PATH
|
|
515
|
+
const environment = directory
|
|
516
|
+
? `PEACH_SWIFT_SDKS_PATH='${path.resolve(directory).replaceAll("'", "'\\''")}' `
|
|
517
|
+
: ''
|
|
518
|
+
return `${environment}node --input-type=module -e '${program.replaceAll("'", "'\\''")}'`
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/** the commands that install the pinned compiler and SDK on this host. */
|
|
94
522
|
export function swiftInstallSteps(release: string, wasmSdk: SwiftWasmSdk): string {
|
|
523
|
+
const { note, commands } = swiftCompilerInstall(release)
|
|
524
|
+
return [
|
|
525
|
+
`install the pinned compiler and SDK${note}:`,
|
|
526
|
+
...[...commands, swiftSdkInstallCommand(release, wasmSdk)].map((line) => ` ${line}`),
|
|
527
|
+
].join('\n')
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/** the commands that install the pinned compiler alone, for a caller that installs the SDK. */
|
|
531
|
+
export function swiftCompilerInstallSteps(release: string): string {
|
|
532
|
+
const { note, commands } = swiftCompilerInstall(release)
|
|
533
|
+
return [
|
|
534
|
+
`install the pinned compiler${note}:`,
|
|
535
|
+
...commands.map((line) => ` ${line}`),
|
|
536
|
+
].join('\n')
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function swiftCompilerInstall(release: string): { note: string; commands: string[] } {
|
|
540
|
+
if (release.includes('-custom-')) {
|
|
541
|
+
const archive = swiftHostCompilerArchive(release)
|
|
542
|
+
const directory = path.resolve(swiftToolchainBin(release), '../..')
|
|
543
|
+
return {
|
|
544
|
+
note: '',
|
|
545
|
+
commands: [
|
|
546
|
+
`curl -fsSL ${archive.url} -o compiler.tar.gz`,
|
|
547
|
+
`echo '${archive.checksum} compiler.tar.gz' | shasum -a 256 -c -`,
|
|
548
|
+
`mkdir -p '${directory}'`,
|
|
549
|
+
`tar -xzf compiler.tar.gz --strip-components=1 -C '${directory}'`,
|
|
550
|
+
],
|
|
551
|
+
}
|
|
552
|
+
}
|
|
95
553
|
const version = release.replace(/^swift-/, '').toLowerCase()
|
|
96
554
|
const downloads = `https://download.swift.org/swift-${version}`
|
|
97
|
-
const sdk = `swift sdk install ${wasmSdk.wasmSdkUrl} --checksum ${wasmSdk.wasmSdkChecksum}`
|
|
98
555
|
if (process.platform === 'darwin') {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
556
|
+
return {
|
|
557
|
+
note: '',
|
|
558
|
+
commands: [
|
|
559
|
+
`curl -fsSLO ${downloads}/xcode/${release}/${release}-osx.pkg`,
|
|
560
|
+
`installer -pkg ${release}-osx.pkg -target CurrentUserHomeDirectory`,
|
|
561
|
+
],
|
|
562
|
+
}
|
|
106
563
|
}
|
|
107
564
|
const platform = `debian12${process.arch === 'arm64' ? '-aarch64' : ''}`
|
|
108
565
|
const name = `${release}-${platform}`
|
|
109
|
-
return
|
|
110
|
-
'
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
566
|
+
return {
|
|
567
|
+
note: ' (Debian 12 shown; other distributions: https://www.swift.org/install/linux/)',
|
|
568
|
+
commands: [
|
|
569
|
+
`curl -fsSL ${downloads}/${platform}/${release}/${name}.tar.gz | tar -xz -C /opt`,
|
|
570
|
+
`export PATH=/opt/${name}/usr/bin:$PATH`,
|
|
571
|
+
],
|
|
572
|
+
}
|
|
115
573
|
}
|