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.
- package/README.md +5 -1
- package/cli/app-config.ts +14 -5
- package/cli/app-stubs.ts +62 -13
- package/cli/bridge-flow-runner.ts +2 -2
- package/cli/cloud-client.ts +60 -40
- package/cli/commands/doctor.ts +13 -5
- package/cli/commands/flow.ts +1 -1
- package/cli/commands/inspect/shared.ts +9 -4
- package/cli/commands/inspect.ts +65 -47
- package/cli/commands/preview.ts +1 -1
- package/cli/commands/record.ts +97 -47
- package/cli/commands/swift.ts +9 -3
- package/cli/commands/upload.ts +48 -42
- package/cli/drivers/playwright-sim-host.ts +65 -42
- package/cli/main.ts +1 -1
- package/cli/outbound-endpoints.ts +7 -5
- package/cli/pause-aware-timeout.ts +4 -0
- package/cli/ws-bridge.ts +19 -10
- package/detox/attempt-events.cjs +62 -5
- package/detox/attempt-events.d.cts +7 -0
- package/detox/expectations.ts +7 -3
- package/detox/index.ts +121 -47
- package/detox/motion-change.cjs +170 -21
- package/detox/motion-change.d.cts +32 -0
- package/detox/navigation-lifecycle.ts +35 -3
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +8 -7
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -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 +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/bundle-channel.cjs +4038 -1983
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/config.mjs +1 -1
- package/dist-lib/detox/index.cjs +509 -86
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/expo-swift-bundle.cjs +3551 -1574
- package/dist-lib/expo-swift-metadata.cjs +9 -7
- package/dist-lib/expo-swift-metadata.mjs +9 -7
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +43633 -2635
- 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 +2748 -128
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/kotlin-host.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +2547 -83
- package/dist-lib/native-app-identity-project.cjs +237 -50
- package/dist-lib/native-app-identity-project.mjs +237 -50
- package/dist-lib/native-app-identity.cjs +29 -30
- package/dist-lib/native-app-identity.mjs +29 -30
- package/dist-lib/native-firebase-metadata.cjs +1 -1
- package/dist-lib/native-firebase-metadata.mjs +1 -1
- package/dist-lib/plist.cjs +1 -1
- package/dist-lib/plist.mjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +2 -2
- package/dist-lib/react-native-host-modules.mjs +2 -2
- package/dist-lib/scripts/dev-server-scanner.cjs +8 -6
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +65403 -22739
- package/dist-lib/swift-compile.cjs +93 -32
- package/dist-lib/swift-compile.mjs +93 -32
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +7 -5
- package/dist-lib/swift-submit.mjs +7 -5
- package/dist-lib/swift-toolchain.cjs +316 -41
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +7335 -5432
- package/dist-lib/vite.cjs +2857 -12
- package/dist-lib/worklet-executable.cjs +14 -2
- package/dist-lib/worklet-executable.mjs +14 -2
- package/package.json +1 -1
- package/src/backend-origin.ts +13 -6
- package/src/expo-swift-host.ts +5 -2
- package/src/expoKotlinContract.ts +4 -7
- package/src/expoSwiftContract.ts +4 -6
- package/src/host/staged-host-files.ts +9 -0
- package/src/native-app-identity-project.ts +1 -4
- package/src/native-app-identity.ts +1 -4
- package/src/nativeAppIconContract.ts +1 -4
- package/src/output-lock-stdio.ts +36 -0
- package/src/react-native-bridge-modules.ts +21 -6
- package/src/react-native-host-modules.ts +4 -1
- package/src/settings-bundle.ts +1 -4
- package/src/swift-compile.ts +91 -33
- package/src/swift-distribution.lock.json +22 -12
- package/src/swift-distribution.ts +103 -4
- package/src/swift-host.ts +89 -26
- package/src/swift-icu.ts +16 -10
- package/src/swift-objc-compile.ts +355 -0
- package/src/swift-objc-sdk.ts +689 -18
- package/src/swift-objc-standin.ts +126 -14
- package/src/swift-package-manifest.ts +39 -1
- package/src/swift-packages.ts +35 -21
- package/src/swift-standin-emit.ts +40 -7
- package/src/swift-standins.ts +404 -138
- package/src/swift-submit.ts +3 -5
- package/src/swift-toolchain.ts +378 -46
- package/src/worker-system-chunks.ts +11 -12
- package/src/worklet-executable.ts +19 -1
- package/src/xcode-project.ts +270 -30
package/src/swift-host.ts
CHANGED
|
@@ -15,6 +15,8 @@ import { authHeaderValue, resolveCliAuth } from '../cli/auth'
|
|
|
15
15
|
import { resolveContrastBackendOrigin } from './backend-origin'
|
|
16
16
|
import { cacheDir } from './home-paths'
|
|
17
17
|
import iosSdk from './ios-sdk-frameworks.json' with { type: 'json' }
|
|
18
|
+
import { type NativeAppIdentity } from './native-app-identity'
|
|
19
|
+
import { readNativeAppIdentityFromXcodeTarget } from './native-app-identity-project'
|
|
18
20
|
import { swiftBinaryModules } from './swift-binary-modules'
|
|
19
21
|
import {
|
|
20
22
|
compileSwiftProjectUnit,
|
|
@@ -28,6 +30,7 @@ import {
|
|
|
28
30
|
} from './swift-compile'
|
|
29
31
|
import { designTimeValues } from './swift-design-time'
|
|
30
32
|
import { swiftDistributionRoot, swiftModuleToolchain } from './swift-distribution'
|
|
33
|
+
import { compileXcodeObjCImplementations } from './swift-objc-compile'
|
|
31
34
|
import {
|
|
32
35
|
swiftImplicitResourcePaths,
|
|
33
36
|
swiftMetalSourceFiles,
|
|
@@ -267,7 +270,9 @@ export function openXcodeProject(
|
|
|
267
270
|
moduleRoot: string | undefined,
|
|
268
271
|
configuration?: string,
|
|
269
272
|
): string {
|
|
270
|
-
const app = readXcodeAppTarget(xcodeproj, target, configuration
|
|
273
|
+
const app = readXcodeAppTarget(xcodeproj, target, configuration, {
|
|
274
|
+
prepareGeneratedSources: true,
|
|
275
|
+
})
|
|
271
276
|
const root = app.rootDir
|
|
272
277
|
xcodeProjects.set(root, {
|
|
273
278
|
xcodeproj,
|
|
@@ -312,20 +317,20 @@ function swiftPackages(
|
|
|
312
317
|
projectGaps: Map<string, string>
|
|
313
318
|
binaries: SwiftPackageBinary[]
|
|
314
319
|
} {
|
|
315
|
-
const stamp = (file: string
|
|
316
|
-
|
|
320
|
+
const stamp = (file: string) =>
|
|
321
|
+
fs.existsSync(file)
|
|
322
|
+
? createHash('sha256').update(fs.readFileSync(file)).digest('hex')
|
|
323
|
+
: null
|
|
317
324
|
const key = JSON.stringify([
|
|
318
325
|
app.linkedProducts,
|
|
319
|
-
app.packageResolved,
|
|
320
|
-
stamp(app.packageResolved),
|
|
326
|
+
app.packageResolved.map((file) => [file, stamp(file)]),
|
|
321
327
|
app.localPackageDirs.map((dir) => stamp(path.join(dir, 'Package.swift'))),
|
|
322
328
|
app.linkedProducts.map(({ source }) =>
|
|
323
329
|
source.kind === 'local' ? stamp(path.join(source.dir, 'Package.swift')) : 0,
|
|
324
330
|
),
|
|
325
331
|
])
|
|
326
|
-
//
|
|
327
|
-
//
|
|
328
|
-
// emitter), so a changed input rebuilds instead of reusing stale output.
|
|
332
|
+
// resolution follows lock and manifest contents even when a checkout restores
|
|
333
|
+
// timestamps. the stand-in key also covers artifacts, peach modules and emitter.
|
|
329
334
|
let cached = entry.packages
|
|
330
335
|
if (cached?.key !== key) {
|
|
331
336
|
const packages = resolveSwiftPackages({
|
|
@@ -345,6 +350,13 @@ function swiftPackages(
|
|
|
345
350
|
}
|
|
346
351
|
entry.packages = cached
|
|
347
352
|
}
|
|
353
|
+
if (
|
|
354
|
+
entry.moduleRoot &&
|
|
355
|
+
[app, ...app.frameworks].some((target) =>
|
|
356
|
+
target.allSources.some((file) => /\.mm?$/.test(file)),
|
|
357
|
+
)
|
|
358
|
+
)
|
|
359
|
+
compileXcodeObjCImplementations(app, cached.binaries, entry.moduleRoot)
|
|
348
360
|
// a reached binary target with stand-ins for every module it ships
|
|
349
361
|
// resolves as provided, so what depends on it compiles; the rest keep
|
|
350
362
|
// their reason with why no stand-in was built. surfaces peach already
|
|
@@ -450,11 +462,26 @@ function xcodeLayout(root: string): XcodeLayout | null {
|
|
|
450
462
|
registered.xcodeproj,
|
|
451
463
|
registered.target,
|
|
452
464
|
registered.configuration,
|
|
465
|
+
{ prepareGeneratedSources: true },
|
|
453
466
|
)
|
|
467
|
+
const implementations = [app, ...app.frameworks].flatMap((target) =>
|
|
468
|
+
target.allSources
|
|
469
|
+
.filter((file) => /\.mm?$/i.test(file))
|
|
470
|
+
.map((file) => `${target.name}: ${path.relative(app.rootDir, file)}`),
|
|
471
|
+
)
|
|
472
|
+
if (implementations.length > 0 && !registered.moduleRoot)
|
|
473
|
+
throw new Error(
|
|
474
|
+
`Peach cannot compile Objective-C implementations in Xcode targets until the runtime and shared Swift object bridge are available:\n${implementations.map((file) => ` ${file}`).join('\n')}`,
|
|
475
|
+
)
|
|
454
476
|
const appSources = app.allSources.filter(
|
|
455
477
|
(file) => file.endsWith('.swift') || file.endsWith('.metal'),
|
|
456
478
|
)
|
|
457
|
-
const files = new Set([
|
|
479
|
+
const files = new Set([
|
|
480
|
+
...app.projectFiles,
|
|
481
|
+
...app.packageResolved,
|
|
482
|
+
...appSources,
|
|
483
|
+
...app.resources,
|
|
484
|
+
])
|
|
458
485
|
if (app.infoPlistFile) files.add(app.infoPlistFile)
|
|
459
486
|
const targetDirs = new Map(
|
|
460
487
|
[...appSources, ...app.resources].map((file) => [file, XCODE_APP_DIR]),
|
|
@@ -605,10 +632,18 @@ function xcodeManifest(layout: XcodeLayout): string {
|
|
|
605
632
|
|
|
606
633
|
// preflight the entire input tree before reading any source or resource bytes.
|
|
607
634
|
// a nested package is a separate compile unit; ignored build trees never enter it.
|
|
608
|
-
function packageFiles(root: string): string[] {
|
|
635
|
+
function packageFiles(root: string, includeBuildInputs = true): string[] {
|
|
609
636
|
const canonicalRoot = fs.realpathSync(root)
|
|
610
637
|
const xcode = xcodeLayout(canonicalRoot)
|
|
611
|
-
|
|
638
|
+
if (!xcode) return sourceTreeFiles(canonicalRoot, canonicalRoot)
|
|
639
|
+
if (includeBuildInputs) return xcode.files
|
|
640
|
+
const buildInputs = new Set(xcode.app.projectFiles)
|
|
641
|
+
return xcode.files.filter(
|
|
642
|
+
(file) =>
|
|
643
|
+
!buildInputs.has(file) ||
|
|
644
|
+
xcode.targetDirs.has(file) ||
|
|
645
|
+
xcode.packages.targets.some((target) => target.files.includes(file)),
|
|
646
|
+
)
|
|
612
647
|
}
|
|
613
648
|
|
|
614
649
|
// remote dependencies are enumerated from peach's checkout cache, while app
|
|
@@ -668,16 +703,18 @@ function isPackageSource(file: string): boolean {
|
|
|
668
703
|
}
|
|
669
704
|
|
|
670
705
|
export function sourceFiles(root: string): string[] {
|
|
671
|
-
return packageFiles(root).filter(isPackageSource)
|
|
706
|
+
return packageFiles(root, false).filter(isPackageSource)
|
|
672
707
|
}
|
|
673
708
|
|
|
674
709
|
// project files and the xcconfigs their settings read are build inputs, never resources
|
|
675
710
|
function resourceCandidateFiles(root: string): string[] {
|
|
676
|
-
return packageFiles(root).filter(
|
|
711
|
+
return packageFiles(root, false).filter(
|
|
677
712
|
(file) =>
|
|
678
713
|
!isPackageSource(file) &&
|
|
679
714
|
!file.endsWith('.xcconfig') &&
|
|
680
|
-
!['project.pbxproj', 'project.xcproj'].includes(
|
|
715
|
+
!['project.pbxproj', 'project.xcproj', 'Package.resolved'].includes(
|
|
716
|
+
path.basename(file),
|
|
717
|
+
),
|
|
681
718
|
)
|
|
682
719
|
}
|
|
683
720
|
|
|
@@ -685,6 +722,7 @@ function resourceCandidateFiles(root: string): string[] {
|
|
|
685
722
|
export function swiftProjectAppInfo(packagePath: string): {
|
|
686
723
|
name: string
|
|
687
724
|
icon: { bytes: Buffer; contentType: string } | null
|
|
725
|
+
identity: NativeAppIdentity | null
|
|
688
726
|
} {
|
|
689
727
|
const registered = xcodeProjects.get(packagePath)
|
|
690
728
|
const app = registered
|
|
@@ -747,12 +785,17 @@ export function swiftProjectAppInfo(packagePath: string): {
|
|
|
747
785
|
}
|
|
748
786
|
break
|
|
749
787
|
}
|
|
750
|
-
return {
|
|
788
|
+
return {
|
|
789
|
+
name: app?.name ?? path.basename(packagePath),
|
|
790
|
+
icon,
|
|
791
|
+
identity: app ? readNativeAppIdentityFromXcodeTarget(app) : null,
|
|
792
|
+
}
|
|
751
793
|
}
|
|
752
794
|
|
|
753
795
|
// the hosted compile service caps what a project sends it; a local compile
|
|
754
796
|
// reads the author's own files, so nothing caps them there.
|
|
755
797
|
const HOSTED_LIMITS = {
|
|
798
|
+
sourceBytes: MAX_SWIFT_COMPILE_SOURCE_BYTES,
|
|
756
799
|
resources: MAX_SWIFT_COMPILE_RESOURCES,
|
|
757
800
|
resourceBytes: MAX_SWIFT_COMPILE_RESOURCE_BYTES,
|
|
758
801
|
resourceTotalBytes: MAX_SWIFT_COMPILE_RESOURCE_TOTAL_BYTES,
|
|
@@ -761,6 +804,7 @@ const HOSTED_LIMITS = {
|
|
|
761
804
|
imageTotalBytes: MAX_SWIFT_COMPILE_IMAGE_TOTAL_BYTES,
|
|
762
805
|
}
|
|
763
806
|
const LOCAL_LIMITS: typeof HOSTED_LIMITS = {
|
|
807
|
+
sourceBytes: Infinity,
|
|
764
808
|
resources: Infinity,
|
|
765
809
|
resourceBytes: Infinity,
|
|
766
810
|
resourceTotalBytes: Infinity,
|
|
@@ -969,7 +1013,7 @@ export function buildSwiftBundleFiles(
|
|
|
969
1013
|
const xcode = xcodeLayout(packagePath)
|
|
970
1014
|
const bundles =
|
|
971
1015
|
xcode?.resourceBundles ??
|
|
972
|
-
swiftResourceBundles(inputs ?? packageSources(packagePath), relative)
|
|
1016
|
+
swiftResourceBundles(inputs ?? packageSources(packagePath, hosted), relative)
|
|
973
1017
|
const addFile = (key: string, bytes: Buffer) => {
|
|
974
1018
|
if (
|
|
975
1019
|
Object.keys(files).length >= limits.resources ||
|
|
@@ -1175,6 +1219,7 @@ export function standinLibraries(
|
|
|
1175
1219
|
|
|
1176
1220
|
function packageSources(
|
|
1177
1221
|
packagePath: string,
|
|
1222
|
+
hosted = false,
|
|
1178
1223
|
linked: ReadonlySet<SwiftStandin> = new Set(),
|
|
1179
1224
|
): Record<string, string> {
|
|
1180
1225
|
const xcode = xcodeLayout(packagePath)
|
|
@@ -1184,13 +1229,14 @@ function packageSources(
|
|
|
1184
1229
|
),
|
|
1185
1230
|
)
|
|
1186
1231
|
const files: Record<string, string> = {}
|
|
1232
|
+
const limits = inputLimits(hosted)
|
|
1187
1233
|
for (const file of sourceFiles(packagePath)) {
|
|
1188
1234
|
const relative = path.relative(packagePath, file).split(path.sep).join(path.posix.sep)
|
|
1189
1235
|
if (manifests.has(file)) continue
|
|
1190
1236
|
files[xcode ? xcodeInputKey(xcode, file, relative) : relative] = readPackageFile(
|
|
1191
1237
|
packagePath,
|
|
1192
1238
|
file,
|
|
1193
|
-
|
|
1239
|
+
limits.sourceBytes,
|
|
1194
1240
|
).toString('utf8')
|
|
1195
1241
|
}
|
|
1196
1242
|
if (xcode) {
|
|
@@ -1221,7 +1267,11 @@ function packageSnapshot(project: SwiftProject, entry: string): SwiftPackageSnap
|
|
|
1221
1267
|
const linked = layout
|
|
1222
1268
|
? standinLibraries(layout.binaries, layout.standins, layout.app.rootDir)
|
|
1223
1269
|
: new Map<string, SwiftStandin[]>()
|
|
1224
|
-
const files = packageSources(
|
|
1270
|
+
const files = packageSources(
|
|
1271
|
+
project.packagePath,
|
|
1272
|
+
Boolean(project.hosted),
|
|
1273
|
+
new Set([...linked.values()].flat()),
|
|
1274
|
+
)
|
|
1225
1275
|
const native = nativeSourceDescriptor(project.packagePath)
|
|
1226
1276
|
return {
|
|
1227
1277
|
files,
|
|
@@ -1262,7 +1312,7 @@ function packageSnapshot(project: SwiftProject, entry: string): SwiftPackageSnap
|
|
|
1262
1312
|
glue: readPackageFile(
|
|
1263
1313
|
project.packagePath,
|
|
1264
1314
|
path.join(project.packagePath, NATIVE_GLUE),
|
|
1265
|
-
|
|
1315
|
+
inputLimits(Boolean(project.hosted)).sourceBytes,
|
|
1266
1316
|
).toString('utf8'),
|
|
1267
1317
|
}
|
|
1268
1318
|
: {}),
|
|
@@ -1411,6 +1461,21 @@ const API_GAP_PATTERNS: [RegExp, (match: RegExpExecArray) => string][] = [
|
|
|
1411
1461
|
(match) => `${match[1]}.${match[2]}`,
|
|
1412
1462
|
],
|
|
1413
1463
|
[/^'([^']+)' is unavailable/, (match) => match[1]!],
|
|
1464
|
+
// an instance api Peach declares static, or the reverse
|
|
1465
|
+
[
|
|
1466
|
+
/^(?:static|instance) member '([^']+)' cannot be used on (?:instance of )?type '([^']+)'/,
|
|
1467
|
+
(match) => `${match[2]}.${match[1]}`,
|
|
1468
|
+
],
|
|
1469
|
+
// an overridable api Peach declares in an extension
|
|
1470
|
+
[
|
|
1471
|
+
/^(?:non-@objc )?[\w ]+ '([^']+)' is declared in extension of '([^']+)' and cannot be overridden/,
|
|
1472
|
+
(match) => `${match[2]}.${match[1]}`,
|
|
1473
|
+
],
|
|
1474
|
+
// a form of a macro Peach's expansion does not accept
|
|
1475
|
+
[
|
|
1476
|
+
/^(@\w+) requires an initial value/,
|
|
1477
|
+
(match) => `${match[1]} without an initial value`,
|
|
1478
|
+
],
|
|
1414
1479
|
[/^no such module '([^']+)'/, (match) => `module ${match[1]}`],
|
|
1415
1480
|
]
|
|
1416
1481
|
|
|
@@ -1425,7 +1490,7 @@ export function swiftApiGapReport(output: string): string | null {
|
|
|
1425
1490
|
const file = diagnostic[1]!
|
|
1426
1491
|
.replace(new RegExp(`^(?:${XCODE_APP_DIR}|${XCODE_FRAMEWORKS_DIR}/[^/]+)/`), '')
|
|
1427
1492
|
.replace(new RegExp(`^${XCODE_PACKAGES_DIR}/`), '')
|
|
1428
|
-
const message = diagnostic[4]
|
|
1493
|
+
const message = diagnostic[4]!.trim()
|
|
1429
1494
|
const rule = API_GAP_PATTERNS.find(([pattern]) => pattern.test(message))
|
|
1430
1495
|
const key = rule ? rule[1](rule[0].exec(message)!) : message
|
|
1431
1496
|
const table = rule ? gaps : other
|
|
@@ -1726,9 +1791,8 @@ export function swiftPackageWatchFiles(
|
|
|
1726
1791
|
packagePath = fs.realpathSync(packagePath)
|
|
1727
1792
|
return [
|
|
1728
1793
|
...(xcodeProjects.has(packagePath) ? [] : [path.join(packagePath, 'Package.swift')]),
|
|
1729
|
-
...
|
|
1794
|
+
...packageFiles(packagePath),
|
|
1730
1795
|
...nativeSourceFiles(packagePath),
|
|
1731
|
-
...resourceCandidateFiles(packagePath),
|
|
1732
1796
|
...(moduleRoot
|
|
1733
1797
|
? swiftModuleInputFiles(moduleRoot).map((file) => path.join(moduleRoot, file))
|
|
1734
1798
|
: []),
|
|
@@ -1744,14 +1808,14 @@ export async function swiftPackageMetadata(
|
|
|
1744
1808
|
signal?: AbortSignal,
|
|
1745
1809
|
): Promise<SwiftEntryModuleArgs> {
|
|
1746
1810
|
packagePath = fs.realpathSync(packagePath)
|
|
1811
|
+
const project = projectFor(packagePath, hosted, signal)
|
|
1812
|
+
const limits = inputLimits(Boolean(project.hosted))
|
|
1747
1813
|
const mains = sourceFiles(packagePath).filter(
|
|
1748
1814
|
(file) =>
|
|
1749
1815
|
file.endsWith('.swift') &&
|
|
1750
1816
|
swiftApplicationEntry(
|
|
1751
1817
|
file,
|
|
1752
|
-
readPackageFile(packagePath, file,
|
|
1753
|
-
'utf8',
|
|
1754
|
-
),
|
|
1818
|
+
readPackageFile(packagePath, file, limits.sourceBytes).toString('utf8'),
|
|
1755
1819
|
),
|
|
1756
1820
|
)
|
|
1757
1821
|
const native = nativeSourceDescriptor(packagePath)
|
|
@@ -1760,7 +1824,6 @@ export async function swiftPackageMetadata(
|
|
|
1760
1824
|
`swift package ${packageName} needs exactly one application entry source or generated native modules; found ${mains.length}`,
|
|
1761
1825
|
)
|
|
1762
1826
|
}
|
|
1763
|
-
const project = projectFor(packagePath, hosted, signal)
|
|
1764
1827
|
const entry =
|
|
1765
1828
|
mains.length === 1
|
|
1766
1829
|
? path.relative(packagePath, mains[0]).split(path.sep).join(path.posix.sep)
|
package/src/swift-icu.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { existsSync, readFileSync } from 'node:fs'
|
|
|
4
4
|
import path from 'node:path'
|
|
5
5
|
import { promisify } from 'node:util'
|
|
6
6
|
import { gzip } from 'node:zlib'
|
|
7
|
-
import { swiftSdkOptions } from './swift-toolchain.ts'
|
|
7
|
+
import { swiftEnvironment, swiftSdkOptions } from './swift-toolchain.ts'
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* the icu common data a swift artifact reserves and its host fills
|
|
@@ -73,17 +73,23 @@ async function load(toolchainBin: string, sdk: string): Promise<Buffer> {
|
|
|
73
73
|
// missing piece instead of shelling out to sdk configure.
|
|
74
74
|
if (!existsSync(swift))
|
|
75
75
|
throw new Error(`swift-icu: no swift at ${swift} for the ${sdk} SDK's icu data`)
|
|
76
|
-
const { stdout: listed } = await run(swift, ['sdk', 'list', ...swiftSdkOptions()]
|
|
76
|
+
const { stdout: listed } = await run(swift, ['sdk', 'list', ...swiftSdkOptions()], {
|
|
77
|
+
env: swiftEnvironment(),
|
|
78
|
+
})
|
|
77
79
|
if (!listed.split('\n').some((line) => line.trim() === sdk))
|
|
78
80
|
throw new Error(`swift-icu: ${sdk} is not installed for ${swift}`)
|
|
79
|
-
const { stdout } = await run(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
const { stdout } = await run(
|
|
82
|
+
swift,
|
|
83
|
+
[
|
|
84
|
+
'sdk',
|
|
85
|
+
'configure',
|
|
86
|
+
...swiftSdkOptions(),
|
|
87
|
+
'--show-configuration',
|
|
88
|
+
sdk,
|
|
89
|
+
'wasm32-unknown-wasip1',
|
|
90
|
+
],
|
|
91
|
+
{ env: swiftEnvironment() },
|
|
92
|
+
)
|
|
87
93
|
const resources = /^swiftStaticResourcesPath: (.+)$/m.exec(stdout)?.[1]?.trim()
|
|
88
94
|
if (!resources) throw new Error(`swift-icu: ${sdk} names no static resources`)
|
|
89
95
|
// a copy, so the 42 MB archive it was sliced from can be collected
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process'
|
|
2
|
+
import { createHash } from 'node:crypto'
|
|
3
|
+
import fs from 'node:fs'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { cacheDir } from './home-paths'
|
|
6
|
+
import { swiftSdkPaths, swiftModuleSdk } from './swift-compile'
|
|
7
|
+
import { swiftModuleToolchain } from './swift-distribution'
|
|
8
|
+
import { IOS_SDK_ID, materializeObjcSdk } from './swift-objc-standin'
|
|
9
|
+
import { swiftTargetModuleName } from './swift-package'
|
|
10
|
+
import { swiftPackageManifest, swiftTargetFiles } from './swift-package-manifest'
|
|
11
|
+
import { remoteSwiftCheckouts, type SwiftPackageBinary } from './swift-packages'
|
|
12
|
+
import { readPackageFile } from './swift-source-file'
|
|
13
|
+
import { swiftEnvironment } from './swift-toolchain'
|
|
14
|
+
import type { XcodeAppTarget } from './xcode-project'
|
|
15
|
+
|
|
16
|
+
/** compile original implementations before any header-derived replacement can hide them.
|
|
17
|
+
* successful objects still require the canonical framework bridge before linking. */
|
|
18
|
+
export function compileXcodeObjCImplementations(
|
|
19
|
+
app: XcodeAppTarget,
|
|
20
|
+
binaries: readonly SwiftPackageBinary[],
|
|
21
|
+
moduleRoot: string,
|
|
22
|
+
): never {
|
|
23
|
+
const toolchain = swiftModuleToolchain(moduleRoot)
|
|
24
|
+
const sdk = swiftSdkPaths(moduleRoot, toolchain.toolchainBin)
|
|
25
|
+
const sources = new Map<string, Buffer>()
|
|
26
|
+
const links: Record<string, string> = {}
|
|
27
|
+
const checkouts = remoteSwiftCheckouts(app.rootDir)
|
|
28
|
+
const add = (file: string, header = false) => {
|
|
29
|
+
const resolved = header ? fs.realpathSync(file) : file
|
|
30
|
+
if (resolved !== file) links[file] = resolved
|
|
31
|
+
if (!sources.has(file))
|
|
32
|
+
sources.set(
|
|
33
|
+
file,
|
|
34
|
+
readPackageFile(
|
|
35
|
+
file.startsWith(`${checkouts}${path.sep}`) ? checkouts : app.rootDir,
|
|
36
|
+
resolved,
|
|
37
|
+
Infinity,
|
|
38
|
+
),
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
const addHeaders = (directory: string): void => {
|
|
42
|
+
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
43
|
+
const file = path.join(directory, entry.name)
|
|
44
|
+
if (entry.isDirectory()) addHeaders(file)
|
|
45
|
+
else if (/\.(h|hh|hpp|hxx|inc|inl|modulemap)$/.test(entry.name)) add(file, true)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const targets: {
|
|
49
|
+
name: string
|
|
50
|
+
deploymentTarget: string | null
|
|
51
|
+
files: string[]
|
|
52
|
+
includes: string[]
|
|
53
|
+
frameworks: string[]
|
|
54
|
+
flags: string[]
|
|
55
|
+
cxxFlags: string[]
|
|
56
|
+
prefix: string | null
|
|
57
|
+
fileFlags: Record<string, string[]>
|
|
58
|
+
publicHeaders?: string
|
|
59
|
+
}[] = []
|
|
60
|
+
const configuration = app.configuration === 'Debug' ? 'debug' : 'release'
|
|
61
|
+
for (const binary of binaries) {
|
|
62
|
+
if (!binary.reached || binary.publicHeaders === undefined) continue
|
|
63
|
+
const manifest = swiftPackageManifest(binary.dir)
|
|
64
|
+
const target = manifest.targets.get(binary.target)
|
|
65
|
+
if (!target)
|
|
66
|
+
throw new Error(`missing original implementation target ${binary.target}`)
|
|
67
|
+
const files = swiftTargetFiles(manifest, target)
|
|
68
|
+
for (const file of files) add(file)
|
|
69
|
+
addHeaders(path.join(target.dir, target.publicHeadersPath))
|
|
70
|
+
for (const dependency of binary.headerDependencies ?? [])
|
|
71
|
+
addHeaders(path.resolve(binary.dir, dependency.path))
|
|
72
|
+
const settings = target.nativeSettings.filter(
|
|
73
|
+
(setting) =>
|
|
74
|
+
setting.configuration === null || setting.configuration === configuration,
|
|
75
|
+
)
|
|
76
|
+
targets.push({
|
|
77
|
+
name: target.name,
|
|
78
|
+
deploymentTarget: app.iosDeploymentTarget,
|
|
79
|
+
files: files.filter((file) => /\.(c|cc|cpp|cxx|m|mm)$/.test(file)),
|
|
80
|
+
includes: [
|
|
81
|
+
path.join(target.dir, target.publicHeadersPath),
|
|
82
|
+
...(binary.headerDependencies ?? []).map((dependency) =>
|
|
83
|
+
path.resolve(binary.dir, dependency.path),
|
|
84
|
+
),
|
|
85
|
+
],
|
|
86
|
+
frameworks: [],
|
|
87
|
+
// retain the source iOS package's ARC and module semantics on the wasm ABI.
|
|
88
|
+
flags: [
|
|
89
|
+
'-fobjc-arc',
|
|
90
|
+
'-fmodules',
|
|
91
|
+
'-DSWIFT_PACKAGE',
|
|
92
|
+
...(configuration === 'debug' ? ['-DDEBUG'] : []),
|
|
93
|
+
...settings
|
|
94
|
+
.filter((setting) => setting.tool === 'c')
|
|
95
|
+
.flatMap((setting) => setting.flags),
|
|
96
|
+
],
|
|
97
|
+
cxxFlags: settings
|
|
98
|
+
.filter((setting) => setting.tool === 'cxx')
|
|
99
|
+
.flatMap((setting) => setting.flags),
|
|
100
|
+
prefix: null,
|
|
101
|
+
fileFlags: {},
|
|
102
|
+
publicHeaders: path.join(target.dir, target.publicHeadersPath),
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
for (const target of [...app.frameworks, app]) {
|
|
106
|
+
if (!target.objc) continue
|
|
107
|
+
const files = target.allSources.filter((file) => /\.(c|cc|cpp|cxx|m|mm)$/.test(file))
|
|
108
|
+
for (const file of files) add(file)
|
|
109
|
+
for (const file of [
|
|
110
|
+
...target.objc.headers,
|
|
111
|
+
...[target.objc.prefixHeader, target.objc.bridgingHeader].filter(
|
|
112
|
+
(file): file is string => file !== null,
|
|
113
|
+
),
|
|
114
|
+
])
|
|
115
|
+
add(file, true)
|
|
116
|
+
targets.push({
|
|
117
|
+
name: target.moduleName,
|
|
118
|
+
deploymentTarget: target.iosDeploymentTarget,
|
|
119
|
+
files,
|
|
120
|
+
includes: target.objc.headerSearchPaths,
|
|
121
|
+
frameworks: target.objc.frameworkSearchPaths,
|
|
122
|
+
flags: target.objc.flags,
|
|
123
|
+
cxxFlags: target.objc.cxxFlags,
|
|
124
|
+
prefix: target.objc.prefixHeader,
|
|
125
|
+
fileFlags: target.objc.fileFlags,
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
const digest = (bytes: Buffer | string) =>
|
|
129
|
+
createHash('sha256').update(bytes).digest('hex')
|
|
130
|
+
const inputs = Object.fromEntries(
|
|
131
|
+
[...sources].map(([file, bytes]) => [file, digest(bytes)]),
|
|
132
|
+
)
|
|
133
|
+
const identity = {
|
|
134
|
+
version: 1,
|
|
135
|
+
release: toolchain.release,
|
|
136
|
+
compiler: digest(fs.readFileSync(path.join(toolchain.toolchainBin, 'clang'))),
|
|
137
|
+
sdk: swiftModuleSdk(moduleRoot),
|
|
138
|
+
declarations: IOS_SDK_ID,
|
|
139
|
+
targets,
|
|
140
|
+
inputs,
|
|
141
|
+
links,
|
|
142
|
+
}
|
|
143
|
+
const parent = path.join(cacheDir(), 'swift-objc-implementations')
|
|
144
|
+
fs.mkdirSync(parent, { recursive: true })
|
|
145
|
+
const key = digest(JSON.stringify(identity))
|
|
146
|
+
const work = path.join(parent, key)
|
|
147
|
+
const resultFile = path.join(work, 'result.json')
|
|
148
|
+
const record = (value: unknown): value is Record<string, unknown> =>
|
|
149
|
+
value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
150
|
+
if (fs.existsSync(work)) {
|
|
151
|
+
let message: string
|
|
152
|
+
try {
|
|
153
|
+
const result: unknown = JSON.parse(
|
|
154
|
+
readPackageFile(work, resultFile, Infinity).toString(),
|
|
155
|
+
)
|
|
156
|
+
if (
|
|
157
|
+
!record(result) ||
|
|
158
|
+
result.key !== key ||
|
|
159
|
+
typeof result.message !== 'string' ||
|
|
160
|
+
!result.message ||
|
|
161
|
+
!record(result.files) ||
|
|
162
|
+
Object.keys(result.files).length < sources.size + 1 ||
|
|
163
|
+
!('receipt.json' in result.files)
|
|
164
|
+
)
|
|
165
|
+
throw new Error('invalid completed receipt')
|
|
166
|
+
for (const [file, hash] of Object.entries(result.files)) {
|
|
167
|
+
if (
|
|
168
|
+
typeof hash !== 'string' ||
|
|
169
|
+
digest(readPackageFile(work, path.resolve(work, file), Infinity)) !== hash
|
|
170
|
+
)
|
|
171
|
+
throw new Error(`retained file changed: ${file}`)
|
|
172
|
+
}
|
|
173
|
+
message = result.message
|
|
174
|
+
} catch (cause) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
`Objective-C implementation preparation has an incomplete or damaged receipt at ${work}`,
|
|
177
|
+
{ cause },
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
throw new Error(message)
|
|
181
|
+
}
|
|
182
|
+
// exclusive creation prevents another caller from writing the same objects.
|
|
183
|
+
fs.mkdirSync(work)
|
|
184
|
+
const staged = (file: string): string => {
|
|
185
|
+
for (const { root, name } of [
|
|
186
|
+
{ root: checkouts, name: 'packages' },
|
|
187
|
+
{ root: app.rootDir, name: 'project' },
|
|
188
|
+
]) {
|
|
189
|
+
const relative = path.relative(root, file)
|
|
190
|
+
if (
|
|
191
|
+
relative !== '..' &&
|
|
192
|
+
!relative.startsWith(`..${path.sep}`) &&
|
|
193
|
+
!path.isAbsolute(relative)
|
|
194
|
+
)
|
|
195
|
+
return path.join(work, name, relative)
|
|
196
|
+
}
|
|
197
|
+
// native SDK include paths select the wasm SDK, never host system headers.
|
|
198
|
+
if (file.startsWith('/usr/include/'))
|
|
199
|
+
return path.join(sdk.sysroot, 'include', file.slice('/usr/include/'.length))
|
|
200
|
+
throw new Error(
|
|
201
|
+
`Objective-C compiler input is outside the project and its pinned packages: ${file}`,
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
for (const [file, bytes] of sources) {
|
|
205
|
+
const output = staged(file)
|
|
206
|
+
fs.mkdirSync(path.dirname(output), { recursive: true })
|
|
207
|
+
fs.writeFileSync(output, bytes)
|
|
208
|
+
}
|
|
209
|
+
const headers = materializeObjcSdk(path.join(cacheDir(), 'swift-standins'))
|
|
210
|
+
const modulemaps = targets.flatMap((target, index) => {
|
|
211
|
+
if (!target.publicHeaders) return []
|
|
212
|
+
const custom = path.join(target.publicHeaders, 'module.modulemap')
|
|
213
|
+
if (sources.has(custom)) return [`-fmodule-map-file=${staged(custom)}`]
|
|
214
|
+
const output = path.join(work, `module-${index}.modulemap`)
|
|
215
|
+
fs.writeFileSync(
|
|
216
|
+
output,
|
|
217
|
+
`module ${swiftTargetModuleName(target.name)} {\n umbrella ${JSON.stringify(staged(target.publicHeaders))}\n export *\n}\n`,
|
|
218
|
+
)
|
|
219
|
+
return [`-fmodule-map-file=${output}`]
|
|
220
|
+
})
|
|
221
|
+
const commands: {
|
|
222
|
+
target: string
|
|
223
|
+
source: string
|
|
224
|
+
args: string[]
|
|
225
|
+
status: number | null
|
|
226
|
+
signal: string | null
|
|
227
|
+
object?: string
|
|
228
|
+
}[] = []
|
|
229
|
+
const receipt = path.join(work, 'receipt.json')
|
|
230
|
+
const save = (status: string) =>
|
|
231
|
+
fs.writeFileSync(
|
|
232
|
+
receipt,
|
|
233
|
+
JSON.stringify({ ...identity, work, status, commands }, null, 2),
|
|
234
|
+
)
|
|
235
|
+
const finish = (status: string, message: string): never => {
|
|
236
|
+
save(status)
|
|
237
|
+
const files = [
|
|
238
|
+
...[...sources.keys()].map(staged),
|
|
239
|
+
...modulemaps.map((flag) => flag.slice('-fmodule-map-file='.length)),
|
|
240
|
+
receipt,
|
|
241
|
+
...commands.flatMap((command) => {
|
|
242
|
+
const output = command.args[command.args.lastIndexOf('-o') + 1]!
|
|
243
|
+
return [output.replace(/\.o$/, '.log'), ...(command.object ? [output] : [])]
|
|
244
|
+
}),
|
|
245
|
+
]
|
|
246
|
+
const result = {
|
|
247
|
+
key,
|
|
248
|
+
message,
|
|
249
|
+
files: Object.fromEntries(
|
|
250
|
+
files.map((file) => [path.relative(work, file), digest(fs.readFileSync(file))]),
|
|
251
|
+
),
|
|
252
|
+
}
|
|
253
|
+
fs.writeFileSync(`${resultFile}.pending`, JSON.stringify(result, null, 2))
|
|
254
|
+
fs.renameSync(`${resultFile}.pending`, resultFile)
|
|
255
|
+
throw new Error(message)
|
|
256
|
+
}
|
|
257
|
+
save('compiling')
|
|
258
|
+
for (const [index, target] of targets.entries()) {
|
|
259
|
+
const version = /^(\d{1,2})(?:\.(\d{1,2}))?(?:\.(\d{1,2}))?$/.exec(
|
|
260
|
+
target.deploymentTarget ?? '',
|
|
261
|
+
)
|
|
262
|
+
if (!version) {
|
|
263
|
+
return finish(
|
|
264
|
+
'unsupported-deployment-target',
|
|
265
|
+
`${target.name}: Objective-C compilation requires an explicit numeric IPHONEOS_DEPLOYMENT_TARGET; received ${JSON.stringify(target.deploymentTarget)}. Inputs: ${receipt}`,
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
const minimum =
|
|
269
|
+
Number(version[1]) * 10_000 +
|
|
270
|
+
Number(version[2] ?? 0) * 100 +
|
|
271
|
+
Number(version[3] ?? 0)
|
|
272
|
+
for (const [unit, file] of target.files.entries()) {
|
|
273
|
+
const output = path.join(work, `${index}-${unit}.o`)
|
|
274
|
+
const cxx = /\.(mm|cc|cpp|cxx)$/.test(file)
|
|
275
|
+
const objc = /\.mm?$/.test(file)
|
|
276
|
+
const flags = [
|
|
277
|
+
...target.flags.filter(
|
|
278
|
+
(flag) =>
|
|
279
|
+
(!cxx || !flag.startsWith('-std=')) && (objc || flag !== '-fobjc-arc'),
|
|
280
|
+
),
|
|
281
|
+
...(cxx ? target.cxxFlags : []),
|
|
282
|
+
...(target.fileFlags[file] ?? []),
|
|
283
|
+
].map((flag, index, flags) => {
|
|
284
|
+
if (
|
|
285
|
+
['-I', '-F', '-iquote', '-isystem', '-include'].includes(flags[index - 1] ?? '')
|
|
286
|
+
)
|
|
287
|
+
return staged(flag)
|
|
288
|
+
const include = /^(-I|-F)(\/.*)$/.exec(flag)
|
|
289
|
+
return include ? `${include[1]}${staged(include[2]!)}` : flag
|
|
290
|
+
})
|
|
291
|
+
const args = [
|
|
292
|
+
'--target=wasm32-unknown-wasip1',
|
|
293
|
+
`--sysroot=${sdk.sysroot}`,
|
|
294
|
+
'-fwasm-exceptions',
|
|
295
|
+
'-fexceptions',
|
|
296
|
+
'-D_WASI_EMULATED_SIGNAL',
|
|
297
|
+
'-D_WASI_EMULATED_MMAN',
|
|
298
|
+
`-D__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__=${minimum}`,
|
|
299
|
+
`-D__IPHONE_OS_VERSION_MIN_REQUIRED=${minimum}`,
|
|
300
|
+
...(objc ? ['-fobjc-runtime=gnustep-2.2', '-fobjc-exceptions', '-fblocks'] : []),
|
|
301
|
+
'-F',
|
|
302
|
+
path.join(headers, 'System/Library/Frameworks'),
|
|
303
|
+
// installed libc and objc runtime headers own the target ABI.
|
|
304
|
+
'-idirafter',
|
|
305
|
+
path.join(headers, 'usr/include'),
|
|
306
|
+
...target.includes.flatMap((include) => ['-I', staged(include)]),
|
|
307
|
+
...target.frameworks.flatMap((framework) => ['-F', staged(framework)]),
|
|
308
|
+
...modulemaps,
|
|
309
|
+
`-fmodule-name=${swiftTargetModuleName(target.name)}`,
|
|
310
|
+
`-fmodules-cache-path=${path.join(work, 'module-cache')}`,
|
|
311
|
+
...flags,
|
|
312
|
+
...(target.prefix ? ['-include', staged(target.prefix)] : []),
|
|
313
|
+
'-Os',
|
|
314
|
+
'-c',
|
|
315
|
+
staged(file),
|
|
316
|
+
'-o',
|
|
317
|
+
output,
|
|
318
|
+
]
|
|
319
|
+
const result = spawnSync(
|
|
320
|
+
path.join(toolchain.toolchainBin, cxx ? 'clang++' : 'clang'),
|
|
321
|
+
args,
|
|
322
|
+
{
|
|
323
|
+
cwd: work,
|
|
324
|
+
encoding: 'utf8',
|
|
325
|
+
env: swiftEnvironment(),
|
|
326
|
+
timeout: 120_000,
|
|
327
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
328
|
+
},
|
|
329
|
+
)
|
|
330
|
+
fs.writeFileSync(
|
|
331
|
+
path.join(work, `${index}-${unit}.log`),
|
|
332
|
+
`${result.stdout ?? ''}${result.stderr ?? ''}${result.error?.message ?? ''}`,
|
|
333
|
+
)
|
|
334
|
+
commands.push({
|
|
335
|
+
target: target.name,
|
|
336
|
+
source: file,
|
|
337
|
+
args,
|
|
338
|
+
status: result.status,
|
|
339
|
+
signal: result.signal,
|
|
340
|
+
...(result.status === 0 ? { object: digest(fs.readFileSync(output)) } : {}),
|
|
341
|
+
})
|
|
342
|
+
if (result.error || result.status !== 0) {
|
|
343
|
+
return finish(
|
|
344
|
+
'compile-failed',
|
|
345
|
+
`Objective-C implementation compile failed in ${target.name}: ${file}\n${result.stderr || result.stdout || result.error?.message}\nOriginal inputs, compiler command and diagnostics: ${receipt}`,
|
|
346
|
+
)
|
|
347
|
+
}
|
|
348
|
+
save('compiling')
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return finish(
|
|
352
|
+
'requires-framework-bridge',
|
|
353
|
+
`Objective-C implementations compiled, but linking their original method bodies requires the canonical Swift framework object bridge. No implementation was replaced. Inputs and objects: ${receipt}`,
|
|
354
|
+
)
|
|
355
|
+
}
|