peachsim 0.1.1640 → 0.1.1641

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.
Files changed (109) hide show
  1. package/README.md +5 -0
  2. package/cli/app-stubs.ts +38 -13
  3. package/cli/cloud-session.ts +16 -0
  4. package/cli/commands/assert.ts +17 -4
  5. package/cli/commands/box.ts +33 -19
  6. package/cli/commands/doctor.ts +24 -15
  7. package/cli/commands/inspect/actions.ts +3 -2
  8. package/cli/commands/inspect/core.ts +0 -8
  9. package/cli/commands/inspect.ts +0 -14
  10. package/cli/commands/platform.ts +2 -1
  11. package/cli/commands/record.ts +64 -28
  12. package/cli/commands/swift.ts +10 -2
  13. package/cli/commands/three-mode.ts +4 -38
  14. package/cli/commands/upload.ts +8 -8
  15. package/cli/flow-live-status.ts +0 -2
  16. package/cli/main.ts +1 -1
  17. package/cli/ws-bridge.ts +0 -6
  18. package/detox/attempt-events.cjs +41 -5
  19. package/detox/index.ts +155 -109
  20. package/detox/jest-environment.cjs +5 -1
  21. package/dist-lib/agent-daemon-client.cjs +1 -1
  22. package/dist-lib/agent-events.cjs +1 -1
  23. package/dist-lib/attached-projects.cjs +1 -1
  24. package/dist-lib/auth/shared-session.cjs +1 -1
  25. package/dist-lib/backend-origin.cjs +10 -1
  26. package/dist-lib/beta.cjs +1 -1
  27. package/dist-lib/beta.mjs +1 -1
  28. package/dist-lib/bridge-constants.cjs +1 -1
  29. package/dist-lib/bridge-contract-input.cjs +1 -1
  30. package/dist-lib/bridge-contract-input.mjs +1 -1
  31. package/dist-lib/bridge-contract.cjs +1 -1
  32. package/dist-lib/bridge-contract.mjs +1 -1
  33. package/dist-lib/bundle-channel.cjs +2059 -1368
  34. package/dist-lib/capture-contract.cjs +1 -1
  35. package/dist-lib/capture-contract.mjs +1 -1
  36. package/dist-lib/cloud-contract.cjs +1 -1
  37. package/dist-lib/cloud-contract.mjs +1 -1
  38. package/dist-lib/cloud-test-args.cjs +1 -1
  39. package/dist-lib/cloud-test-args.mjs +1 -1
  40. package/dist-lib/cloud.cjs +1 -1
  41. package/dist-lib/cloud.mjs +1 -1
  42. package/dist-lib/config.cjs +1 -1
  43. package/dist-lib/config.mjs +1 -1
  44. package/dist-lib/detox/index.cjs +572 -125
  45. package/dist-lib/detox/jest-environment.cjs +5 -1
  46. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  47. package/dist-lib/expo-swift-bundle.cjs +1837 -1164
  48. package/dist-lib/expo-swift-metadata.cjs +1 -1
  49. package/dist-lib/expo-swift-metadata.mjs +1 -1
  50. package/dist-lib/home-paths.cjs +1 -1
  51. package/dist-lib/host/bridge-host.cjs +2290 -1616
  52. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  53. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  54. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  55. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  56. package/dist-lib/host/websocket-proxy.cjs +1 -1
  57. package/dist-lib/index.cjs +44 -17
  58. package/dist-lib/jump-to-source-babel.cjs +1 -1
  59. package/dist-lib/jump-to-source-native.cjs +1 -1
  60. package/dist-lib/kotlin-host.cjs +1 -1
  61. package/dist-lib/menu.cjs +1 -1
  62. package/dist-lib/menu.mjs +1 -1
  63. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  64. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  65. package/dist-lib/metro-production-bundle.cjs +1 -1
  66. package/dist-lib/metro-production-bundle.mjs +1 -1
  67. package/dist-lib/metro.cjs +33 -13
  68. package/dist-lib/native-app-identity-project.cjs +7 -4
  69. package/dist-lib/native-app-identity-project.mjs +7 -4
  70. package/dist-lib/native-app-identity.cjs +1 -1
  71. package/dist-lib/native-app-identity.mjs +1 -1
  72. package/dist-lib/native-firebase-metadata.cjs +1 -1
  73. package/dist-lib/native-firebase-metadata.mjs +1 -1
  74. package/dist-lib/plist.cjs +1 -1
  75. package/dist-lib/plist.mjs +1 -1
  76. package/dist-lib/profiles.cjs +1 -1
  77. package/dist-lib/public-brand.cjs +1 -1
  78. package/dist-lib/react-native-host-modules.cjs +1 -1
  79. package/dist-lib/react-native-host-modules.mjs +1 -1
  80. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  81. package/dist-lib/sdk.cjs +4 -3
  82. package/dist-lib/sdk.mjs +4 -3
  83. package/dist-lib/skills.cjs +2823 -2109
  84. package/dist-lib/swift-compile.cjs +370 -239
  85. package/dist-lib/swift-compile.mjs +368 -230
  86. package/dist-lib/swift-package.cjs +1 -1
  87. package/dist-lib/swift-package.mjs +1 -1
  88. package/dist-lib/swift-submit.cjs +8 -3
  89. package/dist-lib/swift-submit.mjs +8 -3
  90. package/dist-lib/swift-toolchain.cjs +1 -1
  91. package/dist-lib/swift.cjs +1 -1
  92. package/dist-lib/vite-swift.cjs +1757 -1108
  93. package/dist-lib/vite.cjs +1 -1
  94. package/dist-lib/worklet-executable.cjs +1 -1
  95. package/dist-lib/worklet-executable.mjs +1 -1
  96. package/package.json +1 -1
  97. package/src/backend-origin.ts +8 -0
  98. package/src/react-native-bridge-modules.ts +89 -76
  99. package/src/swift-compile.ts +134 -107
  100. package/src/swift-distribution.lock.json +9 -8
  101. package/src/swift-host.ts +6 -4
  102. package/src/swift-objc-compile.ts +385 -193
  103. package/src/swift-objc-metadata.ts +204 -0
  104. package/src/swift-objc-sdk.ts +195 -2
  105. package/src/swift-package-manifest.ts +6 -3
  106. package/src/swift-root-lock.ts +153 -0
  107. package/src/swift-submit.ts +15 -2
  108. package/src/swiftContract.ts +12 -0
  109. package/src/xcode-project.ts +10 -3
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1641 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1641 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1641 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
 
3
3
  // src/worklet-executable.ts
4
4
  import { parse } from "acorn";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peachsim",
3
- "version": "0.1.1640",
3
+ "version": "0.1.1641",
4
4
  "description": "Vite and Metro plugins, testing drivers, and SDK exports for peach.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -29,6 +29,14 @@ export function isLoopbackHost(hostname: string): boolean {
29
29
  )
30
30
  }
31
31
 
32
+ export function isLoopbackOrigin(origin: string): boolean {
33
+ try {
34
+ return isLoopbackHost(new URL(origin).hostname)
35
+ } catch {
36
+ return false
37
+ }
38
+ }
39
+
32
40
  // a browser page may use a local peach bridge (its proxies, websocket,
33
41
  // settings and account routes) only when this machine served the page: a
34
42
  // loopback origin, or the bridge's own host when a tunnel or reverse proxy
@@ -6,7 +6,11 @@
6
6
  import fs from 'node:fs'
7
7
  import path from 'node:path'
8
8
  import { swiftCodeMask, swiftSourceClosure } from './swift-compile'
9
- import { listXcodeAppTargets, readXcodeAppTarget } from './xcode-project'
9
+ import {
10
+ findXcodeProject,
11
+ listXcodeAppTargets,
12
+ readXcodeAppTarget,
13
+ } from './xcode-project'
10
14
  import type { ExpoSwiftSourceUnit } from './expoSwiftContract'
11
15
 
12
16
  interface MethodInfo {
@@ -312,9 +316,9 @@ function swiftClassFor(objcName: string, files: Record<string, string>): string
312
316
  export const BRIDGE_MODULE_FILE_PREFIX = '__peach_react_module_'
313
317
 
314
318
  /**
315
- * the app target's legacy bridge modules, one compile unit each, or null
316
- * when the app has no iOS project or registers none. a module the host
317
- * cannot provide is a unit with a diagnostic instead of files.
319
+ * the app and its framework targets' legacy bridge modules, one compile unit
320
+ * each, or null when the app has no iOS project or registers none. a module
321
+ * the host cannot provide is a unit with a diagnostic instead of files.
318
322
  */
319
323
  export function collectBridgeModuleSources(
320
324
  appRoot: string,
@@ -327,20 +331,13 @@ export function collectBridgeModuleSources(
327
331
  const iosRoot = path.join(appRoot, 'ios')
328
332
  if (!fs.existsSync(iosRoot)) return null
329
333
  const projectRoot = fs.realpathSync(appRoot)
330
- const projects = fs
331
- .readdirSync(iosRoot, { withFileTypes: true })
332
- .filter((entry) => entry.isDirectory() && entry.name.endsWith('.xcodeproj'))
333
- .map((entry) => path.join(iosRoot, entry.name))
334
- const targets = projects.flatMap((project) =>
335
- listXcodeAppTargets(project, { requireSources: false }).map((name) => ({
336
- project,
337
- name,
338
- })),
339
- )
334
+ const project = findXcodeProject(iosRoot)
335
+ if (!project) return null
336
+ const targets = listXcodeAppTargets(project, { requireSources: false })
340
337
  // RCTRegisterModule runs in the one app binary; several targets are
341
338
  // several apps, and nothing here says which one this bundle runs in
342
339
  if (targets.length !== 1) return null
343
- const app = readXcodeAppTarget(targets[0]!.project, targets[0]!.name, 'Release', {
340
+ const app = readXcodeAppTarget(project, targets[0]!, 'Release', {
344
341
  requireSources: false,
345
342
  })
346
343
  const own = (file: string) => {
@@ -360,72 +357,88 @@ export function collectBridgeModuleSources(
360
357
  throw new Error(`${file}: app native source outside ${projectRoot}`)
361
358
  return true
362
359
  }
363
- const objc = app.allSources.filter((file) => /\.mm?$/.test(file) && own(file))
364
- if (objc.length === 0) return null
365
- // headers declare the superclasses; they sit beside the sources that import them
366
- const headers = [...new Set(objc.map((file) => path.dirname(file)))].flatMap((dir) =>
367
- fs
368
- .readdirSync(dir)
369
- .filter((name) => name.endsWith('.h'))
370
- .sort()
371
- .map((name) => path.join(dir, name))
372
- .filter(own),
373
- )
374
- const declarations = parseObjectiveCModules(
375
- [...objc, ...headers].map((file) => ({ file, text: fs.readFileSync(file, 'utf8') })),
376
- )
377
- if (declarations.modules.length === 0) return null
378
- const namespace = app.moduleName.replace(/[^A-Za-z0-9_]/g, '_')
379
- const swiftFiles: Record<string, string> = {}
380
- for (const file of app.sources.filter(own)) {
381
- const relative = path.relative(projectRoot, fs.realpathSync(file))
382
- swiftFiles[`Sources/${namespace}/${relative.split(path.sep).join('/')}`] =
383
- `#sourceLocation(file: ${JSON.stringify(file)}, line: 1)\n${fs.readFileSync(file, 'utf8')}`
384
- }
385
360
  const units: ExpoSwiftSourceUnit[] = []
386
361
  const failures: { namespace: string; diagnostic: string }[] = []
387
- const seen = new Set<string>()
388
- for (const module of declarations.modules) {
362
+ // the reader owns dependency order and visits a shared framework only once.
363
+ // each target keeps its own Swift module identity and compilation settings.
364
+ for (const target of [...app.frameworks, app]) {
389
365
  signal?.throwIfAborted()
390
- if (seen.has(module.className)) continue
391
- seen.add(module.className)
392
- const moduleId = `${namespace}.${module.className}`
393
- const methods = declarations.methods.get(module.className) ?? []
394
- const swiftClass = module.extern ? swiftClassFor(module.className, swiftFiles) : null
395
- const unit: ExpoSwiftSourceUnit = {
396
- namespace,
397
- moduleId,
398
- classes: [],
399
- bridgeModules: [],
400
- files: {},
401
- frameworks: [],
402
- swiftLanguageMode: app.languageMode,
366
+ const objc = [
367
+ ...new Set(
368
+ target.allSources
369
+ .filter((file) => /\.mm?$/.test(file) && own(file))
370
+ .map((file) => fs.realpathSync(file)),
371
+ ),
372
+ ]
373
+ if (objc.length === 0) continue
374
+ // headers declare the superclasses; they sit beside the sources that import them
375
+ const headers = [...new Set(objc.map((file) => path.dirname(file)))].flatMap((dir) =>
376
+ fs
377
+ .readdirSync(dir)
378
+ .filter((name) => name.endsWith('.h'))
379
+ .sort()
380
+ .map((name) => path.join(dir, name))
381
+ .filter(own),
382
+ )
383
+ const declarations = parseObjectiveCModules(
384
+ [...objc, ...headers].map((file) => ({
385
+ file,
386
+ text: fs.readFileSync(file, 'utf8'),
387
+ })),
388
+ )
389
+ if (declarations.modules.length === 0) continue
390
+ const namespace = target.moduleName.replace(/[^A-Za-z0-9_]/g, '_')
391
+ const swiftFiles: Record<string, string> = {}
392
+ for (const file of target.sources.filter(own)) {
393
+ const relative = path.relative(projectRoot, fs.realpathSync(file))
394
+ swiftFiles[`Sources/${namespace}/${relative.split(path.sep).join('/')}`] =
395
+ `#sourceLocation(file: ${JSON.stringify(file)}, line: 1)\n${fs.readFileSync(file, 'utf8')}`
403
396
  }
404
- if (swiftClass) {
405
- const register = `__peachReactModule_${module.className}`
406
- unit.files = swiftSourceClosure(swiftFiles, [swiftClass])
407
- unit.files[
408
- `Sources/${namespace}/${BRIDGE_MODULE_FILE_PREFIX}${module.className}.swift`
409
- ] =
410
- `import React\n\n// ${module.file}: RCT_EXTERN_MODULE(${module.className})\npublic func ${register}() -> RCTModuleHost.Registration {\n RCTModuleHost.Registration(className: ${swiftString(module.className)}, moduleName: ${swiftString(module.jsName)}, methods: ${methodsLiteral(methods)}, make: { ${swiftClass}() })\n}\n`
411
- unit.bridgeModules = [`${namespace}.${register}()`]
412
- } else {
413
- const superclass = compiledSuperclass(module.className, declarations.superclasses)
414
- const selectors = declarations.implementations.get(module.className)
415
- if (!selectors || !superclass) {
416
- failures.push({
417
- namespace: moduleId,
418
- diagnostic: !selectors
419
- ? `${module.file}: no Swift class or Objective-C @implementation provides ${module.className}`
420
- : `${module.file}: ${module.className}'s superclass ${declarations.superclasses.get(module.className) ?? '(none)'} is Objective-C, which Peach does not compile`,
421
- })
422
- continue
397
+ const seen = new Set<string>()
398
+ for (const module of declarations.modules) {
399
+ signal?.throwIfAborted()
400
+ if (seen.has(module.className)) continue
401
+ seen.add(module.className)
402
+ const moduleId = `${namespace}.${module.className}`
403
+ const methods = declarations.methods.get(module.className) ?? []
404
+ const swiftClass = module.extern
405
+ ? swiftClassFor(module.className, swiftFiles)
406
+ : null
407
+ const unit: ExpoSwiftSourceUnit = {
408
+ namespace,
409
+ moduleId,
410
+ classes: [],
411
+ bridgeModules: [],
412
+ files: {},
413
+ frameworks: [],
414
+ swiftLanguageMode: target.languageMode,
415
+ }
416
+ if (swiftClass) {
417
+ const register = `__peachReactModule_${module.className}`
418
+ unit.files = swiftSourceClosure(swiftFiles, [swiftClass])
419
+ unit.files[
420
+ `Sources/${namespace}/${BRIDGE_MODULE_FILE_PREFIX}${module.className}.swift`
421
+ ] =
422
+ `import React\n\n// ${module.file}: RCT_EXTERN_MODULE(${module.className})\npublic func ${register}() -> RCTModuleHost.Registration {\n RCTModuleHost.Registration(className: ${swiftString(module.className)}, moduleName: ${swiftString(module.jsName)}, methods: ${methodsLiteral(methods)}, make: { ${swiftClass}() })\n}\n`
423
+ unit.bridgeModules = [`${namespace}.${register}()`]
424
+ } else {
425
+ const superclass = compiledSuperclass(module.className, declarations.superclasses)
426
+ const selectors = declarations.implementations.get(module.className)
427
+ if (!selectors || !superclass) {
428
+ failures.push({
429
+ namespace: moduleId,
430
+ diagnostic: !selectors
431
+ ? `${module.file}: no Swift class or Objective-C @implementation provides ${module.className}`
432
+ : `${module.file}: ${module.className}'s superclass ${declarations.superclasses.get(module.className) ?? '(none)'} is Objective-C, which Peach does not compile`,
433
+ })
434
+ continue
435
+ }
436
+ unit.bridgeModules = [
437
+ `RCTModuleHost.Registration(className: ${swiftString(module.className)}, moduleName: ${swiftString(module.jsName)}, methods: ${methodsLiteral(methods)}, objectiveCSelectors: ${JSON.stringify([...selectors].sort())}, superclass: { ${superclass}() })`,
438
+ ]
423
439
  }
424
- unit.bridgeModules = [
425
- `RCTModuleHost.Registration(className: ${swiftString(module.className)}, moduleName: ${swiftString(module.jsName)}, methods: ${methodsLiteral(methods)}, objectiveCSelectors: ${JSON.stringify([...selectors].sort())}, superclass: { ${superclass}() })`,
426
- ]
440
+ units.push(unit)
427
441
  }
428
- units.push(unit)
429
442
  }
430
- return { units, failures }
443
+ return units.length === 0 && failures.length === 0 ? null : { units, failures }
431
444
  }
@@ -33,6 +33,7 @@ import {
33
33
  swiftTargetModuleName,
34
34
  type SwiftPackageTarget,
35
35
  } from './swift-package'
36
+ import { withSwiftRootLock } from './swift-root-lock'
36
37
  import { swiftEnvironment, swiftSdkOptions, type SwiftWasmSdk } from './swift-toolchain'
37
38
  import { isSwiftResourceBundles } from './swiftContract'
38
39
  import type { SwiftImageUpload } from './swift-submit'
@@ -178,6 +179,8 @@ export interface SwiftCompileOptions {
178
179
  toolchainBin: string
179
180
  timeoutMs?: number
180
181
  signal?: AbortSignal
182
+ /** called as the compile reaches each stage, in addition to the stderr line. */
183
+ onStage?: (stage: string) => void
181
184
  }
182
185
 
183
186
  export interface SwiftCompileArtifact {
@@ -948,9 +951,10 @@ export function emitSwiftPackageManifest(args: {
948
951
  // compiled: whole, and ahead of the sdk libraries, so PeachICU's
949
952
  // reservation keeps icu's data member out. merged into one relocatable
950
953
  // object (wasm-ld -r) or linked from an archive, a protocol conformance
951
- // goes unfound at runtime or icu links twice (RAN, the Expo fixture). the
952
- // libraries the objects autolink follow, since the driver reads autolink
953
- // entries only from objects it compiled.
954
+ // goes unfound at runtime or icu links twice (RAN, the Expo fixture).
955
+ // clang loads configuration inputs before command-line libraries. forward
956
+ // the file opaquely so swift neither expands it nor scans every framework
957
+ // object for autolinks; distribution.link supplies those libraries.
954
958
  const imports = distribution
955
959
  ? `, "-I", ${JSON.stringify(path.join(moduleRoot, 'modules'))}`
956
960
  : ''
@@ -994,7 +998,7 @@ export function emitSwiftPackageManifest(args: {
994
998
  ].join(', ')
995
999
  const library =
996
1000
  distribution && distributionLink
997
- ? ['-Xlinker', `@${distributionLink.path}`, ...distribution.link]
1001
+ ? ['-Xclang-linker', `--config=${distributionLink.path}`, ...distribution.link]
998
1002
  .map((flag) => `, ${JSON.stringify(flag)}`)
999
1003
  .join('')
1000
1004
  : ''
@@ -6149,8 +6153,10 @@ export function compileSwiftProjectUnit(
6149
6153
  }
6150
6154
  const hash = swiftArtifactHash(input, options.release, moduleRoot)
6151
6155
  const sharedRoot = path.join(rnxHomeDir(), 'cache', 'swift-projects', hash)
6152
- const report = (message: string) =>
6156
+ const report = (message: string) => {
6153
6157
  console.error(`[swift-project ${hash.slice(0, 16)}] ${message}`)
6158
+ options.onStage?.(message)
6159
+ }
6154
6160
  report('checking verified artifacts')
6155
6161
  return serialize(
6156
6162
  root,
@@ -6232,88 +6238,84 @@ export function compileSwiftProjectUnit(
6232
6238
  rmSync(requestRoot, { recursive: true, force: true })
6233
6239
  }
6234
6240
  }
6235
- writeSwiftProject(layout, input, moduleRoot)
6236
- const swift = path.join(options.toolchainBin, 'swift')
6237
- if (!existsSync(swift)) {
6238
- throw new Error(
6239
- `swift-compile: no swift.org toolchain at ${options.toolchainBin}; install ${options.release} and its matching wasm sdk`,
6240
- )
6241
- }
6242
- report('preparing compiler plugins')
6243
- const macroPlugins = await ensureMacroPlugins(
6244
- moduleRoot,
6245
- options.toolchainBin,
6246
- options.release,
6247
- options.signal,
6248
- )
6249
- // memoized per toolchain, so only a process's first build waits on it,
6250
- // and that one reads and gzips it while swift builds
6251
- const sdk = swiftModuleSdk(moduleRoot).wasmSdkId
6252
- const icu = swiftIcuAsset(options.toolchainBin, sdk)
6253
- const buildArgs = [
6254
- 'build',
6255
- ...swiftPackageCacheOptions(),
6256
- '--build-system',
6257
- 'native',
6258
- '-j',
6259
- BUILD_JOBS,
6260
- '--package-path',
6241
+ // the root's sources, manifest and executable are shared by every process
6242
+ // using it, so one process writes, builds and reads them at a time.
6243
+ return withSwiftRootLock(
6261
6244
  root,
6262
- ...swiftSdkOptions(),
6263
- '--swift-sdk',
6264
- sdk,
6265
- '-c',
6266
- 'release',
6267
- '-Xswiftc',
6268
- '-Osize',
6269
- '-Xswiftc',
6270
- '-num-threads',
6271
- '-Xswiftc',
6272
- BUILD_JOBS,
6273
- ...macroPlugins.flatMap((plugin) => [
6274
- '-Xswiftc',
6275
- '-load-plugin-library',
6276
- '-Xswiftc',
6277
- plugin,
6278
- ]),
6279
- ]
6280
- const buildOptions = {
6281
- env: {
6282
- ...process.env,
6283
- // shipped host plugins resolve against the selected compiler's runtime.
6284
- ...(process.platform === 'linux'
6285
- ? {
6286
- LD_LIBRARY_PATH: [
6287
- path.resolve(options.toolchainBin, '../lib/swift/linux'),
6288
- path.resolve(options.toolchainBin, '../lib/swift/host'),
6289
- process.env.LD_LIBRARY_PATH,
6290
- ]
6291
- .filter(Boolean)
6292
- .join(':'),
6293
- }
6294
- : {}),
6295
- },
6296
- maxBuffer: 64 * 1024 * 1024,
6297
- timeout: options.timeoutMs,
6298
- signal: options.signal,
6299
- }
6300
- report('compiling and linking Swift modules')
6301
- try {
6302
- await runCompiler(swift, buildArgs, buildOptions, SWIFTPM_PROGRESS, SWIFTPM_NOISE)
6303
- } catch (error) {
6304
- const output = outputOf(error)
6305
- const moduleCache = path.join(
6306
- root,
6307
- '.build',
6308
- WASM_TRIPLE,
6309
- 'release',
6310
- 'ModuleCache',
6311
- )
6312
- if (
6313
- output.includes('was compiled with module cache path') &&
6314
- existsSync(moduleCache)
6315
- ) {
6316
- rmSync(moduleCache, { recursive: true, force: true })
6245
+ async () => {
6246
+ // a process that held the root while this one waited may have built
6247
+ // this exact address.
6248
+ const built = readCompiledArtifact(path.join(sharedRoot, 'artifact'), hash)
6249
+ if (built) {
6250
+ report('reusing verified disk artifact')
6251
+ return retain(root, built)
6252
+ }
6253
+ writeSwiftProject(layout, input, moduleRoot)
6254
+ const swift = path.join(options.toolchainBin, 'swift')
6255
+ if (!existsSync(swift)) {
6256
+ throw new Error(
6257
+ `swift-compile: no swift.org toolchain at ${options.toolchainBin}; install ${options.release} and its matching wasm sdk`,
6258
+ )
6259
+ }
6260
+ report('preparing compiler plugins')
6261
+ const macroPlugins = await ensureMacroPlugins(
6262
+ moduleRoot,
6263
+ options.toolchainBin,
6264
+ options.release,
6265
+ options.signal,
6266
+ )
6267
+ // memoized per toolchain, so only a process's first build waits on it,
6268
+ // and that one reads and gzips it while swift builds
6269
+ const sdk = swiftModuleSdk(moduleRoot).wasmSdkId
6270
+ const icu = swiftIcuAsset(options.toolchainBin, sdk)
6271
+ const buildArgs = [
6272
+ 'build',
6273
+ ...swiftPackageCacheOptions(),
6274
+ '--build-system',
6275
+ 'native',
6276
+ '-j',
6277
+ BUILD_JOBS,
6278
+ '--package-path',
6279
+ root,
6280
+ ...swiftSdkOptions(),
6281
+ '--swift-sdk',
6282
+ sdk,
6283
+ '-c',
6284
+ 'release',
6285
+ '-Xswiftc',
6286
+ '-Osize',
6287
+ '-Xswiftc',
6288
+ '-num-threads',
6289
+ '-Xswiftc',
6290
+ BUILD_JOBS,
6291
+ ...macroPlugins.flatMap((plugin) => [
6292
+ '-Xswiftc',
6293
+ '-load-plugin-library',
6294
+ '-Xswiftc',
6295
+ plugin,
6296
+ ]),
6297
+ ]
6298
+ const buildOptions = {
6299
+ env: {
6300
+ ...process.env,
6301
+ // shipped host plugins resolve against the selected compiler's runtime.
6302
+ ...(process.platform === 'linux'
6303
+ ? {
6304
+ LD_LIBRARY_PATH: [
6305
+ path.resolve(options.toolchainBin, '../lib/swift/linux'),
6306
+ path.resolve(options.toolchainBin, '../lib/swift/host'),
6307
+ process.env.LD_LIBRARY_PATH,
6308
+ ]
6309
+ .filter(Boolean)
6310
+ .join(':'),
6311
+ }
6312
+ : {}),
6313
+ },
6314
+ maxBuffer: 64 * 1024 * 1024,
6315
+ timeout: options.timeoutMs,
6316
+ signal: options.signal,
6317
+ }
6318
+ report('compiling and linking Swift modules')
6317
6319
  try {
6318
6320
  await runCompiler(
6319
6321
  swift,
@@ -6322,29 +6324,54 @@ export function compileSwiftProjectUnit(
6322
6324
  SWIFTPM_PROGRESS,
6323
6325
  SWIFTPM_NOISE,
6324
6326
  )
6325
- } catch (retryError) {
6326
- throw new Error(
6327
- projectDiagnostic(outputOf(retryError), layout, input.files),
6328
- {
6329
- cause: retryError,
6330
- },
6327
+ } catch (error) {
6328
+ const output = outputOf(error)
6329
+ const moduleCache = path.join(
6330
+ root,
6331
+ '.build',
6332
+ WASM_TRIPLE,
6333
+ 'release',
6334
+ 'ModuleCache',
6331
6335
  )
6336
+ if (
6337
+ output.includes('was compiled with module cache path') &&
6338
+ existsSync(moduleCache)
6339
+ ) {
6340
+ rmSync(moduleCache, { recursive: true, force: true })
6341
+ try {
6342
+ await runCompiler(
6343
+ swift,
6344
+ buildArgs,
6345
+ buildOptions,
6346
+ SWIFTPM_PROGRESS,
6347
+ SWIFTPM_NOISE,
6348
+ )
6349
+ } catch (retryError) {
6350
+ throw new Error(
6351
+ projectDiagnostic(outputOf(retryError), layout, input.files),
6352
+ {
6353
+ cause: retryError,
6354
+ },
6355
+ )
6356
+ }
6357
+ } else {
6358
+ throw new Error(projectDiagnostic(output, layout, input.files), {
6359
+ cause: error,
6360
+ })
6361
+ }
6332
6362
  }
6333
- } else {
6334
- throw new Error(projectDiagnostic(output, layout, input.files), {
6335
- cause: error,
6336
- })
6337
- }
6338
- }
6339
- const artifact = {
6340
- hash,
6341
- bytes: readFileSync(builtArtifact(layout)),
6342
- icu: await icu,
6343
- }
6344
- saveCompiledArtifact(root, artifact)
6345
- saveCompiledArtifact(sharedRoot, artifact)
6346
- report('compiled artifact verified')
6347
- return retain(root, artifact)
6363
+ const artifact = {
6364
+ hash,
6365
+ bytes: readFileSync(builtArtifact(layout)),
6366
+ icu: await icu,
6367
+ }
6368
+ saveCompiledArtifact(root, artifact)
6369
+ saveCompiledArtifact(sharedRoot, artifact)
6370
+ report('compiled artifact verified')
6371
+ return retain(root, artifact)
6372
+ },
6373
+ options.signal,
6374
+ )
6348
6375
  },
6349
6376
  options.signal,
6350
6377
  )
@@ -1,10 +1,10 @@
1
1
  {
2
- "id": "77123740e6b79652",
2
+ "id": "e8dd3a9ff9c4dc74",
3
3
  "release": "swift-6.4.0-RELEASE-custom-0d775d32d7c74eae",
4
4
  "wasmSdkId": "swift-6.4.0-RELEASE_wasm-peach-21f60db0",
5
5
  "wasmSdkUrl": "https://r2.sootsim.com/swift-wasm-sdk/swift-6.4.0-RELEASE_wasm-peach-21f60db0.artifactbundle.tar.gz",
6
6
  "wasmSdkChecksum": "7b7573fd23120549fc928581a42968676f5f00f9ef63e5618032a6928a724e16",
7
- "source": "7625fb75d15f047f1e8c0b14b73407c50263f0c4",
7
+ "source": "693296741f3865605388c21ee7e220cb0b6ccaa3",
8
8
  "targets": [
9
9
  "ImagePlayground",
10
10
  "QuickLookThumbnailing",
@@ -46,6 +46,7 @@
46
46
  "Darwin",
47
47
  "MachO",
48
48
  "ObjectiveC",
49
+ "PeachObjCRuntime",
49
50
  "PeachObjC",
50
51
  "UIUtilities",
51
52
  "UIKitCore",
@@ -191,16 +192,16 @@
191
192
  },
192
193
  "hosts": {
193
194
  "darwin-arm64": {
194
- "sha256": "cc4a8f39ea9e3f12864196793a248bea28c05917ef4e7220ad516447da88d582",
195
- "size": 71374056
195
+ "sha256": "0e06928cd63077f30226a27b2a3e92e89bdcaaf2fcd57fcac6a38a8465ee6865",
196
+ "size": 71438614
196
197
  },
197
198
  "linux-x64": {
198
- "sha256": "c3938547ce5a453c63dc261e98753be6420ee8f2501b64d3622ef16f63d8bd1f",
199
- "size": 71380196
199
+ "sha256": "baae9a89308dfde3cc47366ab4163369ac8537274361d10381004f006a965afa",
200
+ "size": 71444190
200
201
  },
201
202
  "linux-arm64": {
202
- "sha256": "0b32aaf29f8ad7a1ca021a42f889bc4da28d239680c4cff82ca96a803c3d31e1",
203
- "size": 71378226
203
+ "sha256": "422e2fbd6f65a8aebe0456e9becc2a6a28505825457c9758e8275f35e2fe63f1",
204
+ "size": 71442522
204
205
  }
205
206
  }
206
207
  }
package/src/swift-host.ts CHANGED
@@ -1401,10 +1401,11 @@ async function compile(
1401
1401
  throw new SwiftMissingModulesError(await missingModulesMessage(packagePath, missing))
1402
1402
  const artifact = await compileSwiftProjectUnit(input, {
1403
1403
  // the compile rewrites Sources/App in its root before swiftpm builds, and
1404
- // its lock orders one process only: two vites on one root (a lane's dev
1405
- // server and a capture, or two checkouts) build each other's sources. so
1406
- // each app and module checkout gets its own root, which keeps the same
1407
- // app's incremental build across restarts.
1404
+ // a root lock orders every process using it, so two vites on one root
1405
+ // (a lane's dev server and a capture, or two checkouts) queue instead of
1406
+ // building each other's sources. each app and module checkout still gets
1407
+ // its own root, so they do not queue and the same app keeps its
1408
+ // incremental build across restarts.
1408
1409
  buildRoot: (() => {
1409
1410
  if (process.env.CONTRAST_SWIFT_BUILD_ROOT)
1410
1411
  return process.env.CONTRAST_SWIFT_BUILD_ROOT
@@ -1656,6 +1657,7 @@ async function compileRemote(
1656
1657
  },
1657
1658
  origin,
1658
1659
  signal,
1660
+ (stage) => console.error(`Peach: compiling ${path.basename(packagePath)}: ${stage}`),
1659
1661
  )
1660
1662
  // the app's JS calls with the hash its own `one typegen` wrote; glue the
1661
1663
  // service rendered from another One would reject every call as stale.