@auraindustry/aurajs 0.1.1 → 0.1.5
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 +7 -0
- package/benchmarks/perf-thresholds.json +27 -0
- package/package.json +6 -1
- package/src/ai-guidance.mjs +302 -0
- package/src/asset-pack.mjs +2 -1
- package/src/authored-project.mjs +498 -2
- package/src/authored-runtime.mjs +14 -0
- package/src/bin-integrity.mjs +33 -26
- package/src/build-contract/capabilities.mjs +87 -1
- package/src/build-contract/constants.mjs +1 -0
- package/src/build-contract.mjs +2 -0
- package/src/bundler.mjs +143 -13
- package/src/cli.mjs +681 -13
- package/src/commands/packs.mjs +741 -0
- package/src/commands/project-authoring.mjs +128 -1
- package/src/conformance/cases/app-and-ui-runtime-cases.mjs +1 -2
- package/src/conformance/cases/core-runtime-cases.mjs +6 -2
- package/src/conformance/cases/scene3d-and-media-cases.mjs +238 -0
- package/src/conformance/cases/systems-and-gameplay-cases.mjs +1126 -10
- package/src/conformance-mobile.mjs +166 -0
- package/src/conformance.mjs +89 -30
- package/src/evidence-bundle.mjs +242 -0
- package/src/external-package-surface.mjs +1 -1
- package/src/headless-test/runtime-coordinator.mjs +186 -33
- package/src/headless-test.mjs +2 -0
- package/src/helpers/2d/index.mjs +183 -0
- package/src/helpers/index.mjs +26 -0
- package/src/helpers/starter-utils/adventure-objectives.js +102 -0
- package/src/helpers/starter-utils/adventure-world-2d.js +221 -0
- package/src/helpers/starter-utils/animation-2d.js +337 -0
- package/src/helpers/starter-utils/animation-packaging-2d.js +203 -0
- package/src/helpers/starter-utils/atlas-assets-2d.js +111 -0
- package/src/helpers/starter-utils/autoplay-debug-2d.js +215 -0
- package/src/helpers/starter-utils/avatar-3d.js +404 -0
- package/src/helpers/starter-utils/combat-feedback-2d.js +320 -0
- package/src/helpers/starter-utils/combat-runtime-2d.js +290 -0
- package/src/helpers/starter-utils/core.js +150 -0
- package/src/helpers/starter-utils/dialogue-2d.js +351 -0
- package/src/helpers/starter-utils/enemy-archetypes-2d.js +68 -0
- package/src/helpers/starter-utils/index.js +26 -0
- package/src/helpers/starter-utils/inventory-2d.js +268 -0
- package/src/helpers/starter-utils/journal-2d.js +267 -0
- package/src/helpers/starter-utils/platformer-3d.js +132 -0
- package/src/helpers/starter-utils/scene-audio-2d.js +236 -0
- package/src/helpers/starter-utils/streamed-world-2d.js +378 -0
- package/src/helpers/starter-utils/tilemap-nav-2d.js +499 -0
- package/src/helpers/starter-utils/tilemap-world-2d.js +205 -0
- package/src/helpers/starter-utils/triggers.js +662 -0
- package/src/helpers/starter-utils/tween-2d.js +615 -0
- package/src/helpers/starter-utils/wave-director.js +101 -0
- package/src/helpers/starter-utils/world-compositor-2d.js +253 -0
- package/src/helpers/starter-utils/world-persistence-2d.js +180 -0
- package/src/mobile/android/build.mjs +606 -0
- package/src/mobile/android/host-artifact.mjs +280 -0
- package/src/mobile/ios/build.mjs +1323 -0
- package/src/mobile/ios/host-artifact.mjs +819 -0
- package/src/mobile/shared/capabilities.mjs +174 -0
- package/src/package-integrity.mjs +18 -4
- package/src/packs/catalog.mjs +259 -0
- package/src/perf-benchmark-runner.mjs +17 -12
- package/src/perf-benchmark.mjs +408 -4
- package/src/publish-command.mjs +434 -17
- package/src/publish-validation.mjs +22 -11
- package/src/replay-runtime.mjs +257 -0
- package/src/scaffold/config.mjs +2 -0
- package/src/scaffold/fs.mjs +8 -1
- package/src/scaffold/project-docs.mjs +101 -41
- package/src/scaffold.mjs +4 -0
- package/src/session-runtime.mjs +4 -3
- package/src/web-conformance.mjs +0 -36
- package/templates/create/2d/src/runtime/app.js +4 -0
- package/templates/create/2d-adventure/config/gameplay/adventure.config.js +9 -6
- package/templates/create/2d-adventure/content/gameplay/dialogue.js +85 -0
- package/templates/create/2d-adventure/content/gameplay/world.js +32 -36
- package/templates/create/2d-adventure/content/gameplay/world.tilemap.json +273 -0
- package/templates/create/2d-adventure/docs/design/loop.md +4 -3
- package/templates/create/2d-adventure/prefabs/relic.prefab.js +10 -10
- package/templates/create/2d-adventure/prefabs/world.prefab.js +127 -74
- package/templates/create/2d-adventure/scenes/gameplay.scene.js +603 -112
- package/templates/create/2d-adventure/src/runtime/capabilities.js +16 -0
- package/templates/create/2d-adventure/ui/hud.screen.js +187 -4
- package/templates/create/2d-adventure/ui/journal.screen.js +183 -0
- package/templates/create/2d-survivor/src/runtime/app.js +4 -0
- package/templates/create/3d/scenes/gameplay.scene.js +30 -3
- package/templates/create/3d/src/runtime/app.js +4 -0
- package/templates/create/3d/src/runtime/capabilities.js +5 -0
- package/templates/create/3d/src/runtime/materials.js +10 -0
- package/templates/create/3d-adventure/scenes/gameplay.scene.js +30 -3
- package/templates/create/3d-adventure/src/runtime/capabilities.js +5 -0
- package/templates/create/3d-adventure/src/runtime/materials.js +11 -0
- package/templates/create/3d-collectathon/scenes/gameplay.scene.js +30 -3
- package/templates/create/3d-collectathon/src/runtime/app.js +4 -0
- package/templates/create/3d-collectathon/src/runtime/capabilities.js +5 -0
- package/templates/create/3d-collectathon/src/runtime/materials.js +10 -0
- package/templates/create/blank/assets/splash/aurajs-gg-wordmark.webp +0 -0
- package/templates/create/blank/assets/splash/bg.webp +0 -0
- package/templates/create/blank/assets/splash/boot-loop.wav +0 -0
- package/templates/create/blank/assets/splash/boot-sting.wav +0 -0
- package/templates/create/blank/assets/splash/logo-mascot-sheet.webp +0 -0
- package/templates/create/blank/assets/splash/logoholo.webp +0 -0
- package/templates/create/blank/src/main.js +5 -1
- package/templates/create/blank/src/runtime/splash.js +305 -0
- package/templates/create/local-multiplayer/scenes/gameplay.scene.js +186 -12
- package/templates/create/local-multiplayer/src/runtime/capabilities.js +8 -1
- package/templates/create/shared/assets/splash/aurajs-gg-wordmark.webp +0 -0
- package/templates/create/shared/assets/splash/bg.webp +0 -0
- package/templates/create/shared/assets/splash/boot-loop.wav +0 -0
- package/templates/create/shared/assets/splash/boot-sting.wav +0 -0
- package/templates/create/shared/assets/splash/logo-mascot-sheet.webp +0 -0
- package/templates/create/shared/assets/splash/logoholo.webp +0 -0
- package/templates/create/shared/src/runtime/splash.js +305 -0
- package/templates/create/shared/src/runtime/ui-forms.js +552 -0
- package/templates/create/shared/src/starter-utils/adventure-world-2d.js +221 -0
- package/templates/create/shared/src/starter-utils/animation-packaging-2d.js +203 -0
- package/templates/create/shared/src/starter-utils/atlas-assets-2d.js +111 -0
- package/templates/create/shared/src/starter-utils/autoplay-debug-2d.js +215 -0
- package/templates/create/shared/src/starter-utils/combat-runtime-2d.js +290 -0
- package/templates/create/shared/src/starter-utils/dialogue-2d.js +351 -0
- package/templates/create/shared/src/starter-utils/index.js +15 -1
- package/templates/create/shared/src/starter-utils/inventory-2d.js +268 -0
- package/templates/create/shared/src/starter-utils/journal-2d.js +267 -0
- package/templates/create/shared/src/starter-utils/scene-audio-2d.js +236 -0
- package/templates/create/shared/src/starter-utils/streamed-world-2d.js +378 -0
- package/templates/create/shared/src/starter-utils/tilemap-nav-2d.js +499 -0
- package/templates/create/shared/src/starter-utils/tilemap-world-2d.js +205 -0
- package/templates/create/shared/src/starter-utils/world-compositor-2d.js +253 -0
- package/templates/create/shared/src/starter-utils/world-persistence-2d.js +180 -0
- package/templates/create/video-cutscene/src/runtime/app.js +4 -0
- package/templates/create-bin/play.js +148 -7
- package/templates/skills/auramaxx/SKILL.md +46 -0
- package/templates/skills/auramaxx/project-requirements.md +68 -0
- package/templates/skills/auramaxx/starter-recipes.md +104 -0
- package/templates/skills/auramaxx/validation-checklist.md +49 -0
- package/templates/starter/assets/splash/aurajs-gg-wordmark.webp +0 -0
- package/templates/starter/assets/splash/bg.webp +0 -0
- package/templates/starter/assets/splash/boot-loop.wav +0 -0
- package/templates/starter/assets/splash/boot-sting.wav +0 -0
- package/templates/starter/assets/splash/logo-mascot-sheet.webp +0 -0
- package/templates/starter/assets/splash/logoholo.webp +0 -0
- package/templates/starter/src/main.js +4 -0
- package/templates/starter/src/runtime/splash.js +305 -0
- package/templates/skills/aurajs/SKILL.md +0 -96
- package/templates/skills/aurajs/api-contract-3d.md +0 -7
- package/templates/skills/aurajs/api-contract.md +0 -7
|
@@ -0,0 +1,819 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import {
|
|
3
|
+
cpSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
mkdtempSync,
|
|
7
|
+
rmSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
} from 'node:fs';
|
|
10
|
+
import { tmpdir } from 'node:os';
|
|
11
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
|
|
14
|
+
export const IOS_HOST_XCFRAMEWORK_NAME = 'aurajs-host.xcframework';
|
|
15
|
+
|
|
16
|
+
const IOS_HOST_FRAMEWORK_DIRECTORY = 'aurajs-host.framework';
|
|
17
|
+
const IOS_HOST_FRAMEWORK_BINARY = 'aurajs-host';
|
|
18
|
+
const IOS_HOST_DYNAMIC_LIBRARY = 'libaurajs_host.dylib';
|
|
19
|
+
const IOS_HOST_STATIC_LIBRARY = 'libaurajs_host.a';
|
|
20
|
+
const IOS_DEVICE_LIBRARY_IDENTIFIER = 'ios-arm64';
|
|
21
|
+
const IOS_SIMULATOR_LIBRARY_IDENTIFIER = 'ios-arm64-simulator';
|
|
22
|
+
const IOS_DEVICE_RUST_TARGET = 'aarch64-apple-ios';
|
|
23
|
+
const IOS_SIMULATOR_RUST_TARGET = 'aarch64-apple-ios-sim';
|
|
24
|
+
const REQUIRED_HOST_SYMBOLS = [
|
|
25
|
+
'aurajs_host_bootstrap_v1',
|
|
26
|
+
'aurajs_host_run_v1',
|
|
27
|
+
];
|
|
28
|
+
const DEFAULT_DIST_ROOT = resolve(
|
|
29
|
+
fileURLToPath(new URL('../../../../platform/dist/ios/', import.meta.url)),
|
|
30
|
+
);
|
|
31
|
+
const DEFAULT_RUST_HOST_ROOT = resolve(
|
|
32
|
+
fileURLToPath(new URL('../../../../rust-host/', import.meta.url)),
|
|
33
|
+
);
|
|
34
|
+
const DEFAULT_IOS_PATCHED_V8_ROOT = resolve(
|
|
35
|
+
fileURLToPath(new URL('./vendor/v8-0.92.0/', import.meta.url)),
|
|
36
|
+
);
|
|
37
|
+
const DEFAULT_IOS_HOST_DEPLOYMENT_TARGET = '15.0';
|
|
38
|
+
|
|
39
|
+
export function produceIosHostArtifact(options = {}) {
|
|
40
|
+
const environment = options.environment || process.env;
|
|
41
|
+
const spawnSyncImpl = options.spawnSyncImpl || spawnSync;
|
|
42
|
+
const distRoot = resolve(options.distRoot || DEFAULT_DIST_ROOT);
|
|
43
|
+
const rustHostRoot = resolve(options.rustHostRoot || DEFAULT_RUST_HOST_ROOT);
|
|
44
|
+
const cargoManifestPath = resolve(options.cargoManifestPath || join(rustHostRoot, 'Cargo.toml'));
|
|
45
|
+
const outputPath = join(distRoot, IOS_HOST_XCFRAMEWORK_NAME);
|
|
46
|
+
const profile = cleanString(options.profile) || 'release';
|
|
47
|
+
const xcodebuildExecutable = cleanString(options.xcodebuildExecutable)
|
|
48
|
+
|| cleanString(environment.AURA_IOS_XCODEBUILD)
|
|
49
|
+
|| 'xcodebuild';
|
|
50
|
+
const cargoExecutable = cleanString(options.cargoExecutable)
|
|
51
|
+
|| cleanString(environment.AURA_IOS_CARGO)
|
|
52
|
+
|| 'cargo';
|
|
53
|
+
const nmExecutable = cleanString(options.nmExecutable)
|
|
54
|
+
|| cleanString(environment.AURA_IOS_NM)
|
|
55
|
+
|| 'nm';
|
|
56
|
+
const rustupExecutable = cleanString(options.rustupExecutable)
|
|
57
|
+
|| cleanString(environment.AURA_IOS_RUSTUP)
|
|
58
|
+
|| 'rustup';
|
|
59
|
+
const patchedV8Path = cleanString(options.patchedV8Path)
|
|
60
|
+
|| cleanString(environment.AURA_IOS_PATCHED_V8_PATH)
|
|
61
|
+
|| DEFAULT_IOS_PATCHED_V8_ROOT;
|
|
62
|
+
|
|
63
|
+
mkdirSync(distRoot, { recursive: true });
|
|
64
|
+
|
|
65
|
+
const existingDist = validateXcframework(outputPath, {
|
|
66
|
+
spawnSyncImpl,
|
|
67
|
+
nmExecutable,
|
|
68
|
+
cwd: rustHostRoot,
|
|
69
|
+
environment,
|
|
70
|
+
});
|
|
71
|
+
if (existingDist.ok) {
|
|
72
|
+
return {
|
|
73
|
+
ok: true,
|
|
74
|
+
reasonCode: 'ios_host_artifact_existing_dist',
|
|
75
|
+
detail: 'Reused the validated iOS host artifact from the deterministic dist handoff.',
|
|
76
|
+
artifactPath: outputPath,
|
|
77
|
+
distRoot,
|
|
78
|
+
sourceKind: 'existing_dist',
|
|
79
|
+
checks: null,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const explicitInputPath = cleanString(options.hostFrameworkPath)
|
|
84
|
+
? resolve(options.hostFrameworkPath)
|
|
85
|
+
: null;
|
|
86
|
+
if (explicitInputPath) {
|
|
87
|
+
const validation = validateXcframework(explicitInputPath, {
|
|
88
|
+
spawnSyncImpl,
|
|
89
|
+
nmExecutable,
|
|
90
|
+
cwd: rustHostRoot,
|
|
91
|
+
environment,
|
|
92
|
+
});
|
|
93
|
+
if (!validation.ok) {
|
|
94
|
+
return {
|
|
95
|
+
ok: false,
|
|
96
|
+
reasonCode: 'ios_host_artifact_invalid_input',
|
|
97
|
+
detail: validation.detail,
|
|
98
|
+
artifactPath: outputPath,
|
|
99
|
+
distRoot,
|
|
100
|
+
sourceKind: 'explicit_input',
|
|
101
|
+
checks: null,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
replaceDirectory(explicitInputPath, outputPath);
|
|
106
|
+
return {
|
|
107
|
+
ok: true,
|
|
108
|
+
reasonCode: 'ios_host_artifact_copied',
|
|
109
|
+
detail: 'Copied the validated iOS host artifact into the deterministic dist handoff.',
|
|
110
|
+
artifactPath: outputPath,
|
|
111
|
+
distRoot,
|
|
112
|
+
sourceKind: 'explicit_input',
|
|
113
|
+
checks: null,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (String(environment.AURAJS_SKIP_XCODE_PROBE || '') === '1') {
|
|
118
|
+
return failedHostArtifactResult({
|
|
119
|
+
reasonCode: 'ios_xcode_probe_skipped',
|
|
120
|
+
detail: 'Xcode probing was skipped by AURAJS_SKIP_XCODE_PROBE=1.',
|
|
121
|
+
artifactPath: outputPath,
|
|
122
|
+
distRoot,
|
|
123
|
+
sourceKind: 'local_build',
|
|
124
|
+
checks: {
|
|
125
|
+
xcodebuild: {
|
|
126
|
+
available: false,
|
|
127
|
+
reasonCode: 'ios_xcode_probe_skipped',
|
|
128
|
+
detail: 'Xcode probing was skipped by AURAJS_SKIP_XCODE_PROBE=1.',
|
|
129
|
+
executablePath: xcodebuildExecutable,
|
|
130
|
+
},
|
|
131
|
+
cargo: null,
|
|
132
|
+
rustTargets: null,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const xcodebuild = probeExecutable({
|
|
138
|
+
spawnSyncImpl,
|
|
139
|
+
executablePath: xcodebuildExecutable,
|
|
140
|
+
args: ['-version'],
|
|
141
|
+
cwd: rustHostRoot,
|
|
142
|
+
environment,
|
|
143
|
+
missingReasonCode: 'ios_host_artifact_xcodebuild_missing',
|
|
144
|
+
successReasonCode: 'ios_host_artifact_xcodebuild_available',
|
|
145
|
+
});
|
|
146
|
+
if (!xcodebuild.available) {
|
|
147
|
+
return failedHostArtifactResult({
|
|
148
|
+
reasonCode: xcodebuild.reasonCode,
|
|
149
|
+
detail: xcodebuild.detail,
|
|
150
|
+
artifactPath: outputPath,
|
|
151
|
+
distRoot,
|
|
152
|
+
sourceKind: 'local_build',
|
|
153
|
+
checks: {
|
|
154
|
+
xcodebuild,
|
|
155
|
+
cargo: null,
|
|
156
|
+
rustTargets: null,
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!existsSync(cargoManifestPath)) {
|
|
162
|
+
return failedHostArtifactResult({
|
|
163
|
+
reasonCode: 'ios_host_artifact_rust_manifest_missing',
|
|
164
|
+
detail: `Missing Rust host manifest at ${cargoManifestPath}.`,
|
|
165
|
+
artifactPath: outputPath,
|
|
166
|
+
distRoot,
|
|
167
|
+
sourceKind: 'local_build',
|
|
168
|
+
checks: {
|
|
169
|
+
xcodebuild,
|
|
170
|
+
cargo: null,
|
|
171
|
+
rustTargets: null,
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const cargo = probeExecutable({
|
|
177
|
+
spawnSyncImpl,
|
|
178
|
+
executablePath: cargoExecutable,
|
|
179
|
+
args: ['--version'],
|
|
180
|
+
cwd: rustHostRoot,
|
|
181
|
+
environment,
|
|
182
|
+
missingReasonCode: 'ios_host_artifact_cargo_missing',
|
|
183
|
+
successReasonCode: 'ios_host_artifact_cargo_available',
|
|
184
|
+
});
|
|
185
|
+
if (!cargo.available) {
|
|
186
|
+
return failedHostArtifactResult({
|
|
187
|
+
reasonCode: cargo.reasonCode,
|
|
188
|
+
detail: cargo.detail,
|
|
189
|
+
artifactPath: outputPath,
|
|
190
|
+
distRoot,
|
|
191
|
+
sourceKind: 'local_build',
|
|
192
|
+
checks: {
|
|
193
|
+
xcodebuild,
|
|
194
|
+
cargo,
|
|
195
|
+
rustTargets: null,
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const rustTargets = probeRustTargets({
|
|
201
|
+
spawnSyncImpl,
|
|
202
|
+
executablePath: rustupExecutable,
|
|
203
|
+
cwd: rustHostRoot,
|
|
204
|
+
environment,
|
|
205
|
+
});
|
|
206
|
+
if (!rustTargets.available) {
|
|
207
|
+
return failedHostArtifactResult({
|
|
208
|
+
reasonCode: rustTargets.reasonCode,
|
|
209
|
+
detail: rustTargets.detail,
|
|
210
|
+
artifactPath: outputPath,
|
|
211
|
+
distRoot,
|
|
212
|
+
sourceKind: 'local_build',
|
|
213
|
+
checks: {
|
|
214
|
+
xcodebuild,
|
|
215
|
+
cargo,
|
|
216
|
+
rustTargets,
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const tempRoot = mkdtempSync(join(tmpdir(), 'aurajs-ios-host-artifact-'));
|
|
222
|
+
try {
|
|
223
|
+
const isolatedWorkspace = createIsolatedIosCargoWorkspace({
|
|
224
|
+
rustHostRoot,
|
|
225
|
+
patchedV8Path,
|
|
226
|
+
tempRoot,
|
|
227
|
+
});
|
|
228
|
+
const isolatedRustHostRoot = isolatedWorkspace.rustHostRoot;
|
|
229
|
+
const isolatedCargoManifestPath = isolatedWorkspace.cargoManifestPath;
|
|
230
|
+
const cargoTargetRoot = resolveCargoTargetRoot({
|
|
231
|
+
rustHostRoot: isolatedRustHostRoot,
|
|
232
|
+
environment,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
runCargoBuild({
|
|
236
|
+
spawnSyncImpl,
|
|
237
|
+
executablePath: cargoExecutable,
|
|
238
|
+
manifestPath: isolatedCargoManifestPath,
|
|
239
|
+
rustHostRoot: isolatedRustHostRoot,
|
|
240
|
+
rustTarget: IOS_DEVICE_RUST_TARGET,
|
|
241
|
+
profile,
|
|
242
|
+
environment: createIosCargoBuildEnvironment(environment, IOS_DEVICE_RUST_TARGET),
|
|
243
|
+
});
|
|
244
|
+
runCargoBuild({
|
|
245
|
+
spawnSyncImpl,
|
|
246
|
+
executablePath: cargoExecutable,
|
|
247
|
+
manifestPath: isolatedCargoManifestPath,
|
|
248
|
+
rustHostRoot: isolatedRustHostRoot,
|
|
249
|
+
rustTarget: IOS_SIMULATOR_RUST_TARGET,
|
|
250
|
+
profile,
|
|
251
|
+
environment: createIosCargoBuildEnvironment(environment, IOS_SIMULATOR_RUST_TARGET),
|
|
252
|
+
});
|
|
253
|
+
const deviceHostLibraryPath = resolveBuiltHostLibraryPath({
|
|
254
|
+
cargoTargetRoot,
|
|
255
|
+
rustTarget: IOS_DEVICE_RUST_TARGET,
|
|
256
|
+
profile,
|
|
257
|
+
});
|
|
258
|
+
const simulatorHostLibraryPath = resolveBuiltHostLibraryPath({
|
|
259
|
+
cargoTargetRoot,
|
|
260
|
+
rustTarget: IOS_SIMULATOR_RUST_TARGET,
|
|
261
|
+
profile,
|
|
262
|
+
});
|
|
263
|
+
const deviceFrameworkPath = createStaticFrameworkBundle({
|
|
264
|
+
tempRoot,
|
|
265
|
+
sliceName: IOS_DEVICE_LIBRARY_IDENTIFIER,
|
|
266
|
+
libraryPath: deviceHostLibraryPath,
|
|
267
|
+
});
|
|
268
|
+
const simulatorFrameworkPath = createStaticFrameworkBundle({
|
|
269
|
+
tempRoot,
|
|
270
|
+
sliceName: IOS_SIMULATOR_LIBRARY_IDENTIFIER,
|
|
271
|
+
libraryPath: simulatorHostLibraryPath,
|
|
272
|
+
});
|
|
273
|
+
const tempOutputPath = join(tempRoot, IOS_HOST_XCFRAMEWORK_NAME);
|
|
274
|
+
const createResult = spawnSyncImpl(
|
|
275
|
+
xcodebuildExecutable,
|
|
276
|
+
[
|
|
277
|
+
'-create-xcframework',
|
|
278
|
+
'-framework',
|
|
279
|
+
deviceFrameworkPath,
|
|
280
|
+
'-framework',
|
|
281
|
+
simulatorFrameworkPath,
|
|
282
|
+
'-output',
|
|
283
|
+
tempOutputPath,
|
|
284
|
+
],
|
|
285
|
+
{
|
|
286
|
+
cwd: isolatedRustHostRoot,
|
|
287
|
+
encoding: 'utf8',
|
|
288
|
+
env: environment,
|
|
289
|
+
},
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
if (createResult.status !== 0) {
|
|
293
|
+
return failedHostArtifactResult({
|
|
294
|
+
reasonCode: 'ios_host_artifact_xcframework_create_failed',
|
|
295
|
+
detail: formatSpawnFailure('xcodebuild -create-xcframework', createResult),
|
|
296
|
+
artifactPath: outputPath,
|
|
297
|
+
distRoot,
|
|
298
|
+
sourceKind: 'local_build',
|
|
299
|
+
checks: {
|
|
300
|
+
xcodebuild,
|
|
301
|
+
cargo,
|
|
302
|
+
rustTargets,
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const builtArtifact = validateXcframework(tempOutputPath, {
|
|
308
|
+
spawnSyncImpl,
|
|
309
|
+
nmExecutable,
|
|
310
|
+
cwd: isolatedRustHostRoot,
|
|
311
|
+
environment,
|
|
312
|
+
});
|
|
313
|
+
if (!builtArtifact.ok) {
|
|
314
|
+
return failedHostArtifactResult({
|
|
315
|
+
reasonCode: 'ios_host_artifact_output_invalid',
|
|
316
|
+
detail: builtArtifact.detail,
|
|
317
|
+
artifactPath: outputPath,
|
|
318
|
+
distRoot,
|
|
319
|
+
sourceKind: 'local_build',
|
|
320
|
+
checks: {
|
|
321
|
+
xcodebuild,
|
|
322
|
+
cargo,
|
|
323
|
+
rustTargets,
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
replaceDirectory(tempOutputPath, outputPath);
|
|
329
|
+
return {
|
|
330
|
+
ok: true,
|
|
331
|
+
reasonCode: 'ios_host_artifact_built',
|
|
332
|
+
detail: 'Built the iOS host xcframework from the local Rust host and copied it into dist.',
|
|
333
|
+
artifactPath: outputPath,
|
|
334
|
+
distRoot,
|
|
335
|
+
sourceKind: 'local_build',
|
|
336
|
+
checks: {
|
|
337
|
+
xcodebuild,
|
|
338
|
+
cargo,
|
|
339
|
+
rustTargets,
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
} catch (error) {
|
|
343
|
+
const reasonCode = error?.reasonCode || 'ios_host_artifact_cargo_build_failed';
|
|
344
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
345
|
+
if (reasonCode === 'ios_host_artifact_staticlib_missing' || reasonCode === 'ios_host_artifact_v8_patch_missing') {
|
|
346
|
+
return failedHostArtifactResult({
|
|
347
|
+
reasonCode,
|
|
348
|
+
detail,
|
|
349
|
+
artifactPath: outputPath,
|
|
350
|
+
distRoot,
|
|
351
|
+
sourceKind: 'local_build',
|
|
352
|
+
checks: {
|
|
353
|
+
xcodebuild,
|
|
354
|
+
cargo,
|
|
355
|
+
rustTargets,
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
return failedHostArtifactResult({
|
|
360
|
+
reasonCode,
|
|
361
|
+
detail,
|
|
362
|
+
artifactPath: outputPath,
|
|
363
|
+
distRoot,
|
|
364
|
+
sourceKind: 'local_build',
|
|
365
|
+
checks: {
|
|
366
|
+
xcodebuild,
|
|
367
|
+
cargo,
|
|
368
|
+
rustTargets,
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
} finally {
|
|
372
|
+
rmSync(tempRoot, { recursive: true, force: true });
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function failedHostArtifactResult({
|
|
377
|
+
reasonCode,
|
|
378
|
+
detail,
|
|
379
|
+
artifactPath,
|
|
380
|
+
distRoot,
|
|
381
|
+
sourceKind,
|
|
382
|
+
checks,
|
|
383
|
+
}) {
|
|
384
|
+
return {
|
|
385
|
+
ok: false,
|
|
386
|
+
reasonCode,
|
|
387
|
+
detail,
|
|
388
|
+
artifactPath,
|
|
389
|
+
distRoot,
|
|
390
|
+
sourceKind,
|
|
391
|
+
checks,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function probeExecutable({
|
|
396
|
+
spawnSyncImpl,
|
|
397
|
+
executablePath,
|
|
398
|
+
args,
|
|
399
|
+
cwd,
|
|
400
|
+
environment,
|
|
401
|
+
missingReasonCode,
|
|
402
|
+
successReasonCode,
|
|
403
|
+
}) {
|
|
404
|
+
const result = spawnSyncImpl(executablePath, args, {
|
|
405
|
+
cwd,
|
|
406
|
+
encoding: 'utf8',
|
|
407
|
+
env: environment,
|
|
408
|
+
});
|
|
409
|
+
if (result.status !== 0) {
|
|
410
|
+
return {
|
|
411
|
+
available: false,
|
|
412
|
+
reasonCode: missingReasonCode,
|
|
413
|
+
executablePath,
|
|
414
|
+
detail: formatSpawnFailure(executablePath, result),
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
available: true,
|
|
419
|
+
reasonCode: successReasonCode,
|
|
420
|
+
executablePath,
|
|
421
|
+
detail: firstNonEmptyLine(result.stdout) || null,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function probeRustTargets({
|
|
426
|
+
spawnSyncImpl,
|
|
427
|
+
executablePath,
|
|
428
|
+
cwd,
|
|
429
|
+
environment,
|
|
430
|
+
}) {
|
|
431
|
+
const result = spawnSyncImpl(executablePath, ['target', 'list', '--installed'], {
|
|
432
|
+
cwd,
|
|
433
|
+
encoding: 'utf8',
|
|
434
|
+
env: environment,
|
|
435
|
+
});
|
|
436
|
+
if (result.status !== 0) {
|
|
437
|
+
return {
|
|
438
|
+
available: false,
|
|
439
|
+
reasonCode: 'ios_host_artifact_rustup_missing',
|
|
440
|
+
executablePath,
|
|
441
|
+
detail: formatSpawnFailure(`${executablePath} target list --installed`, result),
|
|
442
|
+
installed: [],
|
|
443
|
+
missing: [IOS_DEVICE_RUST_TARGET, IOS_SIMULATOR_RUST_TARGET],
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const installed = String(result.stdout || '')
|
|
448
|
+
.split('\n')
|
|
449
|
+
.map((line) => line.trim())
|
|
450
|
+
.filter(Boolean);
|
|
451
|
+
const missing = [IOS_DEVICE_RUST_TARGET, IOS_SIMULATOR_RUST_TARGET]
|
|
452
|
+
.filter((target) => !installed.includes(target));
|
|
453
|
+
if (missing.length > 0) {
|
|
454
|
+
return {
|
|
455
|
+
available: false,
|
|
456
|
+
reasonCode: 'ios_host_artifact_rust_target_missing',
|
|
457
|
+
executablePath,
|
|
458
|
+
detail: `Missing required iOS Rust targets: ${missing.join(', ')}.`,
|
|
459
|
+
installed,
|
|
460
|
+
missing,
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return {
|
|
465
|
+
available: true,
|
|
466
|
+
reasonCode: 'ios_host_artifact_rust_targets_available',
|
|
467
|
+
executablePath,
|
|
468
|
+
detail: null,
|
|
469
|
+
installed,
|
|
470
|
+
missing: [],
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function createIosCargoBuildEnvironment(environment, rustTarget) {
|
|
475
|
+
const buildEnvironment = {
|
|
476
|
+
...environment,
|
|
477
|
+
};
|
|
478
|
+
const deploymentTarget = cleanString(buildEnvironment.AURA_IOS_DEPLOYMENT_TARGET)
|
|
479
|
+
|| cleanString(buildEnvironment.IPHONEOS_DEPLOYMENT_TARGET)
|
|
480
|
+
|| DEFAULT_IOS_HOST_DEPLOYMENT_TARGET;
|
|
481
|
+
const deploymentFlag = rustTarget === IOS_SIMULATOR_RUST_TARGET
|
|
482
|
+
? `-mios-simulator-version-min=${deploymentTarget}`
|
|
483
|
+
: `-miphoneos-version-min=${deploymentTarget}`;
|
|
484
|
+
|
|
485
|
+
if (!cleanString(buildEnvironment.V8_FROM_SOURCE) && !cleanString(buildEnvironment.RUSTY_V8_ARCHIVE)) {
|
|
486
|
+
buildEnvironment.V8_FROM_SOURCE = '1';
|
|
487
|
+
}
|
|
488
|
+
if (!cleanString(buildEnvironment.PYTHON)) {
|
|
489
|
+
buildEnvironment.PYTHON = 'python3';
|
|
490
|
+
}
|
|
491
|
+
buildEnvironment.IPHONEOS_DEPLOYMENT_TARGET = deploymentTarget;
|
|
492
|
+
appendTargetShellFlag(buildEnvironment, 'CFLAGS', rustTarget, deploymentFlag);
|
|
493
|
+
appendTargetShellFlag(buildEnvironment, 'CXXFLAGS', rustTarget, deploymentFlag);
|
|
494
|
+
appendTargetShellFlag(buildEnvironment, 'OBJCFLAGS', rustTarget, deploymentFlag);
|
|
495
|
+
buildEnvironment.RUSTFLAGS = withShellFlag(
|
|
496
|
+
buildEnvironment.RUSTFLAGS,
|
|
497
|
+
`-C link-arg=${deploymentFlag}`,
|
|
498
|
+
);
|
|
499
|
+
buildEnvironment.GN_ARGS = withIosGnArgs(buildEnvironment.GN_ARGS, rustTarget, deploymentTarget);
|
|
500
|
+
|
|
501
|
+
return buildEnvironment;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function createIsolatedIosCargoWorkspace({
|
|
505
|
+
rustHostRoot,
|
|
506
|
+
patchedV8Path,
|
|
507
|
+
tempRoot,
|
|
508
|
+
}) {
|
|
509
|
+
const resolvedPatchedV8Path = resolveExistingPath(patchedV8Path, 'patched iOS v8 crate');
|
|
510
|
+
const isolatedRustHostRoot = join(tempRoot, 'rust-host');
|
|
511
|
+
copyDirectoryExcluding(rustHostRoot, isolatedRustHostRoot, ['target', '.git', '.cargo']);
|
|
512
|
+
const cargoConfigDirectory = join(isolatedRustHostRoot, '.cargo');
|
|
513
|
+
mkdirSync(cargoConfigDirectory, { recursive: true });
|
|
514
|
+
writeFileSync(
|
|
515
|
+
join(cargoConfigDirectory, 'config.toml'),
|
|
516
|
+
[
|
|
517
|
+
'[patch.crates-io]',
|
|
518
|
+
`v8 = { path = ${JSON.stringify(resolvedPatchedV8Path)} }`,
|
|
519
|
+
'',
|
|
520
|
+
].join('\n'),
|
|
521
|
+
'utf8',
|
|
522
|
+
);
|
|
523
|
+
const cargoManifestPath = join(isolatedRustHostRoot, 'Cargo.toml');
|
|
524
|
+
if (!existsSync(cargoManifestPath)) {
|
|
525
|
+
const error = new Error(`Missing isolated Rust host manifest at ${cargoManifestPath}.`);
|
|
526
|
+
error.reasonCode = 'ios_host_artifact_rust_manifest_missing';
|
|
527
|
+
throw error;
|
|
528
|
+
}
|
|
529
|
+
return {
|
|
530
|
+
rustHostRoot: isolatedRustHostRoot,
|
|
531
|
+
cargoManifestPath,
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function withIosGnArgs(existingGnArgs, rustTarget, deploymentTarget) {
|
|
536
|
+
const targetEnvironment = rustTarget === IOS_SIMULATOR_RUST_TARGET ? 'simulator' : 'device';
|
|
537
|
+
let gnArgs = cleanString(existingGnArgs) || '';
|
|
538
|
+
|
|
539
|
+
gnArgs = appendGnArgIfMissing(gnArgs, 'target_os', '"ios"');
|
|
540
|
+
gnArgs = appendGnArgIfMissing(gnArgs, 'target_environment', `"${targetEnvironment}"`);
|
|
541
|
+
gnArgs = appendGnArgIfMissing(gnArgs, 'ios_deployment_target', `"${deploymentTarget}"`);
|
|
542
|
+
gnArgs = appendGnArgIfMissing(gnArgs, 'ios_enable_code_signing', 'false');
|
|
543
|
+
gnArgs = appendGnArgIfMissing(gnArgs, 'enable_ios_bitcode', 'false');
|
|
544
|
+
|
|
545
|
+
return gnArgs;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function appendGnArgIfMissing(gnArgs, key, value) {
|
|
549
|
+
const pattern = new RegExp(`(?:^|\\s)${escapeRegExp(key)}=`);
|
|
550
|
+
if (pattern.test(gnArgs)) {
|
|
551
|
+
return gnArgs;
|
|
552
|
+
}
|
|
553
|
+
return gnArgs ? `${gnArgs} ${key}=${value}` : `${key}=${value}`;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function escapeRegExp(input) {
|
|
557
|
+
return String(input).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function withShellFlag(existingFlags, flag) {
|
|
561
|
+
let flags = cleanString(existingFlags) || '';
|
|
562
|
+
if (!flags.includes(flag)) {
|
|
563
|
+
flags = `${flags} ${flag}`.trim();
|
|
564
|
+
}
|
|
565
|
+
return flags;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function appendTargetShellFlag(environment, prefix, rustTarget, flag) {
|
|
569
|
+
const targetKey = `${prefix}_${rustTarget.replaceAll('-', '_')}`;
|
|
570
|
+
environment[targetKey] = withShellFlag(environment[targetKey], flag);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function resolveExistingPath(path, label) {
|
|
574
|
+
const resolved = resolve(path);
|
|
575
|
+
if (!existsSync(resolved)) {
|
|
576
|
+
const error = new Error(`Missing ${label}: ${resolved}`);
|
|
577
|
+
error.reasonCode = 'ios_host_artifact_v8_patch_missing';
|
|
578
|
+
throw error;
|
|
579
|
+
}
|
|
580
|
+
return resolved;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function runCargoBuild({
|
|
584
|
+
spawnSyncImpl,
|
|
585
|
+
executablePath,
|
|
586
|
+
manifestPath,
|
|
587
|
+
rustHostRoot,
|
|
588
|
+
rustTarget,
|
|
589
|
+
profile,
|
|
590
|
+
environment,
|
|
591
|
+
}) {
|
|
592
|
+
const args = [
|
|
593
|
+
'build',
|
|
594
|
+
'--manifest-path',
|
|
595
|
+
manifestPath,
|
|
596
|
+
'--lib',
|
|
597
|
+
'--target',
|
|
598
|
+
rustTarget,
|
|
599
|
+
];
|
|
600
|
+
if (profile === 'release') {
|
|
601
|
+
args.push('--release');
|
|
602
|
+
}
|
|
603
|
+
const result = spawnSyncImpl(executablePath, args, {
|
|
604
|
+
cwd: rustHostRoot,
|
|
605
|
+
encoding: 'utf8',
|
|
606
|
+
env: environment,
|
|
607
|
+
});
|
|
608
|
+
if (result.status !== 0) {
|
|
609
|
+
const error = new Error(formatSpawnFailure(`${executablePath} build`, result));
|
|
610
|
+
error.reasonCode = 'ios_host_artifact_cargo_build_failed';
|
|
611
|
+
throw error;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function resolveCargoTargetRoot({ rustHostRoot, environment }) {
|
|
616
|
+
const configuredTargetDir = cleanString(environment.CARGO_TARGET_DIR);
|
|
617
|
+
if (!configuredTargetDir) {
|
|
618
|
+
return join(rustHostRoot, 'target');
|
|
619
|
+
}
|
|
620
|
+
return resolve(rustHostRoot, configuredTargetDir);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function resolveBuiltHostLibraryPath({ cargoTargetRoot, rustTarget, profile }) {
|
|
624
|
+
const outputRoot = join(cargoTargetRoot, rustTarget, profile);
|
|
625
|
+
const dynamicLibraryPath = join(outputRoot, IOS_HOST_DYNAMIC_LIBRARY);
|
|
626
|
+
if (existsSync(dynamicLibraryPath)) {
|
|
627
|
+
return dynamicLibraryPath;
|
|
628
|
+
}
|
|
629
|
+
const staticLibraryPath = join(outputRoot, IOS_HOST_STATIC_LIBRARY);
|
|
630
|
+
if (existsSync(staticLibraryPath)) {
|
|
631
|
+
return staticLibraryPath;
|
|
632
|
+
}
|
|
633
|
+
const error = new Error(
|
|
634
|
+
`Missing iOS host library output for ${rustTarget} under ${outputRoot}.`,
|
|
635
|
+
);
|
|
636
|
+
error.reasonCode = 'ios_host_artifact_staticlib_missing';
|
|
637
|
+
throw error;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
function createStaticFrameworkBundle({ tempRoot, sliceName, libraryPath }) {
|
|
641
|
+
const frameworkRoot = join(tempRoot, sliceName, IOS_HOST_FRAMEWORK_DIRECTORY);
|
|
642
|
+
mkdirSync(frameworkRoot, { recursive: true });
|
|
643
|
+
cpSync(libraryPath, join(frameworkRoot, IOS_HOST_FRAMEWORK_BINARY), { force: true });
|
|
644
|
+
writeFileSync(
|
|
645
|
+
join(frameworkRoot, 'Info.plist'),
|
|
646
|
+
[
|
|
647
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
648
|
+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
649
|
+
'<plist version="1.0">',
|
|
650
|
+
'<dict>',
|
|
651
|
+
' <key>CFBundleExecutable</key>',
|
|
652
|
+
` <string>${IOS_HOST_FRAMEWORK_BINARY}</string>`,
|
|
653
|
+
' <key>CFBundleIdentifier</key>',
|
|
654
|
+
` <string>gg.aurajs.${sliceName}.${IOS_HOST_FRAMEWORK_BINARY}</string>`,
|
|
655
|
+
' <key>CFBundleName</key>',
|
|
656
|
+
` <string>${IOS_HOST_FRAMEWORK_BINARY}</string>`,
|
|
657
|
+
' <key>CFBundlePackageType</key>',
|
|
658
|
+
' <string>FMWK</string>',
|
|
659
|
+
' <key>CFBundleShortVersionString</key>',
|
|
660
|
+
' <string>1.0.0</string>',
|
|
661
|
+
' <key>CFBundleVersion</key>',
|
|
662
|
+
' <string>1</string>',
|
|
663
|
+
'</dict>',
|
|
664
|
+
'</plist>',
|
|
665
|
+
'',
|
|
666
|
+
].join('\n'),
|
|
667
|
+
'utf8',
|
|
668
|
+
);
|
|
669
|
+
return frameworkRoot;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function validateXcframework(
|
|
673
|
+
xcframeworkPath,
|
|
674
|
+
{
|
|
675
|
+
spawnSyncImpl = spawnSync,
|
|
676
|
+
nmExecutable = 'nm',
|
|
677
|
+
cwd = process.cwd(),
|
|
678
|
+
environment = process.env,
|
|
679
|
+
} = {},
|
|
680
|
+
) {
|
|
681
|
+
try {
|
|
682
|
+
requireExistingPath(join(xcframeworkPath, 'Info.plist'), 'xcframework Info.plist');
|
|
683
|
+
const deviceBinaryPath = requireExistingPath(
|
|
684
|
+
join(
|
|
685
|
+
xcframeworkPath,
|
|
686
|
+
IOS_DEVICE_LIBRARY_IDENTIFIER,
|
|
687
|
+
IOS_HOST_FRAMEWORK_DIRECTORY,
|
|
688
|
+
IOS_HOST_FRAMEWORK_BINARY,
|
|
689
|
+
),
|
|
690
|
+
'xcframework device binary',
|
|
691
|
+
);
|
|
692
|
+
const simulatorBinaryPath = requireExistingPath(
|
|
693
|
+
join(
|
|
694
|
+
xcframeworkPath,
|
|
695
|
+
IOS_SIMULATOR_LIBRARY_IDENTIFIER,
|
|
696
|
+
IOS_HOST_FRAMEWORK_DIRECTORY,
|
|
697
|
+
IOS_HOST_FRAMEWORK_BINARY,
|
|
698
|
+
),
|
|
699
|
+
'xcframework simulator binary',
|
|
700
|
+
);
|
|
701
|
+
validateFrameworkBinarySymbols(deviceBinaryPath, 'device', {
|
|
702
|
+
spawnSyncImpl,
|
|
703
|
+
nmExecutable,
|
|
704
|
+
cwd,
|
|
705
|
+
environment,
|
|
706
|
+
});
|
|
707
|
+
validateFrameworkBinarySymbols(simulatorBinaryPath, 'simulator', {
|
|
708
|
+
spawnSyncImpl,
|
|
709
|
+
nmExecutable,
|
|
710
|
+
cwd,
|
|
711
|
+
environment,
|
|
712
|
+
});
|
|
713
|
+
return {
|
|
714
|
+
ok: true,
|
|
715
|
+
detail: null,
|
|
716
|
+
};
|
|
717
|
+
} catch (error) {
|
|
718
|
+
return {
|
|
719
|
+
ok: false,
|
|
720
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function validateFrameworkBinarySymbols(
|
|
726
|
+
binaryPath,
|
|
727
|
+
sliceLabel,
|
|
728
|
+
{
|
|
729
|
+
spawnSyncImpl,
|
|
730
|
+
nmExecutable,
|
|
731
|
+
cwd,
|
|
732
|
+
environment,
|
|
733
|
+
},
|
|
734
|
+
) {
|
|
735
|
+
const result = spawnSyncImpl(nmExecutable, ['-gU', binaryPath], {
|
|
736
|
+
cwd,
|
|
737
|
+
encoding: 'utf8',
|
|
738
|
+
env: environment,
|
|
739
|
+
});
|
|
740
|
+
if (result.status !== 0) {
|
|
741
|
+
throw new Error(
|
|
742
|
+
`Failed to inspect exported iOS host symbols in ${sliceLabel} binary ${resolve(binaryPath)}: ${formatSpawnFailure(`${nmExecutable} -gU`, result)}`,
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
const exportedSymbols = String(result.stdout || '');
|
|
746
|
+
const missingSymbols = REQUIRED_HOST_SYMBOLS.filter((symbol) => {
|
|
747
|
+
const pattern = new RegExp(`(?:^|\\s)_?${escapeRegExp(symbol)}(?:\\s|$)`, 'm');
|
|
748
|
+
return !pattern.test(exportedSymbols);
|
|
749
|
+
});
|
|
750
|
+
if (missingSymbols.length > 0) {
|
|
751
|
+
throw new Error(
|
|
752
|
+
`Missing required iOS host symbol(s) in ${sliceLabel} binary ${resolve(binaryPath)}: ${missingSymbols.join(', ')}`,
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
function requireExistingPath(path, label) {
|
|
758
|
+
const resolved = resolve(path);
|
|
759
|
+
if (!existsSync(resolved)) {
|
|
760
|
+
throw new Error(`Missing ${label}: ${resolved}`);
|
|
761
|
+
}
|
|
762
|
+
return resolved;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
function replaceDirectory(sourceRoot, destRoot) {
|
|
766
|
+
rmSync(destRoot, { recursive: true, force: true });
|
|
767
|
+
copyDirectory(sourceRoot, destRoot);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
function copyDirectory(sourceRoot, destRoot) {
|
|
771
|
+
mkdirSync(destRoot, { recursive: true });
|
|
772
|
+
cpSync(sourceRoot, destRoot, {
|
|
773
|
+
recursive: true,
|
|
774
|
+
dereference: false,
|
|
775
|
+
force: true,
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
function copyDirectoryExcluding(sourceRoot, destRoot, excludedTopLevelNames) {
|
|
780
|
+
const excluded = new Set(excludedTopLevelNames);
|
|
781
|
+
mkdirSync(destRoot, { recursive: true });
|
|
782
|
+
cpSync(sourceRoot, destRoot, {
|
|
783
|
+
recursive: true,
|
|
784
|
+
dereference: false,
|
|
785
|
+
force: true,
|
|
786
|
+
filter(source) {
|
|
787
|
+
const rel = relative(sourceRoot, source);
|
|
788
|
+
if (!rel || rel === '') {
|
|
789
|
+
return true;
|
|
790
|
+
}
|
|
791
|
+
const topLevel = rel.split(/[\\/]/)[0];
|
|
792
|
+
return !excluded.has(topLevel);
|
|
793
|
+
},
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
function formatSpawnFailure(commandLabel, result) {
|
|
798
|
+
const detail = [result?.stdout, result?.stderr]
|
|
799
|
+
.filter((value) => typeof value === 'string' && value.trim().length > 0)
|
|
800
|
+
.join('\n')
|
|
801
|
+
.trim();
|
|
802
|
+
return detail.length > 0
|
|
803
|
+
? detail
|
|
804
|
+
: `${commandLabel} was not available or returned a non-zero exit status.`;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
function firstNonEmptyLine(value) {
|
|
808
|
+
return String(value || '')
|
|
809
|
+
.split('\n')
|
|
810
|
+
.map((line) => line.trim())
|
|
811
|
+
.find(Boolean)
|
|
812
|
+
|| null;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
function cleanString(value) {
|
|
816
|
+
return typeof value === 'string' && value.trim().length > 0
|
|
817
|
+
? value.trim()
|
|
818
|
+
: null;
|
|
819
|
+
}
|