peachsim 0.1.1635 → 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.
Files changed (129) hide show
  1. package/README.md +5 -1
  2. package/cli/app-config.ts +14 -5
  3. package/cli/app-stubs.ts +62 -13
  4. package/cli/bridge-flow-runner.ts +2 -2
  5. package/cli/cloud-client.ts +60 -40
  6. package/cli/commands/doctor.ts +13 -5
  7. package/cli/commands/flow.ts +1 -1
  8. package/cli/commands/inspect/shared.ts +9 -4
  9. package/cli/commands/inspect.ts +65 -47
  10. package/cli/commands/preview.ts +1 -1
  11. package/cli/commands/record.ts +97 -47
  12. package/cli/commands/swift.ts +9 -3
  13. package/cli/commands/upload.ts +48 -42
  14. package/cli/drivers/playwright-sim-host.ts +65 -42
  15. package/cli/main.ts +1 -1
  16. package/cli/outbound-endpoints.ts +7 -5
  17. package/cli/pause-aware-timeout.ts +4 -0
  18. package/cli/ws-bridge.ts +19 -10
  19. package/detox/attempt-events.cjs +62 -5
  20. package/detox/attempt-events.d.cts +7 -0
  21. package/detox/expectations.ts +7 -3
  22. package/detox/index.ts +121 -47
  23. package/detox/motion-change.cjs +170 -21
  24. package/detox/motion-change.d.cts +32 -0
  25. package/detox/navigation-lifecycle.ts +35 -3
  26. package/dist-lib/agent-daemon-client.cjs +1 -1
  27. package/dist-lib/agent-events.cjs +1 -1
  28. package/dist-lib/attached-projects.cjs +1 -1
  29. package/dist-lib/auth/shared-session.cjs +1 -1
  30. package/dist-lib/backend-origin.cjs +8 -7
  31. package/dist-lib/beta.cjs +1 -1
  32. package/dist-lib/beta.mjs +1 -1
  33. package/dist-lib/bridge-constants.cjs +1 -1
  34. package/dist-lib/bridge-contract-input.cjs +1 -1
  35. package/dist-lib/bridge-contract-input.mjs +1 -1
  36. package/dist-lib/bridge-contract.cjs +1 -1
  37. package/dist-lib/bridge-contract.mjs +1 -1
  38. package/dist-lib/bundle-channel.cjs +4038 -1983
  39. package/dist-lib/capture-contract.cjs +1 -1
  40. package/dist-lib/capture-contract.mjs +1 -1
  41. package/dist-lib/cloud-contract.cjs +1 -1
  42. package/dist-lib/cloud-contract.mjs +1 -1
  43. package/dist-lib/cloud-test-args.cjs +1 -1
  44. package/dist-lib/cloud-test-args.mjs +1 -1
  45. package/dist-lib/cloud.cjs +1 -1
  46. package/dist-lib/cloud.mjs +1 -1
  47. package/dist-lib/config.cjs +1 -1
  48. package/dist-lib/config.mjs +1 -1
  49. package/dist-lib/detox/index.cjs +509 -86
  50. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  51. package/dist-lib/expo-swift-bundle.cjs +3551 -1574
  52. package/dist-lib/expo-swift-metadata.cjs +9 -7
  53. package/dist-lib/expo-swift-metadata.mjs +9 -7
  54. package/dist-lib/home-paths.cjs +1 -1
  55. package/dist-lib/host/bridge-host.cjs +43633 -2635
  56. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  57. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  58. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  59. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  60. package/dist-lib/host/websocket-proxy.cjs +1 -1
  61. package/dist-lib/index.cjs +2748 -128
  62. package/dist-lib/jump-to-source-babel.cjs +1 -1
  63. package/dist-lib/jump-to-source-native.cjs +1 -1
  64. package/dist-lib/kotlin-host.cjs +1 -1
  65. package/dist-lib/menu.cjs +1 -1
  66. package/dist-lib/menu.mjs +1 -1
  67. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  68. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  69. package/dist-lib/metro-production-bundle.cjs +1 -1
  70. package/dist-lib/metro-production-bundle.mjs +1 -1
  71. package/dist-lib/metro.cjs +2547 -83
  72. package/dist-lib/native-app-identity-project.cjs +237 -50
  73. package/dist-lib/native-app-identity-project.mjs +237 -50
  74. package/dist-lib/native-app-identity.cjs +29 -30
  75. package/dist-lib/native-app-identity.mjs +29 -30
  76. package/dist-lib/native-firebase-metadata.cjs +1 -1
  77. package/dist-lib/native-firebase-metadata.mjs +1 -1
  78. package/dist-lib/plist.cjs +1 -1
  79. package/dist-lib/plist.mjs +1 -1
  80. package/dist-lib/profiles.cjs +1 -1
  81. package/dist-lib/public-brand.cjs +1 -1
  82. package/dist-lib/react-native-host-modules.cjs +2 -2
  83. package/dist-lib/react-native-host-modules.mjs +2 -2
  84. package/dist-lib/scripts/dev-server-scanner.cjs +8 -6
  85. package/dist-lib/sdk.cjs +1 -1
  86. package/dist-lib/sdk.mjs +1 -1
  87. package/dist-lib/skills.cjs +65403 -22739
  88. package/dist-lib/swift-compile.cjs +93 -32
  89. package/dist-lib/swift-compile.mjs +93 -32
  90. package/dist-lib/swift-package.cjs +1 -1
  91. package/dist-lib/swift-package.mjs +1 -1
  92. package/dist-lib/swift-submit.cjs +7 -5
  93. package/dist-lib/swift-submit.mjs +7 -5
  94. package/dist-lib/swift-toolchain.cjs +316 -41
  95. package/dist-lib/swift.cjs +1 -1
  96. package/dist-lib/vite-swift.cjs +7335 -5432
  97. package/dist-lib/vite.cjs +2857 -12
  98. package/dist-lib/worklet-executable.cjs +14 -2
  99. package/dist-lib/worklet-executable.mjs +14 -2
  100. package/package.json +1 -1
  101. package/src/backend-origin.ts +13 -6
  102. package/src/expo-swift-host.ts +5 -2
  103. package/src/expoKotlinContract.ts +4 -7
  104. package/src/expoSwiftContract.ts +4 -6
  105. package/src/host/staged-host-files.ts +9 -0
  106. package/src/native-app-identity-project.ts +1 -4
  107. package/src/native-app-identity.ts +1 -4
  108. package/src/nativeAppIconContract.ts +1 -4
  109. package/src/output-lock-stdio.ts +36 -0
  110. package/src/react-native-bridge-modules.ts +21 -6
  111. package/src/react-native-host-modules.ts +4 -1
  112. package/src/settings-bundle.ts +1 -4
  113. package/src/swift-compile.ts +91 -33
  114. package/src/swift-distribution.lock.json +22 -12
  115. package/src/swift-distribution.ts +103 -4
  116. package/src/swift-host.ts +89 -26
  117. package/src/swift-icu.ts +16 -10
  118. package/src/swift-objc-compile.ts +355 -0
  119. package/src/swift-objc-sdk.ts +689 -18
  120. package/src/swift-objc-standin.ts +126 -14
  121. package/src/swift-package-manifest.ts +39 -1
  122. package/src/swift-packages.ts +35 -21
  123. package/src/swift-standin-emit.ts +40 -7
  124. package/src/swift-standins.ts +404 -138
  125. package/src/swift-submit.ts +3 -5
  126. package/src/swift-toolchain.ts +378 -46
  127. package/src/worker-system-chunks.ts +11 -12
  128. package/src/worklet-executable.ts +19 -1
  129. package/src/xcode-project.ts +270 -30
@@ -1,11 +1,14 @@
1
1
  // reads an xcode project's application target the way xcode builds it: the
2
2
  // swift sources and resources that are members of the target, the local
3
3
  // swift packages its products come from, the remote package products it links,
4
- // and the build settings that change how its swift compiles. never runs xcode
5
- // or evaluates a build phase; the project file is data: project.pbxproj, or
4
+ // and the build settings that change how its swift compiles. project opening
5
+ // prepares declared generated swift sources without running xcode. the project
6
+ // file is data: project.pbxproj, or
6
7
  // xcode 27.2's json project.xcproj, which xcode-json-project.ts reads into
7
8
  // the same object table.
8
9
 
10
+ import { execFileSync } from 'node:child_process'
11
+ import { createHash } from 'node:crypto'
9
12
  import fs from 'node:fs'
10
13
  import path from 'node:path'
11
14
  import plist from 'plist'
@@ -45,6 +48,19 @@ export interface XcodeModuleTarget {
45
48
  mainActorIsolation: boolean
46
49
  /** the compilation conditions the build configuration defines, as `-D` passes them. */
47
50
  defines: string[]
51
+ /** the resolved source iOS minimum, including for native package dependencies. */
52
+ iosDeploymentTarget: string | null
53
+ /** original implementation inputs and clang settings, before any declaration import. */
54
+ objc?: {
55
+ headers: string[]
56
+ prefixHeader: string | null
57
+ bridgingHeader: string | null
58
+ headerSearchPaths: string[]
59
+ frameworkSearchPaths: string[]
60
+ flags: string[]
61
+ cxxFlags: string[]
62
+ fileFlags: Record<string, string[]>
63
+ }
48
64
  }
49
65
 
50
66
  /** an application target, resolved to files on disk with everything it builds. */
@@ -72,8 +88,8 @@ export interface XcodeAppTarget extends XcodeModuleTarget {
72
88
  localPackageDirs: string[]
73
89
  /** prebuilt frameworks and xcframeworks the projects vendor and link, at their path when it resolves. */
74
90
  binaryFrameworks: { name: string; path: string | null }[]
75
- /** the Package.resolved pinning the remote packages' revisions, when resolved. */
76
- packageResolved: string | null
91
+ /** workspace pins, or local package locks when the workspace has no lock. */
92
+ packageResolved: string[]
77
93
  /** every application target in the workspace, for a chooser. */
78
94
  appTargets: string[]
79
95
  /** the build configuration every target's settings are read in. */
@@ -498,14 +514,17 @@ function expand(value: string, lookup: (name: string) => string): string {
498
514
  )
499
515
  }
500
516
 
517
+ const preparedSourcePhases = new Map<string, string>()
518
+
501
519
  // the swift sources and resources that are members of a target.
502
- // run script phases never execute here; their declared swift outputs are
503
- // compile inputs xcode generates before compiling, so a missing one names
504
- // the script instead of silently dropping the file.
520
+ // metadata-only reads never execute scripts. opening a project prepares the
521
+ // declared swift outputs with the project's own shell and build settings.
505
522
  function targetMembers(
506
523
  ref: TargetRef,
507
524
  settings: (key: string) => string,
508
- options: { requireSources?: boolean } = {},
525
+ options: { requireSources?: boolean; prepareGeneratedSources?: boolean },
526
+ settingKeys: Set<string>,
527
+ projectInputs: Set<string>,
509
528
  ): {
510
529
  sources: string[]
511
530
  resources: string[]
@@ -517,30 +536,137 @@ function targetMembers(
517
536
  const allSources = new Set<string>()
518
537
  const resources = new Set<string>()
519
538
  const scriptForOutput = new Map<string, string>()
520
- const noteScriptOutput = (raw: string, script: string) => {
521
- const expanded = expand(raw, settings)
522
- if (!expanded.endsWith('.swift')) return
523
- const absolute = path.isAbsolute(expanded)
524
- ? path.normalize(expanded)
525
- : path.resolve(project.projectDir, expanded)
526
- if (!scriptForOutput.has(absolute)) scriptForOutput.set(absolute, script)
527
- }
528
539
  for (const phaseId of list(target.buildPhases)) {
529
540
  const phase = project.object(phaseId)
530
541
  if (phase?.isa !== 'PBXShellScriptBuildPhase') continue
531
542
  const script = str(phase.name) ?? phaseId
532
- for (const raw of list(phase.outputPaths)) noteScriptOutput(raw, script)
533
- for (const listPath of list(phase.outputFileListPaths)) {
534
- const expandedList = expand(listPath, settings)
535
- const absoluteList = path.isAbsolute(expandedList)
536
- ? expandedList
537
- : path.resolve(project.projectDir, expandedList)
538
- if (!fs.existsSync(absoluteList)) continue
539
- for (const line of fs.readFileSync(absoluteList, 'utf8').split('\n')) {
540
- const trimmed = line.trim()
541
- if (trimmed === '' || trimmed.startsWith('#')) continue
542
- noteScriptOutput(trimmed, script)
543
+ const absolute = (raw: string) =>
544
+ path.resolve(project.projectDir, expand(raw, settings))
545
+ const inputs = list(phase.inputPaths).map(absolute)
546
+ const outputs = list(phase.outputPaths).map(absolute)
547
+ const fileLists = new Map<string, { file: string; entries: string[] }[]>()
548
+ for (const kind of ['INPUT', 'OUTPUT']) {
549
+ const lists = list(
550
+ kind === 'INPUT' ? phase.inputFileListPaths : phase.outputFileListPaths,
551
+ ).map((raw) => {
552
+ const file = absolute(raw)
553
+ if (!fs.existsSync(file)) {
554
+ return { file, entries: [] }
555
+ }
556
+ const entries = fs
557
+ .readFileSync(file, 'utf8')
558
+ .split(/\r?\n/)
559
+ .map((line) => line.trim())
560
+ .filter((line) => line !== '' && !line.startsWith('#'))
561
+ .map(absolute)
562
+ return { file, entries }
563
+ })
564
+ fileLists.set(kind, lists)
565
+ }
566
+ const allOutputs = [
567
+ ...outputs,
568
+ ...fileLists.get('OUTPUT')!.flatMap((item) => item.entries),
569
+ ]
570
+ for (const output of allOutputs)
571
+ if (output.endsWith('.swift') && !scriptForOutput.has(output))
572
+ scriptForOutput.set(output, script)
573
+ const inputFiles = [
574
+ ...inputs,
575
+ ...fileLists.get('INPUT')!.flatMap((item) => [item.file, ...item.entries]),
576
+ ...fileLists.get('OUTPUT')!.map((item) => item.file),
577
+ ]
578
+ if (allOutputs.some((file) => file.endsWith('.swift')))
579
+ for (const file of inputFiles) if (fs.existsSync(file)) projectInputs.add(file)
580
+ if (
581
+ options.prepareGeneratedSources &&
582
+ options.requireSources !== false &&
583
+ allOutputs.some((file) => file.endsWith('.swift'))
584
+ ) {
585
+ const phaseDir = path.join(settings('TARGET_TEMP_DIR'), `Script-${phaseId}`)
586
+ for (const lists of fileLists.values())
587
+ for (const { file } of lists)
588
+ if (!fs.existsSync(file))
589
+ throw new Error(
590
+ `${targetName}: Run Script "${script}" missing file list ${file}`,
591
+ )
592
+ const env = {
593
+ ...process.env,
594
+ ...Object.fromEntries(
595
+ [...settingKeys]
596
+ .filter((key) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
597
+ .map((key) => [key, settings(key)]),
598
+ ),
599
+ }
600
+ const declared = { INPUT: inputs, OUTPUT: outputs }
601
+ for (const kind of ['INPUT', 'OUTPUT'] satisfies (keyof typeof declared)[]) {
602
+ env[`SCRIPT_${kind}_FILE_COUNT`] = String(declared[kind].length)
603
+ declared[kind].forEach((file, index) => {
604
+ env[`SCRIPT_${kind}_FILE_${index}`] = file
605
+ })
606
+ const lists = fileLists.get(kind)!
607
+ env[`SCRIPT_${kind}_FILE_LIST_COUNT`] = String(lists.length)
608
+ lists.forEach((_item, index) => {
609
+ env[`SCRIPT_${kind}_FILE_LIST_${index}`] = path.join(
610
+ phaseDir,
611
+ `${kind}-${index}.xcfilelist`,
612
+ )
613
+ })
614
+ }
615
+ const signature = createHash('sha256').update(
616
+ JSON.stringify([phase, [...settingKeys].map((key) => [key, settings(key)])]),
617
+ )
618
+ for (const file of inputFiles) {
619
+ signature.update(file)
620
+ if (fs.existsSync(file)) {
621
+ const stat = fs.statSync(file)
622
+ signature.update(`${stat.mtimeMs}:${stat.ctimeMs}:${stat.size}`)
623
+ } else signature.update('missing')
624
+ }
625
+ const stamp = signature.digest('hex')
626
+ const key = `${project.projectFile}:${targetId}:${phaseId}:${settings('CONFIGURATION')}`
627
+ if (
628
+ preparedSourcePhases.get(key) === stamp &&
629
+ allOutputs.every((file) => fs.existsSync(file))
630
+ )
631
+ continue
632
+ fs.mkdirSync(phaseDir, { recursive: true })
633
+ fs.mkdirSync(settings('BUILT_PRODUCTS_DIR'), { recursive: true })
634
+ fs.mkdirSync(settings('DERIVED_FILE_DIR'), { recursive: true })
635
+ for (const [kind, lists] of fileLists)
636
+ lists.forEach((item, index) => {
637
+ fs.writeFileSync(
638
+ env[`SCRIPT_${kind}_FILE_LIST_${index}`]!,
639
+ `${item.entries.join('\n')}\n`,
640
+ )
641
+ })
642
+ try {
643
+ execFileSync(
644
+ str(phase.shellPath) ?? '/bin/sh',
645
+ ['-c', str(phase.shellScript) ?? ''],
646
+ {
647
+ cwd: project.projectDir,
648
+ env,
649
+ stdio: ['ignore', 'pipe', 'pipe'],
650
+ timeout: 120_000,
651
+ },
652
+ )
653
+ } catch (error) {
654
+ const output =
655
+ error instanceof Error
656
+ ? `${error.message}\n${'stdout' in error ? String(error.stdout) : ''}${'stderr' in error ? String(error.stderr) : ''}`
657
+ : String(error)
658
+ throw new Error(
659
+ `${targetName}: Run Script "${script}" failed preparing generated Swift sources:\n${output}`,
660
+ )
543
661
  }
662
+ const missing = allOutputs.filter(
663
+ (file) => file.endsWith('.swift') && !fs.existsSync(file),
664
+ )
665
+ if (missing.length > 0)
666
+ throw new Error(
667
+ `${targetName}: Run Script "${script}" did not produce ${missing.join(', ')}`,
668
+ )
669
+ preparedSourcePhases.set(key, stamp)
544
670
  }
545
671
  }
546
672
  visitXcodeTargetMembers(
@@ -769,7 +895,7 @@ export function readXcodeAppTarget(
769
895
  input: string,
770
896
  targetName?: string,
771
897
  configurationName?: string,
772
- options: { requireSources?: boolean } = {},
898
+ options: { requireSources?: boolean; prepareGeneratedSources?: boolean } = {},
773
899
  ): XcodeAppTarget {
774
900
  // canonical, so every path returned agrees with the host's realpath checks
775
901
  const xcodeprojOrWorkspace = fs.realpathSync(input)
@@ -881,22 +1007,112 @@ export function readXcodeAppTarget(
881
1007
  SRCROOT: ref.project.projectDir,
882
1008
  SOURCE_ROOT: ref.project.projectDir,
883
1009
  PROJECT_DIR: ref.project.projectDir,
1010
+ SYMROOT: path.join(ref.project.projectDir, '.build/peach-xcode/Build/Products'),
1011
+ BUILD_DIR: '$(SYMROOT)',
1012
+ OBJROOT: path.join(
1013
+ ref.project.projectDir,
1014
+ '.build/peach-xcode/Build/Intermediates.noindex',
1015
+ ),
1016
+ CONFIGURATION_BUILD_DIR:
1017
+ '$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)',
1018
+ BUILT_PRODUCTS_DIR: '$(CONFIGURATION_BUILD_DIR)',
1019
+ TARGET_BUILD_DIR: '$(CONFIGURATION_BUILD_DIR)',
1020
+ PROJECT_TEMP_DIR: '$(OBJROOT)/$(PROJECT_NAME).build',
1021
+ CONFIGURATION_TEMP_DIR:
1022
+ '$(PROJECT_TEMP_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)',
1023
+ TARGET_TEMP_DIR: '$(CONFIGURATION_TEMP_DIR)/$(TARGET_NAME).build',
1024
+ DERIVED_FILE_DIR: '$(TARGET_TEMP_DIR)/DerivedSources',
1025
+ DERIVED_FILES_DIR: '$(DERIVED_FILE_DIR)',
1026
+ PLATFORM_NAME: 'iphonesimulator',
1027
+ EFFECTIVE_PLATFORM_NAME: '-iphonesimulator',
1028
+ CURRENT_ARCH: 'arm64',
1029
+ ARCHS: 'arm64',
1030
+ ACTION: 'build',
884
1031
  DEVELOPMENT_LANGUAGE: str(ref.project.project.developmentRegion) ?? 'en',
885
1032
  },
886
1033
  options,
887
1034
  )
888
1035
  for (const file of read) xcconfigs.add(file)
889
1036
  const swiftVersion = Number.parseFloat(settings('SWIFT_VERSION') || '5')
1037
+ const members = targetMembers(ref, settings, options, keys, xcconfigs)
1038
+ const implementations = members.allSources.filter((file) =>
1039
+ /\.(c|cc|cpp|cxx|m|mm)$/.test(file),
1040
+ )
1041
+ const absolute = (file: string) => path.resolve(ref.project.projectDir, file)
1042
+ const fileFlags: Record<string, string[]> = {}
1043
+ for (const phaseId of list(ref.target.buildPhases)) {
1044
+ const phase = ref.project.object(phaseId)
1045
+ if (phase?.isa !== 'PBXSourcesBuildPhase') continue
1046
+ for (const id of list(phase.files)) {
1047
+ const file = ref.project.object(id)
1048
+ const resolved = ref.project.resolvePath(str(file?.fileRef) ?? '')
1049
+ if (resolved && implementations.includes(resolved))
1050
+ fileFlags[resolved] = settingList(
1051
+ expand(
1052
+ str(isDict(file?.settings) ? file.settings.COMPILER_FLAGS : undefined) ??
1053
+ '',
1054
+ settings,
1055
+ ),
1056
+ )
1057
+ }
1058
+ }
890
1059
  return {
891
1060
  settings,
892
1061
  keys,
893
1062
  module: {
894
1063
  name,
895
1064
  moduleName: applyOperator(settings('PRODUCT_MODULE_NAME'), 'c99extidentifier'),
896
- ...targetMembers(ref, settings, options),
1065
+ ...members,
897
1066
  languageMode: swiftVersion >= 6 ? 6 : 5,
898
1067
  mainActorIsolation: settings('SWIFT_DEFAULT_ACTOR_ISOLATION') === 'MainActor',
899
1068
  defines: compilationConditions(settings),
1069
+ iosDeploymentTarget: settings('IPHONEOS_DEPLOYMENT_TARGET') || null,
1070
+ ...(implementations.some((file) => /\.mm?$/.test(file))
1071
+ ? {
1072
+ objc: {
1073
+ headers: Object.keys(ref.project.objects).flatMap((id) => {
1074
+ const file = ref.project.resolvePath(id)
1075
+ return file && /\.(h|hh|hpp|hxx|pch)$/.test(file) && fs.existsSync(file)
1076
+ ? [file]
1077
+ : []
1078
+ }),
1079
+ prefixHeader: settings('GCC_PREFIX_HEADER')
1080
+ ? absolute(settings('GCC_PREFIX_HEADER'))
1081
+ : null,
1082
+ bridgingHeader: settings('SWIFT_OBJC_BRIDGING_HEADER')
1083
+ ? absolute(settings('SWIFT_OBJC_BRIDGING_HEADER'))
1084
+ : null,
1085
+ headerSearchPaths: [
1086
+ ...settingList(settings('HEADER_SEARCH_PATHS')),
1087
+ ...settingList(settings('USER_HEADER_SEARCH_PATHS')),
1088
+ ].map(absolute),
1089
+ frameworkSearchPaths: settingList(settings('FRAMEWORK_SEARCH_PATHS')).map(
1090
+ absolute,
1091
+ ),
1092
+ flags: [
1093
+ ...(settings('CLANG_ENABLE_OBJC_ARC') === 'YES' ? ['-fobjc-arc'] : []),
1094
+ ...(settings('CLANG_ENABLE_OBJC_WEAK') === 'YES'
1095
+ ? ['-fobjc-weak']
1096
+ : []),
1097
+ ...(settings('CLANG_ENABLE_MODULES') === 'YES' ? ['-fmodules'] : []),
1098
+ ...(settings('GCC_C_LANGUAGE_STANDARD')
1099
+ ? [`-std=${settings('GCC_C_LANGUAGE_STANDARD')}`]
1100
+ : []),
1101
+ ...settingList(settings('GCC_PREPROCESSOR_DEFINITIONS')).map(
1102
+ (value) => `-D${value}`,
1103
+ ),
1104
+ ...settingList(settings('OTHER_CFLAGS')),
1105
+ ],
1106
+ cxxFlags: [
1107
+ ...(settings('CLANG_CXX_LANGUAGE_STANDARD')
1108
+ ? [`-std=${settings('CLANG_CXX_LANGUAGE_STANDARD')}`]
1109
+ : []),
1110
+ ...settingList(settings('OTHER_CPLUSPLUSFLAGS')),
1111
+ ],
1112
+ fileFlags,
1113
+ },
1114
+ }
1115
+ : {}),
900
1116
  } satisfies XcodeModuleTarget,
901
1117
  }
902
1118
  }
@@ -967,6 +1183,23 @@ export function readXcodeAppTarget(
967
1183
  ...projects.flatMap((project) =>
968
1184
  Object.entries(project.objects).flatMap(([id, entry]) => {
969
1185
  if (!isDict(entry)) return []
1186
+ if (entry.isa === 'PBXFileSystemSynchronizedRootGroup') {
1187
+ const folder = project.resolvePath(id)
1188
+ const pending = folder ? [folder] : []
1189
+ const packages: string[] = []
1190
+ while (pending.length > 0) {
1191
+ const dir = pending.pop()!
1192
+ if (fs.existsSync(path.join(dir, 'Package.swift'))) {
1193
+ packages.push(dir)
1194
+ continue
1195
+ }
1196
+ if (!isDirectory(dir)) continue
1197
+ for (const child of fs.readdirSync(dir, { withFileTypes: true }))
1198
+ if (child.isDirectory() && !child.name.startsWith('.'))
1199
+ pending.push(path.join(dir, child.name))
1200
+ }
1201
+ return packages
1202
+ }
970
1203
  const candidate =
971
1204
  entry.isa === 'XCLocalSwiftPackageReference'
972
1205
  ? path.resolve(project.projectDir, str(entry.relativePath) ?? '')
@@ -1046,6 +1279,13 @@ export function readXcodeAppTarget(
1046
1279
  'Package.resolved',
1047
1280
  )
1048
1281
 
1282
+ const packageResolved = fs.existsSync(resolvedFile)
1283
+ ? [fs.realpathSync(resolvedFile)]
1284
+ : [...new Set(localPackageDirs.map((dir) => path.join(dir, 'Package.resolved')))]
1285
+ .filter((file) => fs.existsSync(file))
1286
+ .map((file) => fs.realpathSync(file))
1287
+ .sort()
1288
+
1049
1289
  // the app is last in its own dependency order
1050
1290
  const used = order.map((ref) => ref.project)
1051
1291
  return {
@@ -1074,7 +1314,7 @@ export function readXcodeAppTarget(
1074
1314
  binaryFrameworks: [...binaryFrameworks]
1075
1315
  .map(([name, file]) => ({ name, path: file }))
1076
1316
  .sort((left, right) => left.name.localeCompare(right.name)),
1077
- packageResolved: fs.existsSync(resolvedFile) ? resolvedFile : null,
1317
+ packageResolved,
1078
1318
  appTargets: appTargetNames,
1079
1319
  configuration,
1080
1320
  settings,