peachsim 0.1.1620 → 0.1.1635
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 +45 -6
- package/cli/app-config.ts +4 -1
- package/cli/app-fonts.ts +39 -18
- package/cli/app-stubs.ts +224 -9
- package/cli/bridge-flow-runner.ts +248 -156
- package/cli/cloud-client.ts +68 -57
- package/cli/command-registry.ts +1 -0
- package/cli/commands/box/search-command.ts +3 -2
- package/cli/commands/box.ts +10 -29
- package/cli/commands/control.ts +391 -92
- package/cli/commands/doctor.ts +17 -184
- package/cli/commands/flow.ts +79 -11
- package/cli/commands/inspect/core.ts +10 -5
- package/cli/commands/inspect/settling.ts +71 -22
- package/cli/commands/inspect/wait-ready.ts +12 -0
- package/cli/commands/inspect.ts +26 -3
- package/cli/commands/install-desktop.ts +1 -0
- package/cli/commands/maestro-compare.ts +105 -67
- package/cli/commands/maestro.ts +9 -1
- package/cli/commands/platform.ts +71 -36
- package/cli/commands/record.ts +21 -1
- 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 +5 -0
- package/cli/commands/upload.ts +51 -8
- package/cli/desktop-companion.ts +102 -17
- package/cli/dev-build-freshness.ts +37 -2
- package/cli/drivers/desktop.ts +14 -1
- package/cli/drivers/playwright-sim-host.ts +44 -8
- package/cli/drivers/playwright.ts +9 -8
- package/cli/drivers/types.ts +5 -0
- package/cli/maestro-accessibility.ts +53 -0
- package/cli/maestro-compare.ts +108 -12
- package/cli/main.ts +2 -1
- package/cli/outbound-endpoints.ts +35 -6
- package/cli/parent-pid.ts +0 -9
- package/cli/pause-aware-timeout.ts +102 -0
- package/cli/remote-simulators.ts +112 -0
- package/cli/remoteSimulatorContract.ts +21 -0
- package/cli/scene-video.ts +89 -0
- package/cli/ws-bridge.ts +23 -14
- package/detox/attempt-events.cjs +4 -0
- package/detox/expectations.ts +5 -1
- package/detox/index.ts +241 -181
- package/detox/motion-change.cjs +73 -0
- package/detox/motion-change.d.cts +21 -0
- package/detox/navigation-lifecycle.ts +22 -4
- package/dist-lib/agent-daemon-client.cjs +3 -2
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +3 -2
- package/dist-lib/backend-origin.cjs +1 -1
- 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 +10480 -7962
- 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 +3 -2
- package/dist-lib/cloud.mjs +3 -2
- package/dist-lib/config.cjs +10 -6
- package/dist-lib/config.mjs +10 -6
- package/dist-lib/detox/index.cjs +1854 -966
- package/dist-lib/dev-bundle-resolution.cjs +64 -24
- package/dist-lib/expo-swift-bundle.cjs +8715 -6271
- package/dist-lib/expo-swift-metadata.cjs +7 -4
- package/dist-lib/expo-swift-metadata.mjs +7 -4
- package/dist-lib/home-paths.cjs +32 -3
- package/dist-lib/host/bridge-host.cjs +10135 -7060
- 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 +1843 -408
- 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 +3 -2
- package/dist-lib/menu.mjs +3 -2
- 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 +5 -4
- package/dist-lib/metro-production-bundle.mjs +5 -4
- package/dist-lib/metro.cjs +1182 -418
- package/dist-lib/native-app-identity-project.cjs +782 -23
- package/dist-lib/native-app-identity-project.mjs +781 -23
- package/dist-lib/native-app-identity.cjs +276 -9
- package/dist-lib/native-app-identity.mjs +273 -8
- package/dist-lib/native-firebase-metadata.cjs +5 -4
- package/dist-lib/native-firebase-metadata.mjs +5 -4
- 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 +3 -2
- package/dist-lib/react-native-host-modules.cjs +8 -1
- package/dist-lib/react-native-host-modules.mjs +8 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +106 -28
- package/dist-lib/sdk.cjs +115 -34
- package/dist-lib/sdk.mjs +115 -33
- package/dist-lib/skills.cjs +15274 -9904
- package/dist-lib/swift-compile.cjs +1165 -173
- package/dist-lib/swift-compile.mjs +1212 -203
- package/dist-lib/swift-package.cjs +12 -3
- package/dist-lib/swift-package.mjs +12 -3
- package/dist-lib/swift-submit.cjs +7 -1
- package/dist-lib/swift-submit.mjs +5 -1
- package/dist-lib/swift-toolchain.cjs +133 -18
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +2648 -862
- package/dist-lib/vite.cjs +70 -14
- package/dist-lib/worklet-executable.cjs +25 -2
- package/dist-lib/worklet-executable.mjs +25 -2
- package/kotlin/AppSources/dev/peach/app/PeachDrawable.kt +21 -14
- package/kotlin/Sources/androidx/annotation/DrawableRes.kt +14 -0
- package/package.json +2 -2
- package/scripts/dev-server-scanner.ts +35 -14
- package/skills/peach-perf/SKILL.md +11 -5
- package/skills/peach-setup/SKILL.md +16 -19
- package/src/admitted-mac-build.ts +34 -8
- package/src/android-app-identity.ts +146 -0
- package/src/android-autolinking.ts +75 -0
- package/src/android-string-value.ts +36 -0
- package/src/android-strings.ts +5 -36
- package/src/androidAutolinkingContract.ts +40 -0
- package/src/bridge-constants.ts +11 -0
- package/src/bridge-contract.ts +22 -34
- package/src/bundleGlobalFooter.ts +26 -7
- package/src/codex-client.ts +3 -2
- package/src/config.ts +15 -0
- package/src/desktop-host-delivery.ts +5 -1
- package/src/dev-bundle-resolution.ts +60 -26
- package/src/expo-kotlin-host.ts +6 -36
- package/src/expo-manifest-body.ts +13 -0
- package/src/expo-swift-bundle.ts +17 -12
- package/src/expo-swift-host.ts +640 -192
- package/src/expo-swift-ready.ts +78 -0
- package/src/expoSwiftContract.ts +19 -0
- package/src/home-paths.ts +48 -2
- package/src/host/bridge-host.ts +12 -57
- package/src/host/staged-host-files.ts +29 -2
- package/src/installed-package-versions.ts +54 -14
- package/src/launchMetadataContract.ts +24 -0
- package/src/metro-plugin.ts +37 -42
- package/src/native-app-identity-project.ts +142 -5
- package/src/native-app-identity.ts +176 -11
- package/src/native-firebase-config.ts +209 -13
- package/src/nitro-compile.ts +2 -5
- package/src/nitro-source.ts +147 -63
- package/src/process-group.ts +114 -61
- package/src/process-output.ts +0 -9
- package/src/public-brand.ts +1 -0
- package/src/react-native-bridge-modules.ts +416 -0
- package/src/react-native-host-modules.ts +7 -0
- package/src/sim-client.ts +6 -4
- package/src/swift-binary-modules.ts +2 -2
- package/src/swift-compile.ts +1007 -108
- package/src/swift-compiler-digest.ts +14 -0
- package/src/swift-distribution.lock.json +40 -12
- package/src/swift-distribution.ts +61 -9
- package/src/swift-host.ts +149 -56
- package/src/swift-icu.ts +14 -6
- package/src/swift-objc-sdk.ts +133 -10
- package/src/swift-objc-standin.ts +38 -3
- package/src/swift-package-manifest.ts +21 -6
- package/src/swift-package.ts +15 -1
- package/src/swift-packages.ts +119 -60
- package/src/swift-standin-emit.ts +57 -9
- package/src/swift-standins.ts +185 -25
- package/src/swift-submit.ts +10 -0
- package/src/swift-toolchain.ts +149 -23
- package/src/vite-plugin.ts +20 -20
- package/src/worklet-executable.ts +30 -3
- package/src/worklets-babel.ts +35 -14
- package/src/xcode-project.ts +10 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import { readFileSync } from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
// the lowering and manifest code in swift-compile.ts and its design-time lowering
|
|
6
|
+
// decide the emitted output, so their text addresses every swift artifact. bundled
|
|
7
|
+
// builds cannot read these files at runtime, so build-cli injects this digest.
|
|
8
|
+
export function swiftCompilerSourceDigest(srcDir: string): string {
|
|
9
|
+
const hash = createHash('sha256')
|
|
10
|
+
for (const file of ['swift-compile.ts', 'swift-design-time.ts']) {
|
|
11
|
+
hash.update(readFileSync(path.join(srcDir, file)))
|
|
12
|
+
}
|
|
13
|
+
return hash.digest('hex')
|
|
14
|
+
}
|
|
@@ -1,11 +1,34 @@
|
|
|
1
1
|
{
|
|
2
|
-
"id": "
|
|
3
|
-
"release": "swift-6.4.0-RELEASE",
|
|
4
|
-
"wasmSdkId": "swift-6.4.0-RELEASE_wasm-peach-
|
|
5
|
-
"wasmSdkUrl": "https://r2.sootsim.com/swift-wasm-sdk/swift-6.4.0-RELEASE_wasm-peach-
|
|
6
|
-
"wasmSdkChecksum": "
|
|
7
|
-
"source": "
|
|
2
|
+
"id": "4fdcd218fa180b52",
|
|
3
|
+
"release": "swift-6.4.0-RELEASE-custom-91504a66b3f012ac",
|
|
4
|
+
"wasmSdkId": "swift-6.4.0-RELEASE_wasm-peach-7fd9a101",
|
|
5
|
+
"wasmSdkUrl": "https://r2.sootsim.com/swift-wasm-sdk/swift-6.4.0-RELEASE_wasm-peach-7fd9a101.artifactbundle.tar.gz",
|
|
6
|
+
"wasmSdkChecksum": "72f7fec1d090d142d26148d6cc0cd5b1ce78da70bb97bd577dc1482eaf0299b2",
|
|
7
|
+
"source": "80c26dea3c4bbf0e29458c33a3a0e856b0942564",
|
|
8
8
|
"targets": [
|
|
9
|
+
"System",
|
|
10
|
+
"_LocationEssentials",
|
|
11
|
+
"GeoToolbox",
|
|
12
|
+
"DataDetection",
|
|
13
|
+
"Accessibility",
|
|
14
|
+
"Contacts",
|
|
15
|
+
"CoreML",
|
|
16
|
+
"EventKit",
|
|
17
|
+
"CloudKit",
|
|
18
|
+
"IOSurface",
|
|
19
|
+
"OpenGLES",
|
|
20
|
+
"ContactsUI",
|
|
21
|
+
"CoreLocationUI",
|
|
22
|
+
"MediaPlayer",
|
|
23
|
+
"MusicKit",
|
|
24
|
+
"RealityKit",
|
|
25
|
+
"SecureElementCredential",
|
|
26
|
+
"Social",
|
|
27
|
+
"Speech",
|
|
28
|
+
"Vision",
|
|
29
|
+
"VisionKit",
|
|
30
|
+
"WorkoutKit",
|
|
31
|
+
"_PassKit_SwiftUI",
|
|
9
32
|
"ExpoModulesCore",
|
|
10
33
|
"_MapKit_SwiftUI",
|
|
11
34
|
"PeachJSON",
|
|
@@ -13,14 +36,17 @@
|
|
|
13
36
|
"FoundationNetworking",
|
|
14
37
|
"DeveloperToolsSupport",
|
|
15
38
|
"SwiftUI",
|
|
39
|
+
"os_workgroup",
|
|
16
40
|
"Dispatch",
|
|
17
41
|
"DarwinRecords",
|
|
18
42
|
"Darwin",
|
|
19
43
|
"MachO",
|
|
20
44
|
"ObjectiveC",
|
|
21
45
|
"PeachObjC",
|
|
46
|
+
"UIUtilities",
|
|
22
47
|
"UIKitCore",
|
|
23
48
|
"PeachICU",
|
|
49
|
+
"PeachCMark",
|
|
24
50
|
"CoreImage",
|
|
25
51
|
"QuartzCore",
|
|
26
52
|
"CoreGraphics",
|
|
@@ -51,6 +77,7 @@
|
|
|
51
77
|
"ActivityKit",
|
|
52
78
|
"SwiftData",
|
|
53
79
|
"UIKit",
|
|
80
|
+
"React",
|
|
54
81
|
"Combine",
|
|
55
82
|
"PeachFoundationCombine",
|
|
56
83
|
"AppIntents",
|
|
@@ -132,6 +159,7 @@
|
|
|
132
159
|
"systemVersion": ["string"],
|
|
133
160
|
"colorScheme": ["string:light", "string:dark"],
|
|
134
161
|
"semanticColors": ["object"],
|
|
162
|
+
"imageAssets": ["object"],
|
|
135
163
|
"locale": ["string"],
|
|
136
164
|
"dynamicTypeSize": [
|
|
137
165
|
"string:xSmall",
|
|
@@ -153,16 +181,16 @@
|
|
|
153
181
|
},
|
|
154
182
|
"hosts": {
|
|
155
183
|
"darwin-arm64": {
|
|
156
|
-
"sha256": "
|
|
157
|
-
"size":
|
|
184
|
+
"sha256": "2b6768539f39bbdc2d77447fc91b00e9a7ee2d9ed732ea10e67a7b05c1c854ea",
|
|
185
|
+
"size": 69899802
|
|
158
186
|
},
|
|
159
187
|
"linux-x64": {
|
|
160
|
-
"sha256": "
|
|
161
|
-
"size":
|
|
188
|
+
"sha256": "2d357dc6c24559b1e4f792c4142de2608e2dd91a2b322c74013af31a9346ad6e",
|
|
189
|
+
"size": 69902826
|
|
162
190
|
},
|
|
163
191
|
"linux-arm64": {
|
|
164
|
-
"sha256": "
|
|
165
|
-
"size":
|
|
192
|
+
"sha256": "fdbdc2b25d102db38da6c00e74c6d198b87537aefe74365ef65793ec6feb3faa",
|
|
193
|
+
"size": 69900830
|
|
166
194
|
}
|
|
167
195
|
}
|
|
168
196
|
}
|
|
@@ -27,13 +27,23 @@ export interface SwiftModule {
|
|
|
27
27
|
toolchainBin: string
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
const installs = new Map<
|
|
30
|
+
const installs = new Map<
|
|
31
|
+
string,
|
|
32
|
+
{
|
|
33
|
+
promise: Promise<string>
|
|
34
|
+
controller: AbortController
|
|
35
|
+
clients: number
|
|
36
|
+
}
|
|
37
|
+
>()
|
|
31
38
|
|
|
32
39
|
/** the installed distribution's root, downloading it on first use, or null
|
|
33
40
|
* when PEACH_SWIFT_ENDPOINT is unset. */
|
|
34
|
-
export function swiftDistributionRoot(
|
|
41
|
+
export async function swiftDistributionRoot(
|
|
42
|
+
signal?: AbortSignal,
|
|
43
|
+
): Promise<string | null> {
|
|
44
|
+
signal?.throwIfAborted()
|
|
35
45
|
const endpoint = process.env[SWIFT_ENDPOINT_ENV]?.trim().replace(/\/+$/, '')
|
|
36
|
-
if (!endpoint) return
|
|
46
|
+
if (!endpoint) return null
|
|
37
47
|
const lock = parseSwiftDistributionLock(lockJson)
|
|
38
48
|
const host = swiftDistributionHost(process.platform, process.arch)
|
|
39
49
|
if (!host) {
|
|
@@ -46,13 +56,16 @@ export function swiftDistributionRoot(): Promise<string | null> {
|
|
|
46
56
|
swiftToolchain(lock.release, lock)
|
|
47
57
|
const dir = path.join(rnxHomeDir(), 'swift', `${lock.id}-${host}`)
|
|
48
58
|
let pending = installs.get(dir)
|
|
59
|
+
if (pending?.controller.signal.aborted) pending = undefined
|
|
49
60
|
if (!pending) {
|
|
50
|
-
|
|
61
|
+
const controller = new AbortController()
|
|
62
|
+
const promise = (async () => {
|
|
51
63
|
if (!fs.existsSync(path.join(dir, SWIFT_DISTRIBUTION_MANIFEST))) {
|
|
52
64
|
process.stderr.write(
|
|
53
65
|
`peach: downloading Swift distribution ${lock.id} (${host})…\n`,
|
|
54
66
|
)
|
|
55
67
|
await installVerifiedTarball({
|
|
68
|
+
signal: controller.signal,
|
|
56
69
|
url: `${endpoint}/${swiftDistributionKey(lock.id, host)}`,
|
|
57
70
|
digest: {
|
|
58
71
|
algorithm: 'sha256',
|
|
@@ -69,11 +82,48 @@ export function swiftDistributionRoot(): Promise<string | null> {
|
|
|
69
82
|
throw new Error(`${dir} does not hold Swift distribution ${lock.id} (${host})`)
|
|
70
83
|
}
|
|
71
84
|
return dir
|
|
72
|
-
})()
|
|
73
|
-
|
|
85
|
+
})().finally(() => {
|
|
86
|
+
if (installs.get(dir)?.controller === controller) installs.delete(dir)
|
|
87
|
+
})
|
|
88
|
+
pending = { promise, controller, clients: 0 }
|
|
74
89
|
installs.set(dir, pending)
|
|
75
90
|
}
|
|
76
|
-
|
|
91
|
+
const current = pending
|
|
92
|
+
current.clients++
|
|
93
|
+
let released = false
|
|
94
|
+
const release = () => {
|
|
95
|
+
if (released) return
|
|
96
|
+
released = true
|
|
97
|
+
if (--current.clients === 0) current.controller.abort(signal?.reason)
|
|
98
|
+
}
|
|
99
|
+
let abort: (() => void) | undefined
|
|
100
|
+
try {
|
|
101
|
+
const root = await new Promise<string>((resolve, reject) => {
|
|
102
|
+
abort = () => {
|
|
103
|
+
release()
|
|
104
|
+
const reason: unknown = signal?.reason
|
|
105
|
+
const error =
|
|
106
|
+
reason instanceof Error
|
|
107
|
+
? reason
|
|
108
|
+
: new Error('Swift distribution installation canceled', { cause: reason })
|
|
109
|
+
// other callers retain ownership; the last caller waits for cleanup.
|
|
110
|
+
if (current.clients > 0) reject(error)
|
|
111
|
+
else
|
|
112
|
+
current.promise.then(
|
|
113
|
+
() => reject(error),
|
|
114
|
+
() => reject(error),
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
signal?.addEventListener('abort', abort, { once: true })
|
|
118
|
+
current.promise.then(resolve, reject)
|
|
119
|
+
if (signal?.aborted) abort()
|
|
120
|
+
})
|
|
121
|
+
signal?.throwIfAborted()
|
|
122
|
+
return root
|
|
123
|
+
} finally {
|
|
124
|
+
if (abort) signal?.removeEventListener('abort', abort)
|
|
125
|
+
release()
|
|
126
|
+
}
|
|
77
127
|
}
|
|
78
128
|
|
|
79
129
|
/** the module this process compiles app Swift against: the checkout beside a
|
|
@@ -81,7 +131,9 @@ export function swiftDistributionRoot(): Promise<string | null> {
|
|
|
81
131
|
* nothing without it, so a checkout's toolchain error surfaces to them. */
|
|
82
132
|
export async function resolveSwiftModule({
|
|
83
133
|
required = false,
|
|
84
|
-
|
|
134
|
+
signal,
|
|
135
|
+
}: { required?: boolean; signal?: AbortSignal } = {}): Promise<SwiftModule | null> {
|
|
136
|
+
signal?.throwIfAborted()
|
|
85
137
|
const checkout = path.resolve(import.meta.dirname, '../../sootsim-swift')
|
|
86
138
|
if (fs.existsSync(path.join(checkout, 'toolchain.json'))) {
|
|
87
139
|
// a checkout is implicit: without its toolchain, apps run without their
|
|
@@ -93,7 +145,7 @@ export async function resolveSwiftModule({
|
|
|
93
145
|
return null
|
|
94
146
|
}
|
|
95
147
|
}
|
|
96
|
-
const root = await swiftDistributionRoot()
|
|
148
|
+
const root = await swiftDistributionRoot(signal)
|
|
97
149
|
return root ? { root, ...swiftModuleToolchain(root) } : null
|
|
98
150
|
}
|
|
99
151
|
|
package/src/swift-host.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { createHash } from 'node:crypto'
|
|
|
2
2
|
// the node host for every Swift preview: package inputs, one compile and
|
|
3
3
|
// immutable artifact addresses. vite and the foreground CLI adapt this host.
|
|
4
4
|
import fs from 'node:fs'
|
|
5
|
-
import os from 'node:os'
|
|
6
5
|
import path from 'node:path'
|
|
7
6
|
import {
|
|
8
7
|
enumerateBundleColors,
|
|
@@ -11,8 +10,10 @@ import {
|
|
|
11
10
|
enumerateBundleResources,
|
|
12
11
|
resolveSwiftImageResourceUrls,
|
|
13
12
|
} from '../../sootsim-swift/src/bundleAssets'
|
|
13
|
+
import { compileStringCatalog } from '../../sootsim-swift/src/stringCatalog'
|
|
14
14
|
import { authHeaderValue, resolveCliAuth } from '../cli/auth'
|
|
15
15
|
import { resolveContrastBackendOrigin } from './backend-origin'
|
|
16
|
+
import { cacheDir } from './home-paths'
|
|
16
17
|
import iosSdk from './ios-sdk-frameworks.json' with { type: 'json' }
|
|
17
18
|
import { swiftBinaryModules } from './swift-binary-modules'
|
|
18
19
|
import {
|
|
@@ -39,18 +40,19 @@ import {
|
|
|
39
40
|
} from './swift-package'
|
|
40
41
|
import { swiftPackageManifest } from './swift-package-manifest'
|
|
41
42
|
import {
|
|
42
|
-
|
|
43
|
+
remoteSwiftCheckouts,
|
|
43
44
|
resolveSwiftPackages,
|
|
44
45
|
type SwiftPackageBinary,
|
|
45
46
|
type SwiftPackageGap,
|
|
46
47
|
type SwiftPackages,
|
|
47
48
|
} from './swift-packages'
|
|
48
|
-
import { readPackageFile } from './swift-source-file'
|
|
49
|
+
import { readPackageFile as readSwiftSourceFile } from './swift-source-file'
|
|
49
50
|
import { readSwiftStandinBehavior } from './swift-standin-config'
|
|
50
51
|
import {
|
|
51
52
|
standinGenerationInputs,
|
|
52
53
|
standinLibrary,
|
|
53
54
|
swiftStandins,
|
|
55
|
+
SWIFT_STANDINS_DIR,
|
|
54
56
|
type SwiftStandin,
|
|
55
57
|
} from './swift-standins'
|
|
56
58
|
import {
|
|
@@ -67,6 +69,7 @@ import {
|
|
|
67
69
|
MAX_SWIFT_COMPILE_IMAGE_TOTAL_BYTES,
|
|
68
70
|
isSwiftNativeModules,
|
|
69
71
|
compileSwiftProject,
|
|
72
|
+
DEFAULT_SWIFT_COMPILE_BUILD_MS,
|
|
70
73
|
httpSwiftCompileSubmit,
|
|
71
74
|
swiftNativeEntry,
|
|
72
75
|
type SwiftImageUpload,
|
|
@@ -172,13 +175,18 @@ export interface SwiftProject {
|
|
|
172
175
|
packagePath: string
|
|
173
176
|
artifact: SwiftArtifact | null
|
|
174
177
|
// the build in flight and the sources it was started from
|
|
175
|
-
building: {
|
|
178
|
+
building: {
|
|
179
|
+
stamp: string
|
|
180
|
+
promise: Promise<SwiftArtifact>
|
|
181
|
+
signal?: AbortSignal
|
|
182
|
+
} | null
|
|
176
183
|
// two bounded generations of immutable bytes cover in-flight asset reads.
|
|
177
184
|
resourceFiles: Map<string, { bytes: Buffer; contentType: string }>
|
|
178
185
|
resourcesPrevious: Map<string, { bytes: Buffer; contentType: string }>
|
|
179
186
|
artifacts: SwiftArtifact[]
|
|
180
187
|
hosted?: SwiftHostedCompileOptions
|
|
181
188
|
signal?: AbortSignal
|
|
189
|
+
libraries?: Record<string, string>
|
|
182
190
|
}
|
|
183
191
|
|
|
184
192
|
// a project is its swiftpm package: the build, the artifact, and the dev
|
|
@@ -236,6 +244,8 @@ interface XcodeProjectEntry {
|
|
|
236
244
|
packages?: {
|
|
237
245
|
key: string
|
|
238
246
|
standinKey: string
|
|
247
|
+
/** generation always uses the original closure, before stand-ins replace targets. */
|
|
248
|
+
binaries: SwiftPackageBinary[]
|
|
239
249
|
packages: SwiftPackages
|
|
240
250
|
standins: SwiftStandin[]
|
|
241
251
|
/** why a framework the project vendors got no stand-in, by name. */
|
|
@@ -318,16 +328,18 @@ function swiftPackages(
|
|
|
318
328
|
// emitter), so a changed input rebuilds instead of reusing stale output.
|
|
319
329
|
let cached = entry.packages
|
|
320
330
|
if (cached?.key !== key) {
|
|
331
|
+
const packages = resolveSwiftPackages({
|
|
332
|
+
rootDir: app.rootDir,
|
|
333
|
+
resolved: app.packageResolved,
|
|
334
|
+
products: app.linkedProducts,
|
|
335
|
+
localDirs: app.localPackageDirs,
|
|
336
|
+
provided: entry.provided,
|
|
337
|
+
})
|
|
321
338
|
cached = {
|
|
322
339
|
key,
|
|
323
340
|
standinKey: '',
|
|
324
|
-
packages
|
|
325
|
-
|
|
326
|
-
resolved: app.packageResolved,
|
|
327
|
-
products: app.linkedProducts,
|
|
328
|
-
localDirs: app.localPackageDirs,
|
|
329
|
-
provided: entry.provided,
|
|
330
|
-
}),
|
|
341
|
+
packages,
|
|
342
|
+
binaries: packages.binaries,
|
|
331
343
|
standins: [],
|
|
332
344
|
projectGaps: new Map(),
|
|
333
345
|
}
|
|
@@ -354,10 +366,7 @@ function swiftPackages(
|
|
|
354
366
|
]
|
|
355
367
|
: [],
|
|
356
368
|
)
|
|
357
|
-
const reached = [
|
|
358
|
-
...cached.packages.binaries.filter((binary) => binary.reached),
|
|
359
|
-
...vendored,
|
|
360
|
-
]
|
|
369
|
+
const reached = [...cached.binaries.filter((binary) => binary.reached), ...vendored]
|
|
361
370
|
if (entry.moduleRoot && entry.provided && reached.length > 0) {
|
|
362
371
|
const resolved = swiftModuleToolchain(entry.moduleRoot)
|
|
363
372
|
const toolchain = {
|
|
@@ -379,6 +388,8 @@ function swiftPackages(
|
|
|
379
388
|
]),
|
|
380
389
|
binaryGaps: built.gaps,
|
|
381
390
|
})
|
|
391
|
+
for (const target of new Set(built.standins.map((standin) => standin.target)))
|
|
392
|
+
console.error(`Peach: target ${target} was replaced with a stand-in`)
|
|
382
393
|
cached.standins = built.standins
|
|
383
394
|
cached.projectGaps = new Map(
|
|
384
395
|
vendored.flatMap(({ target }) => {
|
|
@@ -402,17 +413,17 @@ const XCODE_FRAMEWORKS_DIR = '__frameworks__'
|
|
|
402
413
|
// remote package checkouts compile here: swiftpm owns `.build` in the
|
|
403
414
|
// compile's package root as its scratch directory.
|
|
404
415
|
const XCODE_PACKAGES_DIR = '__packages__'
|
|
405
|
-
// each binary target's stand-in compiles as a target under its module's
|
|
406
|
-
// directory here, from sources generated on this host.
|
|
407
|
-
const XCODE_STANDINS_DIR = '__standins__'
|
|
408
|
-
const CHECKOUTS_PREFIX = `${REMOTE_CHECKOUTS.split(path.sep).join(path.posix.sep)}/`
|
|
409
416
|
|
|
410
417
|
// the compile input key of a root-relative path in an xcode layout.
|
|
411
418
|
function xcodeInputKey(layout: XcodeLayout, file: string, relative: string): string {
|
|
412
419
|
const owner = layout.targetDirs.get(file)
|
|
413
420
|
if (owner) return path.posix.join(owner, relative)
|
|
414
|
-
|
|
415
|
-
|
|
421
|
+
const checkouts = remoteSwiftCheckouts(layout.app.rootDir)
|
|
422
|
+
return file.startsWith(`${checkouts}${path.sep}`)
|
|
423
|
+
? path.posix.join(
|
|
424
|
+
XCODE_PACKAGES_DIR,
|
|
425
|
+
path.relative(checkouts, file).split(path.sep).join('/'),
|
|
426
|
+
)
|
|
416
427
|
: relative
|
|
417
428
|
}
|
|
418
429
|
|
|
@@ -485,7 +496,8 @@ function xcodeLayout(root: string): XcodeLayout | null {
|
|
|
485
496
|
for (const standin of standins)
|
|
486
497
|
targets.push({
|
|
487
498
|
name: standin.module,
|
|
488
|
-
path: path.posix.join(
|
|
499
|
+
path: path.posix.join(SWIFT_STANDINS_DIR, standin.module),
|
|
500
|
+
dependencies: standin.dependencies?.map((name) => ({ kind: 'target', name })),
|
|
489
501
|
executable: false,
|
|
490
502
|
resources: false,
|
|
491
503
|
exclude: [],
|
|
@@ -497,7 +509,7 @@ function xcodeLayout(root: string): XcodeLayout | null {
|
|
|
497
509
|
const packageConfiguration = app.configuration === 'Debug' ? 'debug' : 'release'
|
|
498
510
|
for (const target of packages.targets) {
|
|
499
511
|
const relative = path.relative(
|
|
500
|
-
target.remote ?
|
|
512
|
+
target.remote ? remoteSwiftCheckouts(root) : root,
|
|
501
513
|
target.dir,
|
|
502
514
|
)
|
|
503
515
|
if (
|
|
@@ -521,6 +533,13 @@ function xcodeLayout(root: string): XcodeLayout | null {
|
|
|
521
533
|
)
|
|
522
534
|
.map((define) => define.name),
|
|
523
535
|
],
|
|
536
|
+
linkedLibraries: target.packageLibraries
|
|
537
|
+
.filter(
|
|
538
|
+
(library) =>
|
|
539
|
+
library.configuration === null ||
|
|
540
|
+
library.configuration === packageConfiguration,
|
|
541
|
+
)
|
|
542
|
+
.map((library) => library.name),
|
|
524
543
|
})
|
|
525
544
|
if (target.resources) {
|
|
526
545
|
resourceBundles.push({
|
|
@@ -565,14 +584,19 @@ function xcodeManifest(layout: XcodeLayout): string {
|
|
|
565
584
|
...(target.mainActorIsolation ? ['.defaultIsolation(MainActor.self)'] : []),
|
|
566
585
|
...(target.defines ?? []).map((name) => `.define(${JSON.stringify(name)})`),
|
|
567
586
|
].join(', ')}]`
|
|
587
|
+
const libraries = (target: SwiftPackageTarget) =>
|
|
588
|
+
target.linkedLibraries?.length
|
|
589
|
+
? `, linkerSettings: [${target.linkedLibraries.map((name) => `.linkedLibrary(${JSON.stringify(name)})`).join(', ')}]`
|
|
590
|
+
: ''
|
|
568
591
|
return [
|
|
569
592
|
'// swift-tools-version: 6.2',
|
|
570
593
|
'import PackageDescription',
|
|
571
594
|
'let package = Package(name: "app", targets: [',
|
|
572
595
|
` .executableTarget(name: "${layout.app.moduleName}", path: "${XCODE_APP_DIR}", ${settings(layout.app)}),`,
|
|
573
|
-
...layout.targets.map(
|
|
574
|
-
|
|
575
|
-
` .
|
|
596
|
+
...layout.targets.map((target) =>
|
|
597
|
+
target.systemLibrary
|
|
598
|
+
? ` .systemLibrary(name: "${target.name}", path: "${target.path}"),`
|
|
599
|
+
: ` .target(name: "${target.name}", dependencies: ${JSON.stringify((target.dependencies ?? []).map((dependency) => dependency.name))}, path: "${target.path}", ${target.exclude.length > 0 ? `exclude: ${JSON.stringify(target.exclude)}, ` : ''}${target.resources ? 'resources: [], ' : ''}${settings(target)}${libraries(target)}),`,
|
|
576
600
|
),
|
|
577
601
|
'])',
|
|
578
602
|
'',
|
|
@@ -587,6 +611,17 @@ function packageFiles(root: string): string[] {
|
|
|
587
611
|
return xcode ? xcode.files : sourceTreeFiles(canonicalRoot, canonicalRoot)
|
|
588
612
|
}
|
|
589
613
|
|
|
614
|
+
// remote dependencies are enumerated from peach's checkout cache, while app
|
|
615
|
+
// inputs retain their selected-project boundary and all symlink checks.
|
|
616
|
+
function readPackageFile(root: string, file: string, maxBytes: number): Buffer {
|
|
617
|
+
if (xcodeProjects.has(root)) {
|
|
618
|
+
const checkouts = remoteSwiftCheckouts(root)
|
|
619
|
+
if (file.startsWith(`${checkouts}${path.sep}`))
|
|
620
|
+
return readSwiftSourceFile(checkouts, file, maxBytes)
|
|
621
|
+
}
|
|
622
|
+
return readSwiftSourceFile(root, file, maxBytes)
|
|
623
|
+
}
|
|
624
|
+
|
|
590
625
|
// every input file under `dir`, which must stay inside `canonicalRoot`.
|
|
591
626
|
function sourceTreeFiles(dir: string, canonicalRoot: string): string[] {
|
|
592
627
|
const found: string[] = []
|
|
@@ -980,6 +1015,13 @@ export function buildSwiftBundleFiles(
|
|
|
980
1015
|
const disk = path.join(packagePath, ...file.split('/'))
|
|
981
1016
|
const bytes = readPackageFile(packagePath, disk, limits.resourceBytes)
|
|
982
1017
|
addFile(bundlePath === '.' ? base : `${bundlePath}/${base}`, bytes)
|
|
1018
|
+
if (base.endsWith('.xcstrings')) {
|
|
1019
|
+
for (const [name, table] of Object.entries(
|
|
1020
|
+
compileStringCatalog(bytes.toString('utf8'), base.slice(0, -10)),
|
|
1021
|
+
)) {
|
|
1022
|
+
addFile(bundlePath === '.' ? name : `${bundlePath}/${name}`, Buffer.from(table))
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
983
1025
|
}
|
|
984
1026
|
}
|
|
985
1027
|
return { files, bundles, watched }
|
|
@@ -1033,6 +1075,8 @@ export function sourceStamp(
|
|
|
1033
1075
|
const hash = createHash('sha256')
|
|
1034
1076
|
hash.update(packagePath)
|
|
1035
1077
|
hash.update(JSON.stringify(xcodeProjects.get(packagePath)?.behavior ?? {}))
|
|
1078
|
+
const libraries = projects.get(packagePath)?.libraries
|
|
1079
|
+
if (libraries) hash.update(JSON.stringify(libraries))
|
|
1036
1080
|
const projectInputs = new Set(packageFiles(packagePath))
|
|
1037
1081
|
for (const file of [...projectInputs].sort()) {
|
|
1038
1082
|
hash.update(path.relative(packagePath, file))
|
|
@@ -1043,6 +1087,24 @@ export function sourceStamp(
|
|
|
1043
1087
|
return hash.digest('hex').slice(0, 16)
|
|
1044
1088
|
}
|
|
1045
1089
|
|
|
1090
|
+
// sourceStamp's inputs read by content and relative to the package, so
|
|
1091
|
+
// another process's build copy of the same sources stamps the same.
|
|
1092
|
+
export function sourceContentStamp(
|
|
1093
|
+
packagePath: string,
|
|
1094
|
+
moduleRootOverride?: string,
|
|
1095
|
+
): string {
|
|
1096
|
+
const hash = createHash('sha256')
|
|
1097
|
+
hash.update(JSON.stringify(xcodeProjects.get(packagePath)?.behavior ?? {}))
|
|
1098
|
+
const libraries = projects.get(packagePath)?.libraries
|
|
1099
|
+
if (libraries) hash.update(JSON.stringify(libraries))
|
|
1100
|
+
for (const file of [...new Set(packageFiles(packagePath))].sort()) {
|
|
1101
|
+
hash.update(path.relative(packagePath, file))
|
|
1102
|
+
hash.update(fs.readFileSync(file))
|
|
1103
|
+
}
|
|
1104
|
+
hash.update(moduleStamp(packagePath, moduleRootOverride, false))
|
|
1105
|
+
return hash.digest('hex').slice(0, 16)
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1046
1108
|
// the linked module's part of the source stamp
|
|
1047
1109
|
function moduleStamp(
|
|
1048
1110
|
packagePath: string,
|
|
@@ -1092,15 +1154,19 @@ export function icuUrl(origin: string, artifact: SwiftArtifact): string {
|
|
|
1092
1154
|
// keyed by the stand-in target it links into; its own source then defines
|
|
1093
1155
|
// nothing, so the library's functions run instead of the traps. one library
|
|
1094
1156
|
// links once, through the first module its framework ships.
|
|
1095
|
-
function standinLibraries(
|
|
1157
|
+
export function standinLibraries(
|
|
1158
|
+
binaries: readonly SwiftPackageBinary[],
|
|
1159
|
+
standins: readonly SwiftStandin[],
|
|
1160
|
+
rootDir: string,
|
|
1161
|
+
): Map<string, SwiftStandin[]> {
|
|
1096
1162
|
const linked = new Map<string, SwiftStandin[]>()
|
|
1097
|
-
for (const binary of
|
|
1098
|
-
const modules =
|
|
1163
|
+
for (const binary of binaries) {
|
|
1164
|
+
const modules = standins.filter(
|
|
1099
1165
|
(standin) =>
|
|
1100
1166
|
standin.target === binary.target && `${standin.module}.c` in standin.files,
|
|
1101
1167
|
)
|
|
1102
1168
|
if (modules.length === 0) continue
|
|
1103
|
-
const library = standinLibrary(binary,
|
|
1169
|
+
const library = standinLibrary(binary, rootDir)
|
|
1104
1170
|
if (!library) continue
|
|
1105
1171
|
linked.set(library, modules)
|
|
1106
1172
|
}
|
|
@@ -1131,7 +1197,7 @@ function packageSources(
|
|
|
1131
1197
|
files['Package.swift'] = xcodeManifest(xcode)
|
|
1132
1198
|
for (const standin of xcode.standins)
|
|
1133
1199
|
for (const [file, text] of Object.entries(standin.files))
|
|
1134
|
-
files[path.posix.join(
|
|
1200
|
+
files[path.posix.join(SWIFT_STANDINS_DIR, standin.module, file)] =
|
|
1135
1201
|
linked.has(standin) && file === `${standin.module}.c`
|
|
1136
1202
|
? `// ${standin.target}'s WebAssembly library defines ${standin.module}'s functions.\n`
|
|
1137
1203
|
: text
|
|
@@ -1152,7 +1218,9 @@ interface SwiftPackageSnapshot {
|
|
|
1152
1218
|
|
|
1153
1219
|
function packageSnapshot(project: SwiftProject, entry: string): SwiftPackageSnapshot {
|
|
1154
1220
|
const layout = xcodeLayout(project.packagePath)
|
|
1155
|
-
const linked = layout
|
|
1221
|
+
const linked = layout
|
|
1222
|
+
? standinLibraries(layout.binaries, layout.standins, layout.app.rootDir)
|
|
1223
|
+
: new Map<string, SwiftStandin[]>()
|
|
1156
1224
|
const files = packageSources(project.packagePath, new Set([...linked.values()].flat()))
|
|
1157
1225
|
const native = nativeSourceDescriptor(project.packagePath)
|
|
1158
1226
|
return {
|
|
@@ -1166,9 +1234,7 @@ function packageSnapshot(project: SwiftProject, entry: string): SwiftPackageSnap
|
|
|
1166
1234
|
.relative(project.packagePath, file)
|
|
1167
1235
|
.split(path.sep)
|
|
1168
1236
|
.join(path.posix.sep)
|
|
1169
|
-
return layout
|
|
1170
|
-
? xcodeInputKey(layout, file, relative)
|
|
1171
|
-
: relative
|
|
1237
|
+
return layout ? xcodeInputKey(layout, file, relative) : relative
|
|
1172
1238
|
}),
|
|
1173
1239
|
),
|
|
1174
1240
|
resources: buildSwiftResourceTable(project, '', files),
|
|
@@ -1178,14 +1244,17 @@ function packageSnapshot(project: SwiftProject, entry: string): SwiftPackageSnap
|
|
|
1178
1244
|
files,
|
|
1179
1245
|
),
|
|
1180
1246
|
native,
|
|
1181
|
-
...(linked.size > 0
|
|
1247
|
+
...(linked.size > 0 || project.libraries
|
|
1182
1248
|
? {
|
|
1183
|
-
libraries:
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1249
|
+
libraries: {
|
|
1250
|
+
...project.libraries,
|
|
1251
|
+
...Object.fromEntries(
|
|
1252
|
+
[...linked].map(([library, [first]]) => [
|
|
1253
|
+
first!.module,
|
|
1254
|
+
fs.readFileSync(library).toString('base64'),
|
|
1255
|
+
]),
|
|
1256
|
+
),
|
|
1257
|
+
},
|
|
1189
1258
|
}
|
|
1190
1259
|
: {}),
|
|
1191
1260
|
...(native
|
|
@@ -1252,7 +1321,7 @@ async function compile(
|
|
|
1252
1321
|
linkedModuleRoots(packagePath).find((root) =>
|
|
1253
1322
|
fs.existsSync(path.join(root, 'toolchain.json')),
|
|
1254
1323
|
) ??
|
|
1255
|
-
(await swiftDistributionRoot())
|
|
1324
|
+
(await swiftDistributionRoot(signal))
|
|
1256
1325
|
if (!moduleRoot)
|
|
1257
1326
|
return {
|
|
1258
1327
|
...(await compileRemote(packagePath, snapshot, undefined, signal)),
|
|
@@ -1290,13 +1359,20 @@ async function compile(
|
|
|
1290
1359
|
if (process.env.CONTRAST_SWIFT_BUILD_ROOT)
|
|
1291
1360
|
return process.env.CONTRAST_SWIFT_BUILD_ROOT
|
|
1292
1361
|
const resolvedPath = path.resolve(packagePath)
|
|
1362
|
+
// units of one app share a root so the module cache stays warm.
|
|
1363
|
+
// peach-expo-swift/<app>/<scope>/<package> shares <app>/<scope>, and
|
|
1364
|
+
// the older <app>/<package> layout shares <app>. the scope is this
|
|
1365
|
+
// process, so two servers do not compile into one tree.
|
|
1366
|
+
const parts = resolvedPath.split(path.sep)
|
|
1367
|
+
const expoAt = parts.lastIndexOf('peach-expo-swift')
|
|
1368
|
+
const scoped = expoAt >= 0 && parts.length >= expoAt + 4
|
|
1293
1369
|
const buildKeyPath =
|
|
1294
|
-
|
|
1295
|
-
?
|
|
1370
|
+
expoAt >= 0 && parts.length >= expoAt + 3
|
|
1371
|
+
? parts.slice(0, scoped ? expoAt + 3 : expoAt + 2).join(path.sep)
|
|
1296
1372
|
: resolvedPath
|
|
1297
1373
|
return path.join(
|
|
1298
|
-
|
|
1299
|
-
'
|
|
1374
|
+
cacheDir(),
|
|
1375
|
+
'swift-builds',
|
|
1300
1376
|
createHash('sha256')
|
|
1301
1377
|
.update(`${moduleRoot}\0${buildKeyPath}`)
|
|
1302
1378
|
.digest('hex')
|
|
@@ -1482,7 +1558,7 @@ async function compileRemote(
|
|
|
1482
1558
|
hosted?.authorization ?? (auth ? authHeaderValue(auth) : undefined)
|
|
1483
1559
|
const signal = AbortSignal.any([
|
|
1484
1560
|
...(ownerSignal ? [ownerSignal] : []),
|
|
1485
|
-
AbortSignal.timeout(
|
|
1561
|
+
AbortSignal.timeout(DEFAULT_SWIFT_COMPILE_BUILD_MS + 60_000),
|
|
1486
1562
|
])
|
|
1487
1563
|
const submit = httpSwiftCompileSubmit({
|
|
1488
1564
|
endpoint: origin,
|
|
@@ -1559,8 +1635,11 @@ export function artifactFor(
|
|
|
1559
1635
|
project: SwiftProject,
|
|
1560
1636
|
entry: string,
|
|
1561
1637
|
moduleRoot?: string,
|
|
1638
|
+
signal: AbortSignal | undefined = project.signal,
|
|
1562
1639
|
): Promise<SwiftArtifact> {
|
|
1563
|
-
project.signal
|
|
1640
|
+
if (project.signal && signal !== project.signal)
|
|
1641
|
+
signal = signal ? AbortSignal.any([project.signal, signal]) : project.signal
|
|
1642
|
+
signal?.throwIfAborted()
|
|
1564
1643
|
const stamp = sourceStamp(project.packagePath, moduleRoot, Boolean(project.hosted))
|
|
1565
1644
|
if (project.artifact?.stamp === stamp) return Promise.resolve(project.artifact)
|
|
1566
1645
|
if (project.building) {
|
|
@@ -1569,13 +1648,27 @@ export function artifactFor(
|
|
|
1569
1648
|
// and ask again, which starts the build these sources need; joining it
|
|
1570
1649
|
// would emit the older artifact and nothing would trigger another load.
|
|
1571
1650
|
const running = project.building
|
|
1572
|
-
if (running.stamp === stamp) return running.promise
|
|
1573
|
-
const again = () => artifactFor(project, entry, moduleRoot)
|
|
1574
|
-
|
|
1651
|
+
if (running.stamp === stamp && running.signal === signal) return running.promise
|
|
1652
|
+
const again = () => artifactFor(project, entry, moduleRoot, signal)
|
|
1653
|
+
const queued = running.promise.then(again, again)
|
|
1654
|
+
if (!signal) return queued
|
|
1655
|
+
return new Promise<SwiftArtifact>((resolve, reject) => {
|
|
1656
|
+
const stop = () =>
|
|
1657
|
+
reject(
|
|
1658
|
+
signal.reason instanceof Error
|
|
1659
|
+
? signal.reason
|
|
1660
|
+
: new Error('Swift compilation canceled', { cause: signal.reason }),
|
|
1661
|
+
)
|
|
1662
|
+
signal.addEventListener('abort', stop, { once: true })
|
|
1663
|
+
if (signal.aborted) stop()
|
|
1664
|
+
queued
|
|
1665
|
+
.then(resolve, reject)
|
|
1666
|
+
.finally(() => signal.removeEventListener('abort', stop))
|
|
1667
|
+
})
|
|
1575
1668
|
}
|
|
1576
1669
|
const snapshot = packageSnapshot(project, entry)
|
|
1577
1670
|
if (sourceStamp(project.packagePath, moduleRoot, Boolean(project.hosted)) !== stamp) {
|
|
1578
|
-
return artifactFor(project, entry, moduleRoot)
|
|
1671
|
+
return artifactFor(project, entry, moduleRoot, signal)
|
|
1579
1672
|
}
|
|
1580
1673
|
// a save that changed only view builder literals keeps the running bytes
|
|
1581
1674
|
const compiled = project.artifact?.compiled
|
|
@@ -1600,7 +1693,7 @@ export function artifactFor(
|
|
|
1600
1693
|
moduleRoot,
|
|
1601
1694
|
project.hosted,
|
|
1602
1695
|
snapshot,
|
|
1603
|
-
|
|
1696
|
+
signal,
|
|
1604
1697
|
).then(
|
|
1605
1698
|
(artifact) => {
|
|
1606
1699
|
project.artifact = artifact
|
|
@@ -1620,9 +1713,9 @@ export function artifactFor(
|
|
|
1620
1713
|
sourceStamp(project.packagePath, moduleRoot, Boolean(project.hosted)) ===
|
|
1621
1714
|
artifact.stamp
|
|
1622
1715
|
? artifact
|
|
1623
|
-
: artifactFor(project, entry, moduleRoot),
|
|
1716
|
+
: artifactFor(project, entry, moduleRoot, signal),
|
|
1624
1717
|
)
|
|
1625
|
-
project.building = { stamp, promise: fresh }
|
|
1718
|
+
project.building = { stamp, promise: fresh, signal }
|
|
1626
1719
|
return fresh
|
|
1627
1720
|
}
|
|
1628
1721
|
|