peachsim 0.1.2221 → 0.1.2223
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/cli/commands/compat.ts +4 -4
- package/cli/commands/report-issue.ts +1 -1
- package/cli/runtime-compat.ts +7 -2
- 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 +1 -1
- 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 +461 -369
- 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 +3 -2
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/expo-swift-bundle.cjs +458 -367
- package/dist-lib/expo-swift-metadata.cjs +1 -1
- package/dist-lib/expo-swift-metadata.mjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +681 -562
- package/dist-lib/host/fetch-proxy-handler.cjs +10 -7
- 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 +8232 -7815
- 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 +8146 -7737
- package/dist-lib/native-app-identity-project.cjs +331 -13
- package/dist-lib/native-app-identity-project.mjs +331 -13
- package/dist-lib/native-app-identity.cjs +16 -3
- package/dist-lib/native-app-identity.mjs +16 -3
- package/dist-lib/native-firebase-metadata.cjs +1 -1
- package/dist-lib/native-firebase-metadata.mjs +1 -1
- package/dist-lib/pdfium-raster.cjs +1 -1
- package/dist-lib/pdfium-raster.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/public-origins-lib-esm.json +25 -24
- package/dist-lib/public-origins-lib.json +99 -96
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/reap-browser.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +518 -408
- package/dist-lib/swift-compile.cjs +69 -36
- package/dist-lib/swift-compile.mjs +69 -36
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift-toolchain.cjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +143 -56
- package/dist-lib/vite.cjs +2930 -2800
- package/dist-lib/worklet-executable.cjs +1 -1
- package/dist-lib/worklet-executable.mjs +1 -1
- package/dist-types/packages/peach-compat/src/stubs/native-seams/react-native-cookies.d.ts +2 -1
- package/dist-types/packages/peach-engine/src/cli-companion.d.ts +1 -0
- package/dist-types/packages/peach-engine/src/cli-host-terser.d.ts +7 -0
- package/dist-types/packages/peach-engine/src/engine/glass-material.d.ts +1 -1
- package/dist-types/packages/peach-engine/src/engine/liquid-glass.d.ts +1 -0
- package/dist-types/packages/peach-engine/src/engine/native-layout.d.ts +1 -0
- package/dist-types/packages/peach-engine/src/engine/node.d.ts +5 -0
- package/dist-types/packages/peach-engine/src/engine/scene-table.d.ts +8 -1
- package/dist-types/packages/peach-engine/src/engine/types.d.ts +3 -0
- package/dist-types/packages/peach-engine/src/engine/yoga-layout.d.ts +1 -1
- package/dist-types/packages/peach-engine/src/remote-stream/cloud-display.d.ts +2 -2
- package/dist-types/packages/peach-engine/src/render-worker/shell-tree-wire.d.ts +1 -2
- package/dist-types/packages/peach-globals/src/browser-object-url.d.ts +10 -0
- package/dist-types/packages/peach-kit/src/internal.d.ts +1 -0
- package/dist-types/packages/peach-kit/src/ios-tint.d.ts +1 -0
- package/dist-types/packages/peach-kit/src/transitions/screen-transition-math.d.ts +25 -0
- package/kotlin/sdk.tar.gz +0 -0
- package/package.json +1 -1
- package/runtime-companion.json +1 -1
- package/sdk/ios/System/Library/Frameworks/SceneKit.framework/Headers/SCNAnimation.h +4 -4
- package/sdk/ios/System/Library/Frameworks/SceneKit.framework/Headers/SCNCameraController.h +1 -1
- package/sdk/ios/System/Library/Frameworks/SceneKit.framework/Headers/SCNConstraint.h +5 -5
- package/sdk/ios/System/Library/Frameworks/SceneKit.framework/Headers/SCNGeometry.h +1 -1
- package/sdk/ios/System/Library/Frameworks/SceneKit.framework/Headers/SCNLevelOfDetail.h +1 -1
- package/sdk/ios/System/Library/Frameworks/SceneKit.framework/Headers/SCNParametricGeometry.h +9 -9
- package/sdk/ios/System/Library/Frameworks/SceneKit.framework/Headers/SCNParticleSystem.h +1 -1
- package/sdk/ios/System/Library/Frameworks/SceneKit.framework/Headers/SCNPhysicsBehavior.h +7 -7
- package/sdk/ios/System/Library/Frameworks/SceneKit.framework/Headers/SCNRenderer.h +1 -1
- package/sdk/ios/System/Library/Frameworks/SceneKit.framework/Headers/SCNShadable.h +1 -1
- package/sdk/ios/System/Library/Frameworks/SceneKit.framework/Headers/SCNTechnique.h +1 -1
- package/sdk/ios/System/Library/Frameworks/UIKit.framework/Headers/UISegmentedControl.h +1 -1
- package/sdk/ios/System/Library/Frameworks/UIKit.framework/Headers/UITabBar.h +1 -1
- package/src/cli-runtime.ts +1 -0
- package/src/conformance-pixel-comparison.ts +16 -3
- package/src/expo-native-workspace.ts +14 -10
- package/src/expo-workspace-exclusion.ts +10 -7
- package/src/react-native-autolinking.ts +2 -1
- package/src/runtime-delivery.ts +7 -0
- package/src/swift-compile.ts +51 -35
- package/src/swift-coverage/xcode-assets.ts +4 -2
- package/src/swift-host.ts +53 -14
- package/src/swift-root-lock.ts +27 -4
- package/src/wasm-optimize.ts +5 -1
- package/src/xcode-project.ts +26 -5
- package/src/xcode-target-members.ts +25 -3
|
@@ -7,7 +7,7 @@ import path from 'node:path'
|
|
|
7
7
|
import { promisify } from 'node:util'
|
|
8
8
|
import { detectPackageManager } from '../cli/commands/detect'
|
|
9
9
|
import { teamMachineInstalled } from './admitted-mac-build'
|
|
10
|
-
import { workspaceExclusion } from './expo-workspace-exclusion'
|
|
10
|
+
import { workspaceExclusion, type WorkspaceExclusion } from './expo-workspace-exclusion'
|
|
11
11
|
import { cacheDir } from './home-paths'
|
|
12
12
|
import { failedOutputLine } from './native-failure-output'
|
|
13
13
|
import { requireNativeGit } from './native-git'
|
|
@@ -49,7 +49,7 @@ export function reactNativePodsEnvironment() {
|
|
|
49
49
|
function workspacePackageManager(
|
|
50
50
|
root: string,
|
|
51
51
|
declared: unknown,
|
|
52
|
-
excluded:
|
|
52
|
+
excluded: WorkspaceExclusion,
|
|
53
53
|
): string {
|
|
54
54
|
const manager = typeof declared === 'string' ? declared : detectPackageManager(root)
|
|
55
55
|
const locks = [
|
|
@@ -116,7 +116,7 @@ export async function verifyExpoWorkspaceCopy(app: string, work: string) {
|
|
|
116
116
|
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
117
117
|
const file = path.join(directory, entry.name)
|
|
118
118
|
const relative = path.relative(root, file)
|
|
119
|
-
if (excluded(relative)) continue
|
|
119
|
+
if (excluded(relative, entry.isDirectory())) continue
|
|
120
120
|
const copy = path.join(work, relative)
|
|
121
121
|
if (entry.isDirectory()) walk(file)
|
|
122
122
|
else if (entry.isSymbolicLink()) {
|
|
@@ -212,7 +212,7 @@ async function hashWorkspaceInputs(
|
|
|
212
212
|
hash: string
|
|
213
213
|
// the subset of inputs that decides the prepared workspace
|
|
214
214
|
nativeHash: string
|
|
215
|
-
excluded:
|
|
215
|
+
excluded: WorkspaceExclusion
|
|
216
216
|
}> {
|
|
217
217
|
const root = findWorkspaceRoot(app)
|
|
218
218
|
const excluded = await workspaceExclusion(
|
|
@@ -286,7 +286,7 @@ async function hashWorkspaceInputs(
|
|
|
286
286
|
signal?.throwIfAborted()
|
|
287
287
|
const file = path.join(directory, entry.name)
|
|
288
288
|
const relative = path.relative(root, file)
|
|
289
|
-
if (excluded(relative)) continue
|
|
289
|
+
if (excluded(relative, entry.isDirectory())) continue
|
|
290
290
|
hash.update(`${relative}\0`)
|
|
291
291
|
if (entry.isSymbolicLink()) {
|
|
292
292
|
const link = `link\0${await fs.promises.readlink(file)}\0`
|
|
@@ -446,7 +446,7 @@ function syncChangedFiles(
|
|
|
446
446
|
root: string,
|
|
447
447
|
work: string,
|
|
448
448
|
appRelative: string,
|
|
449
|
-
excluded:
|
|
449
|
+
excluded: WorkspaceExclusion,
|
|
450
450
|
installed: boolean,
|
|
451
451
|
) {
|
|
452
452
|
const copyIfNewer = (file: string, relative: string, newerOnly: boolean) => {
|
|
@@ -464,8 +464,12 @@ function syncChangedFiles(
|
|
|
464
464
|
)
|
|
465
465
|
return
|
|
466
466
|
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
467
|
-
|
|
468
|
-
|
|
467
|
+
// a tool already running from the copy (the autolinker) must never read a
|
|
468
|
+
// truncated script, so the new bytes land by rename
|
|
469
|
+
const staged = `${target}.${process.pid}.peach-sync`
|
|
470
|
+
fs.copyFileSync(file, staged)
|
|
471
|
+
fs.utimesSync(staged, source.atime, source.mtime)
|
|
472
|
+
fs.renameSync(staged, target)
|
|
469
473
|
}
|
|
470
474
|
const walkInstalled = (directory: string) => {
|
|
471
475
|
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
@@ -490,7 +494,7 @@ function syncChangedFiles(
|
|
|
490
494
|
}
|
|
491
495
|
// the app's ios project is prebuild output, not a source
|
|
492
496
|
if (
|
|
493
|
-
excluded(relative) ||
|
|
497
|
+
excluded(relative, entry.isDirectory()) ||
|
|
494
498
|
entry.isSymbolicLink() ||
|
|
495
499
|
(!sourceIos && relative === path.join(appRelative, 'ios'))
|
|
496
500
|
)
|
|
@@ -640,7 +644,7 @@ function prepare(app: string, stage: 'project' | 'workspace', signal?: AbortSign
|
|
|
640
644
|
async function copyInstalledWorkspace(
|
|
641
645
|
root: string,
|
|
642
646
|
destination: string,
|
|
643
|
-
excluded:
|
|
647
|
+
excluded: WorkspaceExclusion,
|
|
644
648
|
) {
|
|
645
649
|
const installed: string[] = []
|
|
646
650
|
await fs.promises.cp(root, destination, {
|
|
@@ -4,6 +4,8 @@ import path from 'node:path'
|
|
|
4
4
|
import { promisify } from 'node:util'
|
|
5
5
|
import ignore from 'ignore'
|
|
6
6
|
|
|
7
|
+
export type WorkspaceExclusion = (relative: string, isDirectory?: boolean) => boolean
|
|
8
|
+
|
|
7
9
|
// eas build archives a repository without its gitignored paths, so the prepare
|
|
8
10
|
// neither copies nor hashes them: files a dev server generates into an ignored
|
|
9
11
|
// directory never invalidate the prepared workspace.
|
|
@@ -13,7 +15,7 @@ export async function workspaceExclusion(
|
|
|
13
15
|
root: string,
|
|
14
16
|
portable = false,
|
|
15
17
|
linkedPackages: string[] = [],
|
|
16
|
-
) {
|
|
18
|
+
): Promise<WorkspaceExclusion> {
|
|
17
19
|
const listed = await promisify(execFile)(
|
|
18
20
|
'git',
|
|
19
21
|
['ls-files', '-z', '--others', '--ignored', '--exclude-standard', '--directory'],
|
|
@@ -34,8 +36,8 @@ export async function workspaceExclusion(
|
|
|
34
36
|
.filter(Boolean)
|
|
35
37
|
.map((entry) => entry.replace(/\/$/, '')),
|
|
36
38
|
)
|
|
37
|
-
const ignoreFiles = new Map<string, ReturnType<typeof ignore
|
|
38
|
-
return (relative
|
|
39
|
+
const ignoreFiles = new Map<string, ReturnType<typeof ignore> | null>()
|
|
40
|
+
return (relative, knownDirectory) => {
|
|
39
41
|
const parts = relative.split(path.sep)
|
|
40
42
|
const linked = linkedPackages.some((directory) =>
|
|
41
43
|
relative.startsWith(directory + path.sep),
|
|
@@ -47,16 +49,17 @@ export async function workspaceExclusion(
|
|
|
47
49
|
}
|
|
48
50
|
if (portable && relative) {
|
|
49
51
|
let excluded = false
|
|
50
|
-
const isDirectory =
|
|
52
|
+
const isDirectory =
|
|
53
|
+
knownDirectory ?? fs.lstatSync(path.join(root, relative)).isDirectory()
|
|
51
54
|
for (let index = 0; index < parts.length; index++) {
|
|
52
55
|
const directory = parts.slice(0, index).join(path.sep)
|
|
53
56
|
let rules = ignoreFiles.get(directory)
|
|
54
|
-
if (
|
|
55
|
-
rules = ignore()
|
|
57
|
+
if (rules === undefined) {
|
|
56
58
|
const file = path.join(root, directory, '.gitignore')
|
|
57
|
-
|
|
59
|
+
rules = fs.existsSync(file) ? ignore().add(fs.readFileSync(file, 'utf8')) : null
|
|
58
60
|
ignoreFiles.set(directory, rules)
|
|
59
61
|
}
|
|
62
|
+
if (!rules) continue
|
|
60
63
|
const item = parts.slice(index).join('/')
|
|
61
64
|
const result = rules.test(isDirectory ? `${item}/` : item)
|
|
62
65
|
if (result.ignored) excluded = true
|
|
@@ -29,6 +29,7 @@ async function cliAdvertises(
|
|
|
29
29
|
appRoot: string,
|
|
30
30
|
signal?: AbortSignal,
|
|
31
31
|
): Promise<boolean> {
|
|
32
|
+
const started = Date.now()
|
|
32
33
|
const { stdout, stderr } = await run('node', [cli, ...args], {
|
|
33
34
|
cwd: appRoot,
|
|
34
35
|
signal,
|
|
@@ -38,7 +39,7 @@ async function cliAdvertises(
|
|
|
38
39
|
})
|
|
39
40
|
if (!stdout.trim())
|
|
40
41
|
throw new Error(
|
|
41
|
-
`${cli} ${args.join(' ')}
|
|
42
|
+
`${cli} ${args.join(' ')} exited 0 after ${Date.now() - started}ms with empty help output${stderr.trim() ? `: ${stderr.trim()}` : ''}`,
|
|
42
43
|
)
|
|
43
44
|
return stdout.includes(token)
|
|
44
45
|
}
|
package/src/runtime-delivery.ts
CHANGED
|
@@ -39,6 +39,13 @@ export const RNX_RUNTIME_PRODUCT: RuntimeProduct = {
|
|
|
39
39
|
// vite/metro plugins serve, so its absence means the tarball is not one.
|
|
40
40
|
installedMarker: 'index.html',
|
|
41
41
|
verifyInstall: ensureCliCompanion,
|
|
42
|
+
// canvaskit's ganesh backend is a runtime pack because a browser with WebGPU
|
|
43
|
+
// never runs it, but linux and CI browsers usually have no GPU and always do
|
|
44
|
+
afterInstall: async (dir) => {
|
|
45
|
+
if (process.platform !== 'linux' && !process.env.CI) return
|
|
46
|
+
const { ensureRuntimePackFor } = await import('./runtime-packs.ts')
|
|
47
|
+
await ensureRuntimePackFor(dir, '/engine/canvaskit-ganesh.wasm')
|
|
48
|
+
},
|
|
42
49
|
env: {
|
|
43
50
|
cdnOrigin: 'PEACH_CDN_ORIGIN',
|
|
44
51
|
channel: 'PEACH_RUNTIME_CHANNEL',
|
package/src/swift-compile.ts
CHANGED
|
@@ -140,7 +140,7 @@ import {
|
|
|
140
140
|
SWIFT_FRAMEWORK_PRODUCT_FILE,
|
|
141
141
|
swiftFrameworkProductSchema,
|
|
142
142
|
} from './swiftRuntimeProductContract'
|
|
143
|
-
import { optimizeWasm, WASM_OPTIMIZE_IDENTITY } from './wasm-optimize'
|
|
143
|
+
import { KEEP_WASM_NAMES, optimizeWasm, WASM_OPTIMIZE_IDENTITY } from './wasm-optimize'
|
|
144
144
|
import { writeBuildFile } from './write-build-file'
|
|
145
145
|
import { writeXcodeHeaderSearch } from './xcode-header-search'
|
|
146
146
|
import type { SwiftImageUpload } from './swift-submit'
|
|
@@ -839,7 +839,8 @@ const OPTIONAL_FRAMEWORK_DEPENDENCIES: Record<string, string[]> = {
|
|
|
839
839
|
PeachCFNetwork: [],
|
|
840
840
|
JavaScriptCore: ['PeachJavaScriptCore'],
|
|
841
841
|
PeachJavaScriptCore: [],
|
|
842
|
-
GameplayKit
|
|
842
|
+
// GameplayKit names SpriteKit and SceneKit nodes, as on iOS
|
|
843
|
+
GameplayKit: ['SpriteKit', 'SceneKit', 'PeachBox2D', 'simd', 'GLKit'],
|
|
843
844
|
simd: [],
|
|
844
845
|
ModelIO: ['simd'],
|
|
845
846
|
GLKit: ['ModelIO'],
|
|
@@ -1596,14 +1597,25 @@ export function swiftTargetDependencies(
|
|
|
1596
1597
|
}
|
|
1597
1598
|
|
|
1598
1599
|
const ONE_PRODUCT = '.product(name: "One", package: "sootsim-swift")'
|
|
1599
|
-
|
|
1600
|
-
//
|
|
1601
|
-
//
|
|
1602
|
-
const
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1600
|
+
// the game family (plans/peach/spritekit.md, plans/peach/scenekit.md) links
|
|
1601
|
+
// only into an app whose sources import it, so no other artifact carries it.
|
|
1602
|
+
// SceneKit names SpriteKit, so importing it links both.
|
|
1603
|
+
const GAME_FAMILY = ['SpriteKit', 'SceneKit'] as const
|
|
1604
|
+
const gameProducts = (files: Record<string, string>): string[] => {
|
|
1605
|
+
const imported = new Set(
|
|
1606
|
+
Object.entries(files).flatMap(([file, contents]) =>
|
|
1607
|
+
file.endsWith('.swift') ? swiftImportNames(contents) : [],
|
|
1608
|
+
),
|
|
1606
1609
|
)
|
|
1610
|
+
if (imported.has('SceneKit')) return [...GAME_FAMILY]
|
|
1611
|
+
return imported.has('SpriteKit') ? ['SpriteKit'] : []
|
|
1612
|
+
}
|
|
1613
|
+
const gameProduct = (name: string) =>
|
|
1614
|
+
`.product(name: "${name}", package: "sootsim-swift")`
|
|
1615
|
+
const GAME_REACH: Record<string, string[]> = {
|
|
1616
|
+
SpriteKit: ['SpriteKit', 'PeachBox2D', 'simd', 'GLKit'],
|
|
1617
|
+
SceneKit: ['SceneKit'],
|
|
1618
|
+
}
|
|
1607
1619
|
// every iOS source sees through Foundation what darwin's Foundation exports:
|
|
1608
1620
|
// PeachObjC supplies the ObjectiveC module's selectors, Dispatch Foundation's
|
|
1609
1621
|
// Darwin queue exports, and FoundationNetworking (re-exporting PeachFoundation)
|
|
@@ -1675,6 +1687,7 @@ export function swiftArtifactHash(
|
|
|
1675
1687
|
hash.update(`swift-compile\n${release}\n`)
|
|
1676
1688
|
hash.update(compilerSourceDigest)
|
|
1677
1689
|
if (input.oneModule) hash.update('one-module\n')
|
|
1690
|
+
if (KEEP_WASM_NAMES) hash.update('names\n')
|
|
1678
1691
|
hash.update(swiftModuleDigest(moduleRoot))
|
|
1679
1692
|
if (input.optimize) hash.update(`optimize\n${WASM_OPTIMIZE_IDENTITY}\n`)
|
|
1680
1693
|
for (const file of Object.keys(input.files).sort()) {
|
|
@@ -1862,7 +1875,7 @@ export function emitSwiftPackageManifest(args: {
|
|
|
1862
1875
|
siblings: Map<string, string[]>
|
|
1863
1876
|
moduleRoot: string
|
|
1864
1877
|
oneModule?: boolean
|
|
1865
|
-
|
|
1878
|
+
gameProducts?: string[]
|
|
1866
1879
|
/** link the prebuilt distribution at moduleRoot instead of its sources. */
|
|
1867
1880
|
distribution?: SwiftDistributionManifest
|
|
1868
1881
|
/** prebuilt interfaces owned by local source or system-library targets. */
|
|
@@ -1887,7 +1900,14 @@ export function emitSwiftPackageManifest(args: {
|
|
|
1887
1900
|
/** the development region SwiftPM needs as defaultLocalization beside localized resources. */
|
|
1888
1901
|
defaultLocalization?: string
|
|
1889
1902
|
}): string {
|
|
1890
|
-
const {
|
|
1903
|
+
const {
|
|
1904
|
+
targets,
|
|
1905
|
+
siblings,
|
|
1906
|
+
moduleRoot,
|
|
1907
|
+
oneModule,
|
|
1908
|
+
gameProducts = [],
|
|
1909
|
+
distribution,
|
|
1910
|
+
} = args
|
|
1891
1911
|
const prebuilt = args.prebuilt
|
|
1892
1912
|
// a distribution and a separately built framework package both link objects
|
|
1893
1913
|
// instead of compiling the framework sources into this package's scratch.
|
|
@@ -1934,9 +1954,8 @@ export function emitSwiftPackageManifest(args: {
|
|
|
1934
1954
|
for (const name of [...siblings.values()].flat())
|
|
1935
1955
|
if (OPTIONAL_FRAMEWORKS.has(name) && !targets.some((target) => target.name === name))
|
|
1936
1956
|
reach(name)
|
|
1937
|
-
|
|
1938
|
-
for (const name of [
|
|
1939
|
-
}
|
|
1957
|
+
for (const product of gameProducts)
|
|
1958
|
+
for (const name of GAME_REACH[product] ?? []) reach(name)
|
|
1940
1959
|
// the distribution builder names each object lib/<target>-<file>.o.
|
|
1941
1960
|
// preserve link order; only reached framework registrants link eagerly.
|
|
1942
1961
|
// the list is named by its content: SwiftPM relinks when the linker
|
|
@@ -1972,7 +1991,7 @@ export function emitSwiftPackageManifest(args: {
|
|
|
1972
1991
|
const objects = available.filter(
|
|
1973
1992
|
(object) =>
|
|
1974
1993
|
!targets.some((target) => object.startsWith(`lib/${target.name}-`)) &&
|
|
1975
|
-
[...OPTIONAL_FRAMEWORKS,
|
|
1994
|
+
[...OPTIONAL_FRAMEWORKS, ...GAME_FAMILY, 'PeachBox2D'].every(
|
|
1976
1995
|
(name) => !object.startsWith(`lib/${name}-`) || reachedFrameworks.has(name),
|
|
1977
1996
|
),
|
|
1978
1997
|
)
|
|
@@ -2248,7 +2267,7 @@ export function emitSwiftPackageManifest(args: {
|
|
|
2248
2267
|
: [
|
|
2249
2268
|
'.product(name: "SwiftUI", package: "sootsim-swift")',
|
|
2250
2269
|
...(oneModule ? [ONE_PRODUCT] : []),
|
|
2251
|
-
...(
|
|
2270
|
+
...gameProducts.map(gameProduct),
|
|
2252
2271
|
]),
|
|
2253
2272
|
...(siblings.get(target.name) ?? []).flatMap((dependency) =>
|
|
2254
2273
|
clang &&
|
|
@@ -2302,7 +2321,7 @@ export function emitSwiftPackageManifest(args: {
|
|
|
2302
2321
|
.unsafeFlags(${flagArray(`[
|
|
2303
2322
|
${REACTOR_FLAG}${executableLinkFlags(args.root)} "-Xlinker", "--export-if-defined=__main_argc_argv"${library},
|
|
2304
2323
|
]`)}),
|
|
2305
|
-
.unsafeFlags(["-Xlinker", "--strip-all"], .when(configuration: .release)),
|
|
2324
|
+
${KEEP_WASM_NAMES ? '' : '.unsafeFlags(["-Xlinker", "--strip-all"], .when(configuration: .release)),'}
|
|
2306
2325
|
]`
|
|
2307
2326
|
: librarySettings.length > 0
|
|
2308
2327
|
? `,
|
|
@@ -3019,8 +3038,6 @@ const FOUNDATION_OVERLAY_GLOBALS = new Set([
|
|
|
3019
3038
|
'NSURLProtectionSpaceHTTPS',
|
|
3020
3039
|
'NSURLProtectionSpaceHTTPSProxy',
|
|
3021
3040
|
'NSURLProtectionSpaceSOCKSProxy',
|
|
3022
|
-
'NSURLSessionDownloadTaskResumeData',
|
|
3023
|
-
'NSURLSessionTransferSizeUnknown',
|
|
3024
3041
|
'NSURLSessionUploadTaskResumeData',
|
|
3025
3042
|
'NSUTF16BigEndianStringEncoding',
|
|
3026
3043
|
'NSUTF16LittleEndianStringEncoding',
|
|
@@ -5320,10 +5337,10 @@ export function writeSwiftProject(
|
|
|
5320
5337
|
const moduleName = swiftTargetModuleName(name)
|
|
5321
5338
|
const originalMap = originalMaps.get(moduleName)
|
|
5322
5339
|
const stagedMap = sourceModuleMaps.get(moduleName)
|
|
5323
|
-
const originalPath =
|
|
5324
|
-
? path.join(layout.root, originalMap)
|
|
5325
|
-
|
|
5326
|
-
|
|
5340
|
+
const originalPath =
|
|
5341
|
+
stagedMap?.path ?? (originalMap ? path.join(layout.root, originalMap) : undefined)
|
|
5342
|
+
const originalContents =
|
|
5343
|
+
stagedMap?.contents ?? (originalMap && input.files[originalMap])
|
|
5327
5344
|
const originalTokens = originalContents ? moduleMapTokens(originalContents) : []
|
|
5328
5345
|
if (
|
|
5329
5346
|
originalPath &&
|
|
@@ -5652,13 +5669,17 @@ export function writeSwiftProject(
|
|
|
5652
5669
|
.replace(product, `${product}\n ${ONE_PRODUCT},`)
|
|
5653
5670
|
.replace(flags, `${flags}${ONE_IMPLICIT_FLAGS}`)
|
|
5654
5671
|
}
|
|
5655
|
-
|
|
5672
|
+
const games = gameProducts(input.files)
|
|
5673
|
+
if (games.length > 0) {
|
|
5656
5674
|
const anchor = '.product(name: "CoreLocation", package: "sootsim-swift"),'
|
|
5657
5675
|
if (!manifest.includes(anchor))
|
|
5658
5676
|
throw new Error(
|
|
5659
|
-
'scripts/contrast-app-Package.swift lost the line the
|
|
5677
|
+
'scripts/contrast-app-Package.swift lost the line the game products link at',
|
|
5660
5678
|
)
|
|
5661
|
-
manifest = manifest.replace(
|
|
5679
|
+
manifest = manifest.replace(
|
|
5680
|
+
anchor,
|
|
5681
|
+
[anchor, ...games.map((name) => `${gameProduct(name)},`)].join('\n '),
|
|
5682
|
+
)
|
|
5662
5683
|
}
|
|
5663
5684
|
files.set(path.join(layout.root, 'Package.swift'), manifest + moduleFiles)
|
|
5664
5685
|
} else {
|
|
@@ -5828,7 +5849,7 @@ export function writeSwiftProject(
|
|
|
5828
5849
|
siblings: moduleSiblings,
|
|
5829
5850
|
moduleRoot,
|
|
5830
5851
|
oneModule: input.oneModule,
|
|
5831
|
-
|
|
5852
|
+
gameProducts: gameProducts(input.files),
|
|
5832
5853
|
distribution: distribution ?? undefined,
|
|
5833
5854
|
moduleExcludes,
|
|
5834
5855
|
deferDarwinOverlay:
|
|
@@ -6352,14 +6373,9 @@ function linkedFrameworkTargets(
|
|
|
6352
6373
|
))
|
|
6353
6374
|
reach(match[1]!)
|
|
6354
6375
|
}
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
const wanted = [
|
|
6358
|
-
'PeachApp',
|
|
6359
|
-
...(input.oneModule ? ['One'] : []),
|
|
6360
|
-
...(importsSpriteKit(input.files) ? ['SpriteKit'] : []),
|
|
6361
|
-
...reached,
|
|
6362
|
-
]
|
|
6376
|
+
const games = gameProducts(input.files)
|
|
6377
|
+
for (const product of games) for (const name of GAME_REACH[product] ?? []) reach(name)
|
|
6378
|
+
const wanted = ['PeachApp', ...(input.oneModule ? ['One'] : []), ...games, ...reached]
|
|
6363
6379
|
const linked = [...new Set(wanted)].filter((name) => libraries.has(name))
|
|
6364
6380
|
if (!linked.includes('PeachApp'))
|
|
6365
6381
|
throw new Error('swift-compile: the framework package declares no PeachApp library')
|
|
@@ -139,20 +139,22 @@ export function planAssetSymbols(
|
|
|
139
139
|
// xcode 27.1 resolves an unset TARGETED_DEVICE_FAMILY to 1 for an iOS target
|
|
140
140
|
// (xcodebuild -showBuildSettings on an application target that never sets it)
|
|
141
141
|
const families = (settings.TARGETED_DEVICE_FAMILY || '1').split(',').sort().join(',')
|
|
142
|
-
// families 6 and 7 change neither the devices nor symbols for the iOS
|
|
142
|
+
// families 3, 6 and 7 change neither the devices nor symbols for the iOS
|
|
143
|
+
// slice (3: actool output identical for 1,3,7 and 1,7, xcode 27.1)
|
|
143
144
|
const devices: AssetDevice[] | undefined = (
|
|
144
145
|
{
|
|
145
146
|
'1': ['iphone'],
|
|
146
147
|
'2': ['ipad'],
|
|
147
148
|
'1,2': ['iphone', 'ipad'],
|
|
148
149
|
'1,7': ['iphone'],
|
|
150
|
+
'1,3,7': ['iphone'],
|
|
149
151
|
'1,2,6': ['iphone', 'ipad'],
|
|
150
152
|
'1,2,7': ['iphone', 'ipad'],
|
|
151
153
|
} satisfies Record<string, AssetDevice[]>
|
|
152
154
|
)[families]
|
|
153
155
|
if (!devices)
|
|
154
156
|
return unknown(
|
|
155
|
-
`Generated asset symbols are proven only for TARGETED_DEVICE_FAMILY 1, 2, 1,2, 1,7, 1,2,6 or 1,2,7; this target resolves to "${families}".`,
|
|
157
|
+
`Generated asset symbols are proven only for TARGETED_DEVICE_FAMILY 1, 2, 1,2, 1,7, 1,3,7, 1,2,6 or 1,2,7; this target resolves to "${families}".`,
|
|
156
158
|
)
|
|
157
159
|
const plan: SwiftAssetPlan = {
|
|
158
160
|
catalogs: catalogs.map((path) => ({
|
package/src/swift-host.ts
CHANGED
|
@@ -1773,6 +1773,33 @@ export function sourceFiles(root: string): string[] {
|
|
|
1773
1773
|
}
|
|
1774
1774
|
|
|
1775
1775
|
// project files and the xcconfigs their settings read are build inputs, never resources
|
|
1776
|
+
// the app target's resources Xcode copies to the bundle root: a folder
|
|
1777
|
+
// reference keeps its tree and a catalog compiles, so neither is flat
|
|
1778
|
+
function xcodeFlatResources(
|
|
1779
|
+
app: Pick<XcodeModuleTarget, 'resources' | 'resourceNames'> | undefined,
|
|
1780
|
+
): ReadonlySet<string> {
|
|
1781
|
+
if (!app) return new Set()
|
|
1782
|
+
return new Set(
|
|
1783
|
+
app.resources.filter(
|
|
1784
|
+
(file) =>
|
|
1785
|
+
!Object.hasOwn(app.resourceNames, file) &&
|
|
1786
|
+
!file.split(path.sep).some((segment) => segment.endsWith('.xcassets')),
|
|
1787
|
+
),
|
|
1788
|
+
)
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
// an xcode target declares its resources independently of their source
|
|
1792
|
+
// directory; the bundle file and image enumerators use Resources as their root marker.
|
|
1793
|
+
function bundleResourcePath(
|
|
1794
|
+
flatResources: ReadonlySet<string>,
|
|
1795
|
+
disk: string,
|
|
1796
|
+
file: string,
|
|
1797
|
+
): string {
|
|
1798
|
+
return flatResources.has(disk) && !file.split('/').includes('Resources')
|
|
1799
|
+
? `Resources/${file}`
|
|
1800
|
+
: file
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1776
1803
|
function resourceCandidateFiles(root: string): string[] {
|
|
1777
1804
|
return packageFiles(root, false).filter(
|
|
1778
1805
|
(file) =>
|
|
@@ -1941,12 +1968,24 @@ export function buildSwiftResourceTable(
|
|
|
1941
1968
|
const disk = byRelative.get(file)
|
|
1942
1969
|
return !disk || !layout?.targetDirs.get(disk)?.startsWith(`${XCODE_FRAMEWORKS_DIR}/`)
|
|
1943
1970
|
})
|
|
1944
|
-
const
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1971
|
+
const flatResources = xcodeFlatResources(layout?.app)
|
|
1972
|
+
const bundled = new Map(
|
|
1973
|
+
relative.map((file) => [
|
|
1974
|
+
bundleResourcePath(flatResources, byRelative.get(file)!, file),
|
|
1975
|
+
file,
|
|
1976
|
+
]),
|
|
1977
|
+
)
|
|
1978
|
+
const found = enumerateBundleImages(
|
|
1979
|
+
[...bundled.keys()],
|
|
1980
|
+
(file) => readContents(bundled.get(file) ?? file),
|
|
1981
|
+
(marked) => {
|
|
1982
|
+
const file = bundled.get(marked) ?? marked
|
|
1983
|
+
const disk = byRelative.get(file)
|
|
1984
|
+
if (!disk) throw new Error(`image source ${file} has no input file`)
|
|
1985
|
+
const key = layout ? xcodeInputKey(layout, disk, file) : file
|
|
1986
|
+
return swiftResourceBundleForFile(bundles, key)?.path ?? '.'
|
|
1987
|
+
},
|
|
1988
|
+
).map((image) => ({ ...image, file: bundled.get(image.file) ?? image.file }))
|
|
1950
1989
|
const colors = enumerateBundleColors(catalogFiles, readContents)
|
|
1951
1990
|
const symbols = enumerateBundleSymbols(catalogFiles, readContents, (file) => {
|
|
1952
1991
|
const disk = byRelative.get(file)
|
|
@@ -2180,20 +2219,20 @@ export function buildSwiftBundleFiles(
|
|
|
2180
2219
|
}
|
|
2181
2220
|
}
|
|
2182
2221
|
const groups = new Map<string, Map<string, string>>()
|
|
2183
|
-
const
|
|
2222
|
+
const flatResources = xcodeFlatResources(xcode?.app)
|
|
2184
2223
|
for (const file of relative) {
|
|
2185
2224
|
const disk = path.join(packagePath, ...file.split('/'))
|
|
2225
|
+
const folderName = xcode?.app.resourceNames[disk]
|
|
2226
|
+
if (folderName) {
|
|
2227
|
+
watched.push(file)
|
|
2228
|
+
addFile(folderName, readPackageFile(packagePath, disk, limits.resourceBytes))
|
|
2229
|
+
continue
|
|
2230
|
+
}
|
|
2186
2231
|
const key = xcode ? xcodeInputKey(xcode, disk, file) : file
|
|
2187
2232
|
const bundle = swiftResourceBundleForFile(bundles, key)
|
|
2188
2233
|
const bundlePath = bundle?.path ?? '.'
|
|
2189
2234
|
const group = groups.get(bundlePath) ?? new Map<string, string>()
|
|
2190
|
-
|
|
2191
|
-
// directory; the bundle enumerator uses Resources as its root marker.
|
|
2192
|
-
const resource =
|
|
2193
|
-
appResources.has(disk) && !file.split('/').includes('Resources')
|
|
2194
|
-
? `Resources/${file}`
|
|
2195
|
-
: file
|
|
2196
|
-
group.set(resource, disk)
|
|
2235
|
+
group.set(bundleResourcePath(flatResources, disk, file), disk)
|
|
2197
2236
|
groups.set(bundlePath, group)
|
|
2198
2237
|
}
|
|
2199
2238
|
for (const bundle of bundles) {
|
package/src/swift-root-lock.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { readProcessTable, type ProcessRow } from './process-table.ts'
|
|
|
20
20
|
import type { Readable, Writable } from 'node:stream'
|
|
21
21
|
|
|
22
22
|
const POLL_MS = 250
|
|
23
|
+
const INSPECT_ATTEMPTS = 5
|
|
23
24
|
// ps derives a start time from the boot time, which drifts by a second or two
|
|
24
25
|
// on linux; a reused pid starts later than this.
|
|
25
26
|
const START_TOLERANCE_MS = 2_000
|
|
@@ -53,6 +54,24 @@ function processTable(pid?: number): ProcessRow[] {
|
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
// a loaded host can stall ps past its deadline once without the table being
|
|
58
|
+
// broken, and the lock only ever waits on that answer, so lock acquisition
|
|
59
|
+
// asks again before giving up.
|
|
60
|
+
async function processTableWithRetry(
|
|
61
|
+
pid: number | undefined,
|
|
62
|
+
signal: AbortSignal | undefined,
|
|
63
|
+
): Promise<ProcessRow[]> {
|
|
64
|
+
for (let attempt = 1; ; attempt++) {
|
|
65
|
+
try {
|
|
66
|
+
return processTable(pid)
|
|
67
|
+
} catch (error) {
|
|
68
|
+
if (attempt >= INSPECT_ATTEMPTS) throw error
|
|
69
|
+
await pause(POLL_MS * attempt, signal)
|
|
70
|
+
signal?.throwIfAborted()
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
56
75
|
function sameProcess(row: ProcessRow, identity: Identity): boolean {
|
|
57
76
|
return (
|
|
58
77
|
row.pid === identity.pid && Math.abs(row.start - identity.start) <= START_TOLERANCE_MS
|
|
@@ -204,7 +223,11 @@ export async function withSwiftRootLock<T>(
|
|
|
204
223
|
)
|
|
205
224
|
mkdirSync(directory, { recursive: true })
|
|
206
225
|
const token = randomUUID()
|
|
207
|
-
const
|
|
226
|
+
const ownRow = (await processTableWithRetry(process.pid, signal)).find(
|
|
227
|
+
(candidate) => candidate.pid === process.pid && !candidate.zombie,
|
|
228
|
+
)
|
|
229
|
+
if (!ownRow) throw new Error(`swift root lock cannot find process ${process.pid}`)
|
|
230
|
+
const owner = JSON.stringify({ pid: process.pid, start: ownRow.start })
|
|
208
231
|
const pending = path.join(directory, `pending.${token}`)
|
|
209
232
|
let held = 0
|
|
210
233
|
for (;;) {
|
|
@@ -213,15 +236,15 @@ export async function withSwiftRootLock<T>(
|
|
|
213
236
|
const current = newest > 0 ? readGeneration(directory, newest) : null
|
|
214
237
|
if (newest > 0 && !current) continue
|
|
215
238
|
if (current && !current.released) {
|
|
216
|
-
const
|
|
217
|
-
if (
|
|
239
|
+
const holder = await processTableWithRetry(current.holder.pid, signal)
|
|
240
|
+
if (holder.some((row) => !row.zombie && sameProcess(row, current.holder))) {
|
|
218
241
|
if (!wait) return undefined
|
|
219
242
|
await pause(POLL_MS, signal)
|
|
220
243
|
continue
|
|
221
244
|
}
|
|
222
245
|
const children = readChildren(directory, newest)
|
|
223
246
|
if (children.length) {
|
|
224
|
-
const table =
|
|
247
|
+
const table = await processTableWithRetry(undefined, signal)
|
|
225
248
|
if (children.some((child) => groupAlive(table, child))) {
|
|
226
249
|
if (!wait) return undefined
|
|
227
250
|
await pause(POLL_MS, signal)
|
package/src/wasm-optimize.ts
CHANGED
|
@@ -22,8 +22,11 @@ const FEATURES = [
|
|
|
22
22
|
'sign-ext',
|
|
23
23
|
]
|
|
24
24
|
|
|
25
|
+
// PEACH_SWIFT_NAMES=1 keeps the name section so a trap stack names its functions.
|
|
26
|
+
export const KEEP_WASM_NAMES = process.env.PEACH_SWIFT_NAMES === '1'
|
|
27
|
+
|
|
25
28
|
/** feeds the artifact hash: a new optimizer or flag set is a new artifact. */
|
|
26
|
-
export const WASM_OPTIMIZE_IDENTITY = `wasm-opt ${BINARYEN_VERSION} -Oz ${FEATURES.join(',')}`
|
|
29
|
+
export const WASM_OPTIMIZE_IDENTITY = `wasm-opt ${BINARYEN_VERSION} -Oz ${FEATURES.join(',')}${KEEP_WASM_NAMES ? ' -g' : ''}`
|
|
27
30
|
|
|
28
31
|
// the sha256 of each Binaryen release archive wasm-opt resolves for its own platform
|
|
29
32
|
// keys; update both when the wasm-opt package moves BINARYEN_VERSION.
|
|
@@ -73,6 +76,7 @@ async function runWasmOpt(
|
|
|
73
76
|
const result = await wasmOpt(
|
|
74
77
|
[
|
|
75
78
|
'-Oz',
|
|
79
|
+
...(KEEP_WASM_NAMES ? ['-g'] : []),
|
|
76
80
|
...skipPasses.flatMap((pass) => ['--skip-pass', pass]),
|
|
77
81
|
...FEATURES.map((feature) => `--enable-${feature}`),
|
|
78
82
|
file,
|
package/src/xcode-project.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { createHash } from 'node:crypto'
|
|
|
12
12
|
import fs from 'node:fs'
|
|
13
13
|
import path from 'node:path'
|
|
14
14
|
import plist from 'plist'
|
|
15
|
+
import iosSdk from './ios-sdk-frameworks.json' with { type: 'json' }
|
|
15
16
|
import { isRecord } from './is-record'
|
|
16
17
|
import { readKeychainAccessGroups } from './native-app-identity'
|
|
17
18
|
import { preparedXcodePhase } from './prepared-xcode-phases'
|
|
@@ -93,6 +94,8 @@ export interface XcodeModuleTarget {
|
|
|
93
94
|
allSources: string[]
|
|
94
95
|
/** absolute paths of every resource file the target copies, bundles expanded. */
|
|
95
96
|
resources: string[]
|
|
97
|
+
/** a folder reference's files by their path in the bundle, which keeps the folder. */
|
|
98
|
+
resourceNames: Record<string, string>
|
|
96
99
|
/** files the target's shell phases copy with a plain `cp`, resolved from the phase's own assignments and the build settings. */
|
|
97
100
|
copiedFiles: { from: string; to: string }[]
|
|
98
101
|
/** a shell phase of the target generates its INFOPLIST_FILE, which exists only once prepared. */
|
|
@@ -1032,6 +1035,7 @@ function targetMembers(
|
|
|
1032
1035
|
): {
|
|
1033
1036
|
sources: string[]
|
|
1034
1037
|
resources: string[]
|
|
1038
|
+
resourceNames: Record<string, string>
|
|
1035
1039
|
copiedFiles: { from: string; to: string }[]
|
|
1036
1040
|
generatedInfoPlist: boolean
|
|
1037
1041
|
allSources: string[]
|
|
@@ -1043,6 +1047,7 @@ function targetMembers(
|
|
|
1043
1047
|
const allSources = new Set<string>()
|
|
1044
1048
|
const headers = new Set<string>()
|
|
1045
1049
|
const resources = new Set<string>()
|
|
1050
|
+
const resourceNames: Record<string, string> = {}
|
|
1046
1051
|
const scriptForOutput = new Map<string, string>()
|
|
1047
1052
|
const copiedFiles: { from: string; to: string }[] = []
|
|
1048
1053
|
let generatedInfoPlist = false
|
|
@@ -1232,13 +1237,14 @@ function targetMembers(
|
|
|
1232
1237
|
xcodePathResolver(project.objects, project.projectDir, path.join, settings),
|
|
1233
1238
|
(file) => (isDirectory(file) ? walkFiles(file) : fs.existsSync(file) ? [file] : []),
|
|
1234
1239
|
path.join,
|
|
1235
|
-
(file, kind) => {
|
|
1240
|
+
(file, kind, bundleName) => {
|
|
1236
1241
|
if (kind === 'header') {
|
|
1237
1242
|
headers.add(file)
|
|
1238
1243
|
return
|
|
1239
1244
|
}
|
|
1240
1245
|
if (kind === 'resource') {
|
|
1241
1246
|
resources.add(file)
|
|
1247
|
+
if (bundleName) resourceNames[file] = bundleName
|
|
1242
1248
|
return
|
|
1243
1249
|
}
|
|
1244
1250
|
if (!fs.existsSync(file)) {
|
|
@@ -1376,6 +1382,7 @@ function targetMembers(
|
|
|
1376
1382
|
return {
|
|
1377
1383
|
sources: [...sources].sort(),
|
|
1378
1384
|
resources: [...resources].sort(),
|
|
1385
|
+
resourceNames,
|
|
1379
1386
|
copiedFiles,
|
|
1380
1387
|
generatedInfoPlist,
|
|
1381
1388
|
allSources: [...allSources].sort(),
|
|
@@ -3117,10 +3124,17 @@ export function readXcodeAppTarget(
|
|
|
3117
3124
|
)
|
|
3118
3125
|
continue
|
|
3119
3126
|
const resolved = fileId ? project.resolvePath(fileId) : null
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3127
|
+
// a reference to a file that is not there still links -framework
|
|
3128
|
+
// <name> (xcode 27.1, iOS simulator): the sdk's framework answers it,
|
|
3129
|
+
// and a name the sdk lacks fails the link
|
|
3130
|
+
if (fileId && resolved !== null && !fs.existsSync(resolved)) {
|
|
3131
|
+
if (!iosSdk.frameworks.includes(name))
|
|
3132
|
+
throw new Error(
|
|
3133
|
+
`${str(target.name) ?? 'target'}: linked framework ${name} (${fileId}) points at missing ${resolved} in ${project.projectFile}`,
|
|
3134
|
+
)
|
|
3135
|
+
sdkFrameworks.add(name)
|
|
3136
|
+
continue
|
|
3137
|
+
}
|
|
3124
3138
|
if (!binaryFrameworks.get(name)) binaryFrameworks.set(name, resolved)
|
|
3125
3139
|
}
|
|
3126
3140
|
}
|
|
@@ -3234,6 +3248,13 @@ export function readXcodeAppTarget(
|
|
|
3234
3248
|
source.kind === 'local' && fs.existsSync(source.dir) ? [source.dir] : [],
|
|
3235
3249
|
),
|
|
3236
3250
|
...new Set(podSourceDirs),
|
|
3251
|
+
// a target's own files may sit beside the project, such as an asset
|
|
3252
|
+
// catalog one folder up, and xcode builds them from there
|
|
3253
|
+
...new Set(
|
|
3254
|
+
[...module.allSources, ...module.resources]
|
|
3255
|
+
.filter((file) => fs.existsSync(file))
|
|
3256
|
+
.map((file) => path.dirname(file)),
|
|
3257
|
+
),
|
|
3237
3258
|
].map((dir) => fs.realpathSync.native(dir)),
|
|
3238
3259
|
),
|
|
3239
3260
|
projectFiles: [
|