@geastack/apple 0.2.8
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/LICENSE +202 -0
- package/README.md +54 -0
- package/dist/index.d.ts +127 -0
- package/dist/index.js +3076 -0
- package/generated/AVFoundation.d.ts +112 -0
- package/generated/ActivityKit.d.ts +7 -0
- package/generated/AppKit.d.ts +268 -0
- package/generated/CoreGraphics.d.ts +18 -0
- package/generated/CoreLocation.d.ts +28 -0
- package/generated/CoreMedia.d.ts +8 -0
- package/generated/Dispatch.d.ts +2 -0
- package/generated/Foundation.d.ts +13 -0
- package/generated/MapKit.d.ts +28 -0
- package/generated/Metal.d.ts +91 -0
- package/generated/MetalKit.d.ts +22 -0
- package/generated/Photos.d.ts +5 -0
- package/generated/QuartzCore.d.ts +12 -0
- package/generated/SwiftData.d.ts +11 -0
- package/generated/UIKit.d.ts +187 -0
- package/package.json +123 -0
- package/runtime/AVFoundation.js +136 -0
- package/runtime/ActivityKit.js +18 -0
- package/runtime/AppKit.js +313 -0
- package/runtime/CoreGraphics.js +15 -0
- package/runtime/CoreLocation.js +30 -0
- package/runtime/CoreMedia.js +7 -0
- package/runtime/Dispatch.js +11 -0
- package/runtime/Foundation.js +21 -0
- package/runtime/MapKit.js +22 -0
- package/runtime/Metal.js +126 -0
- package/runtime/MetalKit.js +15 -0
- package/runtime/Photos.js +9 -0
- package/runtime/QuartzCore.js +9 -0
- package/runtime/SwiftData.js +29 -0
- package/runtime/UIKit.js +242 -0
- package/runtime/empty.js +1 -0
- package/targets/ios/Info.plist.in +25 -0
- package/targets/ios/README.md +46 -0
- package/targets/ios/build-ios.sh +395 -0
- package/targets/ios/detect-development-team.mjs +109 -0
- package/targets/ios/generate-resident-entry.mjs +67 -0
- package/targets/ios/generate-resident-registry.mjs +162 -0
- package/targets/ios/generate-xcode-project.mjs +520 -0
- package/targets/ios/main/canvas_view.h +7 -0
- package/targets/ios/main/canvas_view.mm +94 -0
- package/targets/ios/main/font_registry.h +17 -0
- package/targets/ios/main/font_registry.mm +129 -0
- package/targets/ios/main/image_bridge.h +13 -0
- package/targets/ios/main/image_bridge.mm +53 -0
- package/targets/ios/main/ios_apps.c +28 -0
- package/targets/ios/main/ios_camera.mm +446 -0
- package/targets/ios/main/ios_display.mm +344 -0
- package/targets/ios/main/ios_main.mm +811 -0
- package/targets/ios/main/ios_memory.cpp +55 -0
- package/targets/ios/main/ios_renderer.h +20 -0
- package/targets/ios/main/ios_renderer.mm +27 -0
- package/targets/ios/main/ios_root_background.cpp +20 -0
- package/targets/ios/main/ios_root_background.h +11 -0
- package/targets/ios/main/ios_sensors.mm +244 -0
- package/targets/ios/main/ios_timers.mm +53 -0
- package/targets/ios/main/renderer/ios_renderer_internal.h +65 -0
- package/targets/ios/main/renderer/ios_renderer_support.mm +111 -0
- package/targets/ios/main/renderer/native_button.mm +179 -0
- package/targets/ios/main/renderer/native_label.mm +161 -0
- package/targets/ios/main/renderer/native_scroll_container.mm +279 -0
- package/targets/ios/main/renderer/view_reconciler.mm +274 -0
- package/targets/macos/Info.plist.in +26 -0
- package/targets/macos/README.md +131 -0
- package/targets/macos/build-child-lifecycle.sh +119 -0
- package/targets/macos/build-macos.sh +2156 -0
- package/targets/macos/check-module-graph-freshness.mjs +82 -0
- package/targets/macos/compiler-input-fingerprint.mjs +32 -0
- package/targets/macos/finalize-apple-native-output.mjs +77 -0
- package/targets/macos/generate-resident-entry.mjs +80 -0
- package/targets/macos/generate-resident-registry.mjs +141 -0
- package/targets/macos/heavy-build-lock.sh +176 -0
- package/targets/macos/main/canvas_view.h +9 -0
- package/targets/macos/main/canvas_view.mm +176 -0
- package/targets/macos/main/color_convert.h +18 -0
- package/targets/macos/main/color_convert.mm +39 -0
- package/targets/macos/main/font_registry.h +35 -0
- package/targets/macos/main/font_registry.mm +350 -0
- package/targets/macos/main/image_bridge.h +16 -0
- package/targets/macos/main/image_bridge.mm +52 -0
- package/targets/macos/main/macos_app_platform.mm +95 -0
- package/targets/macos/main/macos_apps.c +31 -0
- package/targets/macos/main/macos_device_control.mm +56 -0
- package/targets/macos/main/macos_display.mm +484 -0
- package/targets/macos/main/macos_host_device_control.mm +95 -0
- package/targets/macos/main/macos_main.mm +839 -0
- package/targets/macos/main/macos_memory.cpp +31 -0
- package/targets/macos/main/macos_native_shell.h +31 -0
- package/targets/macos/main/macos_native_shell.mm +396 -0
- package/targets/macos/main/macos_network.mm +253 -0
- package/targets/macos/main/macos_renderer.h +39 -0
- package/targets/macos/main/macos_renderer.mm +1969 -0
- package/targets/macos/main/macos_sensors.cpp +148 -0
- package/targets/macos/main/macos_smoke_app.cpp +65 -0
- package/targets/macos/main/macos_timers.mm +52 -0
- package/targets/macos/main/press_bridge.h +21 -0
- package/targets/macos/main/press_bridge.mm +72 -0
- package/targets/macos/main/thermalright_hid_display.h +14 -0
- package/targets/macos/main/thermalright_hid_display.mm +995 -0
- package/targets/macos/native-artifact-provenance.mjs +421 -0
- package/targets/macos/order-compile-edges.mjs +64 -0
- package/targets/macos/preflight-hard-muted-artifact.mjs +93 -0
- package/targets/macos/prune-generated-modules.mjs +147 -0
- package/targets/macos/sync-resources.mjs +153 -0
- package/targets/shared/analyze-audio-capture.mjs +131 -0
- package/targets/shared/apple_audio.mm +1194 -0
- package/targets/shared/apple_audio_selftest.mm +602 -0
- package/targets/shared/apple_audio_testing.h +34 -0
- package/targets/shared/build-audio-selftest.sh +50 -0
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import crypto from 'node:crypto'
|
|
3
|
+
import fs from 'node:fs'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
|
|
7
|
+
import { execFileSync } from 'node:child_process'
|
|
8
|
+
|
|
9
|
+
// The project is the directory this ran in -- the one being compiled, which is
|
|
10
|
+
// also what the CLI resolves from cwd. iOS target sources live next to this
|
|
11
|
+
// script, not in the project. App metadata and icon generation come from the
|
|
12
|
+
// gea CLI (build-ios.sh resolves it and passes $GEA_CLI_BIN).
|
|
13
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
14
|
+
const projectDir = process.cwd()
|
|
15
|
+
const geaCli = process.env.GEA_CLI_BIN || path.join(scriptDir, '../../node_modules/@geastack/cli/bin/gea.mjs')
|
|
16
|
+
function gea(args) {
|
|
17
|
+
return execFileSync(process.execPath, [geaCli, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'inherit'] })
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const args = process.argv.slice(2)
|
|
21
|
+
|
|
22
|
+
function opt(name) {
|
|
23
|
+
const i = args.indexOf(name)
|
|
24
|
+
return i >= 0 ? args[i + 1] : ''
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function fail(message) {
|
|
28
|
+
process.stderr.write(`${message}\n`)
|
|
29
|
+
process.exit(1)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const appId = opt('--app-id')
|
|
33
|
+
const appName = opt('--app-name')
|
|
34
|
+
const bundleId = opt('--bundle-id')
|
|
35
|
+
const generatedDir = path.resolve(opt('--generated-dir'))
|
|
36
|
+
const projectPath = path.resolve(opt('--project-path'))
|
|
37
|
+
|
|
38
|
+
if (!appId || !appName || !bundleId || !generatedDir || !projectPath) {
|
|
39
|
+
fail('usage: generate-xcode-project.mjs --app-id <id> --app-name <name> --bundle-id <id> --generated-dir <path> --project-path <path> (run from the app directory)')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const iosDir = scriptDir // apple/targets/ios — this script's own directory, not the apps repo
|
|
43
|
+
const plistOut = path.join(projectPath, 'Info.plist')
|
|
44
|
+
let appMeta
|
|
45
|
+
try {
|
|
46
|
+
appMeta = JSON.parse(gea(['apps', 'inspect', appId, '--json']))
|
|
47
|
+
} catch {
|
|
48
|
+
fail(`Unknown app id: ${appId}`)
|
|
49
|
+
}
|
|
50
|
+
// `apps inspect` reports an absolute directory; there is nothing to join it to.
|
|
51
|
+
const appRoot = appMeta.root
|
|
52
|
+
const appIconAssetsDir = path.join(generatedDir, 'Assets.xcassets')
|
|
53
|
+
|
|
54
|
+
// Omit the app-icon asset catalog when GEA_IOS_SKIP_APP_ICON=1. actool refuses to
|
|
55
|
+
// compile a catalog unless an installed simulator runtime's build matches the SDK
|
|
56
|
+
// exactly (e.g. SDK 23F81a needs a 23F81a runtime, not a 23F73 one) — a skew that
|
|
57
|
+
// blocks otherwise-fine builds. Skipping it yields an app with the default icon.
|
|
58
|
+
const skipAppIcon = process.env.GEA_IOS_SKIP_APP_ICON === '1'
|
|
59
|
+
|
|
60
|
+
fs.rmSync(projectPath, { recursive: true, force: true })
|
|
61
|
+
fs.mkdirSync(path.join(projectPath, 'project.xcworkspace'), { recursive: true })
|
|
62
|
+
fs.mkdirSync(path.join(projectPath, 'xcshareddata/xcschemes'), { recursive: true })
|
|
63
|
+
if (!skipAppIcon) gea(['apps', 'apple-icons', appId, '--platform', 'ios', '--assets-dir', appIconAssetsDir])
|
|
64
|
+
|
|
65
|
+
const plistTemplate = fs.readFileSync(path.join(iosDir, 'Info.plist.in'), 'utf8')
|
|
66
|
+
fs.writeFileSync(
|
|
67
|
+
plistOut,
|
|
68
|
+
plistTemplate
|
|
69
|
+
.replaceAll('@APP_EXEC@', appName)
|
|
70
|
+
.replaceAll('@APP_NAME@', appName)
|
|
71
|
+
.replaceAll('@BUNDLE_ID@', bundleId),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
function id(label) {
|
|
75
|
+
return crypto.createHash('sha1').update(label).digest('hex').slice(0, 24).toUpperCase()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function q(value) {
|
|
79
|
+
return `"${String(value).replaceAll('\\', '\\\\').replaceAll('"', '\\"')}"`
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function rel(file) {
|
|
83
|
+
return path.relative(projectDir, file).replaceAll(path.sep, '/')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function source(file, kind = 'sourcecode.cpp.cpp') {
|
|
87
|
+
return { file: path.resolve(file), kind }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function resource(file, kind = 'file') {
|
|
91
|
+
return { file: path.resolve(file), kind }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function existing(entry) {
|
|
95
|
+
return fs.existsSync(entry.file)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function readGeatscSources(dir, kind = 'sourcecode.cpp.cpp') {
|
|
99
|
+
const sourceList = path.join(dir, 'geatsc-sources.txt')
|
|
100
|
+
if (!fs.existsSync(sourceList)) return []
|
|
101
|
+
return fs.readFileSync(sourceList, 'utf8')
|
|
102
|
+
.split(/\r?\n/)
|
|
103
|
+
.map((line) => line.trim())
|
|
104
|
+
.filter(Boolean)
|
|
105
|
+
.map((file) => source(file, kind))
|
|
106
|
+
.filter(existing)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function nativeSourceKind(file) {
|
|
110
|
+
if (/\.mm$/i.test(file)) return 'sourcecode.cpp.objcpp'
|
|
111
|
+
if (/\.m$/i.test(file)) return 'sourcecode.c.objc'
|
|
112
|
+
if (/\.c$/i.test(file)) return 'sourcecode.c.c'
|
|
113
|
+
return 'sourcecode.cpp.cpp'
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function findAppNativeSources() {
|
|
117
|
+
const dir = path.join(appRoot, 'ios')
|
|
118
|
+
if (!fs.existsSync(dir)) return []
|
|
119
|
+
const out = []
|
|
120
|
+
const visit = (current) => {
|
|
121
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
122
|
+
const file = path.join(current, entry.name)
|
|
123
|
+
if (entry.isDirectory()) {
|
|
124
|
+
visit(file)
|
|
125
|
+
} else if (entry.isFile() && /\.(c|cc|cpp|m|mm)$/i.test(entry.name)) {
|
|
126
|
+
out.push(source(file, nativeSourceKind(file)))
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
visit(dir)
|
|
131
|
+
return out.sort((a, b) => a.file.localeCompare(b.file))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function findFontResources() {
|
|
135
|
+
const fontDir = path.join(projectDir, 'assets/fonts')
|
|
136
|
+
if (!fs.existsSync(fontDir)) return []
|
|
137
|
+
return fs.readdirSync(fontDir, { withFileTypes: true })
|
|
138
|
+
.filter((entry) => entry.isFile() && /\.(ttf|otf)$/i.test(entry.name))
|
|
139
|
+
.map((entry) => path.join(fontDir, entry.name))
|
|
140
|
+
.sort()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function findResidentSources() {
|
|
144
|
+
const residentsDir = path.join(generatedDir, 'residents')
|
|
145
|
+
if (!fs.existsSync(residentsDir)) return []
|
|
146
|
+
return fs.readdirSync(residentsDir, { withFileTypes: true })
|
|
147
|
+
.filter((entry) => entry.isDirectory())
|
|
148
|
+
.flatMap((entry) => {
|
|
149
|
+
const dir = path.join(residentsDir, entry.name)
|
|
150
|
+
return [
|
|
151
|
+
...readGeatscSources(dir),
|
|
152
|
+
source(path.join(dir, 'entry.cpp')),
|
|
153
|
+
source(path.join(dir, 'gea_embedded_font_generated.cpp')),
|
|
154
|
+
source(path.join(dir, 'gea_embedded_assets_generated.cpp')),
|
|
155
|
+
].filter(existing)
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const residentRegistry = source(path.join(generatedDir, 'resident_registry.cpp'))
|
|
160
|
+
const hasResidentRegistry = existing(residentRegistry)
|
|
161
|
+
const residentSources = hasResidentRegistry ? [residentRegistry, ...findResidentSources()] : []
|
|
162
|
+
const appleNativeBridgeSource = source(path.join(generatedDir, 'gea/apple/native_bridge.mm'), 'sourcecode.cpp.objcpp')
|
|
163
|
+
const generatedProgramSources = readGeatscSources(
|
|
164
|
+
generatedDir,
|
|
165
|
+
existing(appleNativeBridgeSource) ? 'sourcecode.cpp.objcpp' : 'sourcecode.cpp.cpp',
|
|
166
|
+
)
|
|
167
|
+
const rendererSources = [
|
|
168
|
+
'ios_renderer.mm',
|
|
169
|
+
'renderer/ios_renderer_support.mm',
|
|
170
|
+
'renderer/native_label.mm',
|
|
171
|
+
'renderer/native_button.mm',
|
|
172
|
+
'renderer/native_scroll_container.mm',
|
|
173
|
+
'renderer/view_reconciler.mm',
|
|
174
|
+
].map((file) => source(path.join(iosDir, 'main', file), 'sourcecode.cpp.objcpp'))
|
|
175
|
+
const appNativeSources = findAppNativeSources()
|
|
176
|
+
const sources = [
|
|
177
|
+
source(path.join(iosDir, 'main/ios_main.mm'), 'sourcecode.cpp.objcpp'),
|
|
178
|
+
source(path.join(iosDir, 'main/ios_root_background.cpp')),
|
|
179
|
+
...rendererSources,
|
|
180
|
+
source(path.join(iosDir, 'main/canvas_view.mm'), 'sourcecode.cpp.objcpp'),
|
|
181
|
+
source(path.join(iosDir, 'main/color_convert.mm'), 'sourcecode.cpp.objcpp'),
|
|
182
|
+
source(path.join(iosDir, 'main/image_bridge.mm'), 'sourcecode.cpp.objcpp'),
|
|
183
|
+
source(path.join(iosDir, 'main/font_registry.mm'), 'sourcecode.cpp.objcpp'),
|
|
184
|
+
source(path.join(iosDir, 'main/press_bridge.mm'), 'sourcecode.cpp.objcpp'),
|
|
185
|
+
source(path.join(iosDir, 'main/ios_display.mm'), 'sourcecode.cpp.objcpp'),
|
|
186
|
+
source(path.join(iosDir, 'main/ios_timers.mm'), 'sourcecode.cpp.objcpp'),
|
|
187
|
+
source(path.join(iosDir, 'main/ios_app_platform.mm'), 'sourcecode.cpp.objcpp'),
|
|
188
|
+
source(path.join(iosDir, 'main/ios_memory.cpp')),
|
|
189
|
+
// gea::platform::audio — the AVAudioEngine backend, shared verbatim with macOS.
|
|
190
|
+
source(path.join(iosDir, '../shared/apple_audio.mm'), 'sourcecode.cpp.objcpp'),
|
|
191
|
+
source(path.join(iosDir, 'main/ios_sensors.mm'), 'sourcecode.cpp.objcpp'),
|
|
192
|
+
source(path.join(iosDir, 'main/ios_camera.mm'), 'sourcecode.cpp.objcpp'),
|
|
193
|
+
source(path.join(iosDir, 'main/ios_apps.c'), 'sourcecode.c.c'),
|
|
194
|
+
...appNativeSources,
|
|
195
|
+
// Framework C/C++ sources come from @geastack/core/gea_sources.sh,
|
|
196
|
+
// passed by build-ios.sh via env (iOS-curated: camera kept since ios_camera.mm
|
|
197
|
+
// provides it; resident-app/OTA/diagnostics services + the single-app runtime
|
|
198
|
+
// shell excluded — iOS uses its own UIKit run loop). Single source of truth.
|
|
199
|
+
...(process.env.GEA_FW_C_SOURCES || '').split('\n').filter(Boolean).map((f) => source(f, 'sourcecode.c.c')),
|
|
200
|
+
...(process.env.GEA_FW_CXX_SOURCES || '').split('\n').filter(Boolean).map((f) => source(f)),
|
|
201
|
+
// gea_app_entry / resident_apps are mode-specific (deliberately not in the manifest).
|
|
202
|
+
...(hasResidentRegistry
|
|
203
|
+
? []
|
|
204
|
+
: [
|
|
205
|
+
source(path.join(geaCore, 'gea_app_entry.cpp')),
|
|
206
|
+
source(path.join(geaCore, '../geaos/resident_apps.cpp')),
|
|
207
|
+
]),
|
|
208
|
+
...(hasResidentRegistry ? residentSources : generatedProgramSources),
|
|
209
|
+
appleNativeBridgeSource,
|
|
210
|
+
].filter(existing)
|
|
211
|
+
|
|
212
|
+
const fontGenerated = source(path.join(generatedDir, 'gea_embedded_font_generated.cpp'))
|
|
213
|
+
if (!hasResidentRegistry && existing(fontGenerated)) sources.push(fontGenerated)
|
|
214
|
+
const assetsGenerated = source(path.join(generatedDir, 'gea_embedded_assets_generated.cpp'))
|
|
215
|
+
if (!hasResidentRegistry && existing(assetsGenerated)) sources.push(assetsGenerated)
|
|
216
|
+
const hasGeneratedFonts = sources.some((entry) => path.basename(entry.file) === 'gea_embedded_font_generated.cpp')
|
|
217
|
+
|
|
218
|
+
const fontResources = findFontResources().map((file) => resource(file))
|
|
219
|
+
const appIconAssets = resource(appIconAssetsDir, 'folder.assetcatalog')
|
|
220
|
+
const appIconResources = skipAppIcon ? [] : [appIconAssets]
|
|
221
|
+
const uiTestSource = source(path.join(iosDir, 'test/GeaIosRubberBandUITests.swift'), 'sourcecode.swift')
|
|
222
|
+
const uiTestSources = existing(uiTestSource) ? [uiTestSource] : []
|
|
223
|
+
const hasUiTests = uiTestSources.length > 0
|
|
224
|
+
|
|
225
|
+
const frameworks = [
|
|
226
|
+
'UIKit.framework',
|
|
227
|
+
'QuartzCore.framework',
|
|
228
|
+
'CoreGraphics.framework',
|
|
229
|
+
'CoreText.framework',
|
|
230
|
+
'CoreMotion.framework',
|
|
231
|
+
'CoreLocation.framework',
|
|
232
|
+
'CoreMedia.framework',
|
|
233
|
+
'CoreVideo.framework',
|
|
234
|
+
'ImageIO.framework',
|
|
235
|
+
'MapKit.framework',
|
|
236
|
+
'AVFoundation.framework',
|
|
237
|
+
'Photos.framework',
|
|
238
|
+
'Metal.framework',
|
|
239
|
+
'MetalKit.framework',
|
|
240
|
+
'Foundation.framework',
|
|
241
|
+
]
|
|
242
|
+
|
|
243
|
+
const productId = id('product')
|
|
244
|
+
const targetId = id('target')
|
|
245
|
+
const projectId = id('project')
|
|
246
|
+
const mainGroupId = id('group-main')
|
|
247
|
+
const productGroupId = id('group-products')
|
|
248
|
+
const sourcesPhaseId = id('phase-sources')
|
|
249
|
+
const frameworksPhaseId = id('phase-frameworks')
|
|
250
|
+
const resourcesPhaseId = id('phase-resources')
|
|
251
|
+
const configListProjectId = id('configs-project')
|
|
252
|
+
const configListTargetId = id('configs-target')
|
|
253
|
+
const debugProjectId = id('debug-project')
|
|
254
|
+
const releaseProjectId = id('release-project')
|
|
255
|
+
const debugTargetId = id('debug-target')
|
|
256
|
+
const releaseTargetId = id('release-target')
|
|
257
|
+
const uiTestProductId = id('product-ui-tests')
|
|
258
|
+
const uiTestTargetId = id('target-ui-tests')
|
|
259
|
+
const uiTestSourcesPhaseId = id('phase-ui-test-sources')
|
|
260
|
+
const uiTestFrameworksPhaseId = id('phase-ui-test-frameworks')
|
|
261
|
+
const uiTestConfigListId = id('configs-ui-test-target')
|
|
262
|
+
const uiTestDebugId = id('debug-ui-test-target')
|
|
263
|
+
const uiTestReleaseId = id('release-ui-test-target')
|
|
264
|
+
const uiTestDependencyId = id('dependency-ui-test-app')
|
|
265
|
+
const uiTestProxyId = id('proxy-ui-test-app')
|
|
266
|
+
|
|
267
|
+
const fileRefs = []
|
|
268
|
+
const buildFiles = []
|
|
269
|
+
|
|
270
|
+
for (const s of sources) {
|
|
271
|
+
const label = rel(s.file)
|
|
272
|
+
const compilerSettings = s.kind === 'sourcecode.cpp.objcpp'
|
|
273
|
+
? ' settings = {COMPILER_FLAGS = "-fobjc-exceptions"; };'
|
|
274
|
+
: ''
|
|
275
|
+
fileRefs.push(`${id(`file:${label}`)} /* ${path.basename(s.file)} */ = {isa = PBXFileReference; lastKnownFileType = ${s.kind}; path = ${q(s.file)}; sourceTree = "<absolute>"; };`)
|
|
276
|
+
buildFiles.push(`${id(`build:${label}`)} /* ${path.basename(s.file)} in Sources */ = {isa = PBXBuildFile; fileRef = ${id(`file:${label}`)} /* ${path.basename(s.file)} */;${compilerSettings} };`)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
for (const r of fontResources) {
|
|
280
|
+
const label = rel(r.file)
|
|
281
|
+
fileRefs.push(`${id(`file:${label}`)} /* ${path.basename(r.file)} */ = {isa = PBXFileReference; lastKnownFileType = ${r.kind}; path = ${q(r.file)}; sourceTree = "<absolute>"; };`)
|
|
282
|
+
buildFiles.push(`${id(`build:${label}`)} /* ${path.basename(r.file)} in Resources */ = {isa = PBXBuildFile; fileRef = ${id(`file:${label}`)} /* ${path.basename(r.file)} */; };`)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
for (const a of appIconResources) {
|
|
286
|
+
const label = rel(a.file)
|
|
287
|
+
fileRefs.push(`${id(`file:${label}`)} /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = ${a.kind}; path = ${q(a.file)}; sourceTree = "<absolute>"; };`)
|
|
288
|
+
buildFiles.push(`${id(`build:${label}`)} /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ${id(`file:${label}`)} /* Assets.xcassets */; };`)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
for (const fw of frameworks) {
|
|
292
|
+
fileRefs.push(`${id(`file:${fw}`)} /* ${fw} */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ${fw}; path = System/Library/Frameworks/${fw}; sourceTree = SDKROOT; };`)
|
|
293
|
+
buildFiles.push(`${id(`build:${fw}`)} /* ${fw} in Frameworks */ = {isa = PBXBuildFile; fileRef = ${id(`file:${fw}`)} /* ${fw} */; };`)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
for (const s of uiTestSources) {
|
|
297
|
+
const label = rel(s.file)
|
|
298
|
+
fileRefs.push(`${id(`file:${label}`)} /* ${path.basename(s.file)} */ = {isa = PBXFileReference; lastKnownFileType = ${s.kind}; path = ${q(s.file)}; sourceTree = "<absolute>"; };`)
|
|
299
|
+
buildFiles.push(`${id(`build:${label}`)} /* ${path.basename(s.file)} in Sources */ = {isa = PBXBuildFile; fileRef = ${id(`file:${label}`)} /* ${path.basename(s.file)} */; };`)
|
|
300
|
+
}
|
|
301
|
+
if (hasUiTests) {
|
|
302
|
+
fileRefs.push(`${id('file:XCTest.framework')} /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = System/Library/Frameworks/XCTest.framework; sourceTree = SDKROOT; };`)
|
|
303
|
+
buildFiles.push(`${id('build:XCTest.framework')} /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ${id('file:XCTest.framework')} /* XCTest.framework */; };`)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const productRef = `${productId} /* ${appName}.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ${q(`${appName}.app`)}; sourceTree = BUILT_PRODUCTS_DIR; };`
|
|
307
|
+
const uiTestProductRef = hasUiTests
|
|
308
|
+
? `${uiTestProductId} /* GeaIosRubberBandUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GeaIosRubberBandUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };`
|
|
309
|
+
: ''
|
|
310
|
+
const includePaths = [
|
|
311
|
+
path.join(iosDir, 'main'),
|
|
312
|
+
generatedDir,
|
|
313
|
+
path.join(appRoot, 'ios'),
|
|
314
|
+
// Framework include roots (core/host/engine includes + vendor/stb + AnimatedGIF)
|
|
315
|
+
// from the shared manifest gea_sources.sh, passed by build-ios.sh via env.
|
|
316
|
+
...(process.env.GEA_FW_INCLUDE_DIRS || '').split('\n').filter(Boolean),
|
|
317
|
+
]
|
|
318
|
+
|
|
319
|
+
function buildSettings(configuration) {
|
|
320
|
+
const preprocessorDefinitions = [
|
|
321
|
+
'GEA_EMBEDDED_GIF_C_API=1',
|
|
322
|
+
'GEA_EMBEDDED_ENABLE_NATIVE_TEXT_INPUT=1',
|
|
323
|
+
'GEA_EMBEDDED_ENABLE_VIRTUAL_KEYBOARD=0',
|
|
324
|
+
'GEA_CPP_USE_TO_CHARS_DOUBLE=0',
|
|
325
|
+
'GEA_CPP_USE_FROM_CHARS_DOUBLE=0',
|
|
326
|
+
// iOS renders full colour: pixel::native_t is RGBA8888 (32-bit), not RGB565.
|
|
327
|
+
// 1 == GEA_PIXEL_RGBA8888 (see pixel.h). The Canvas engine, the primary
|
|
328
|
+
// display path (ios_display.mm's uint32 framebuffer → gea_ios_display_presented_pixels),
|
|
329
|
+
// and the shared text/canvas-store/camera paths all operate on the 32-bit
|
|
330
|
+
// native pixel with no runtime format branch and no colour conversion.
|
|
331
|
+
'GEA_EMBEDDED_PIXEL_FORMAT=1',
|
|
332
|
+
...(hasGeneratedFonts ? ['GEA_EMBEDDED_HAS_GENERATED_FONTS=1'] : []),
|
|
333
|
+
]
|
|
334
|
+
return `{
|
|
335
|
+
ALWAYS_SEARCH_USER_PATHS = NO;${skipAppIcon ? '' : `
|
|
336
|
+
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;`}
|
|
337
|
+
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
|
338
|
+
CLANG_CXX_LIBRARY = "libc++";
|
|
339
|
+
CLANG_ENABLE_MODULES = YES;
|
|
340
|
+
CLANG_ENABLE_OBJC_ARC = YES;
|
|
341
|
+
CODE_SIGN_STYLE = Automatic;
|
|
342
|
+
CURRENT_PROJECT_VERSION = 1;
|
|
343
|
+
DEFINES_MODULE = NO;
|
|
344
|
+
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
|
345
|
+
GCC_PREPROCESSOR_DEFINITIONS = (${preprocessorDefinitions.map(q).join(', ')});
|
|
346
|
+
HEADER_SEARCH_PATHS = (${includePaths.map(q).join(', ')});
|
|
347
|
+
INFOPLIST_FILE = ${q(plistOut)};
|
|
348
|
+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
|
349
|
+
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
|
350
|
+
ONLY_ACTIVE_ARCH = ${configuration === 'Debug' ? 'YES' : 'NO'};
|
|
351
|
+
PRODUCT_BUNDLE_IDENTIFIER = ${q(bundleId)};
|
|
352
|
+
PRODUCT_NAME = ${q(appName)};
|
|
353
|
+
SDKROOT = iphoneos;
|
|
354
|
+
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
|
355
|
+
SUPPORTS_MACCATALYST = NO;
|
|
356
|
+
TARGETED_DEVICE_FAMILY = "1,2";
|
|
357
|
+
}`
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function uiTestBuildSettings(configuration) {
|
|
361
|
+
return `{
|
|
362
|
+
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
363
|
+
CLANG_ENABLE_MODULES = YES;
|
|
364
|
+
CODE_SIGN_STYLE = Automatic;
|
|
365
|
+
GENERATE_INFOPLIST_FILE = YES;
|
|
366
|
+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
|
367
|
+
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
|
368
|
+
ONLY_ACTIVE_ARCH = ${configuration === 'Debug' ? 'YES' : 'NO'};
|
|
369
|
+
PRODUCT_BUNDLE_IDENTIFIER = ${q(`${bundleId}.rubber-band-ui-tests`)};
|
|
370
|
+
PRODUCT_MODULE_NAME = GeaIosRubberBandUITests;
|
|
371
|
+
PRODUCT_NAME = GeaIosRubberBandUITests;
|
|
372
|
+
SDKROOT = iphoneos;
|
|
373
|
+
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
|
374
|
+
SUPPORTS_MACCATALYST = NO;
|
|
375
|
+
SWIFT_VERSION = 5.0;
|
|
376
|
+
TARGETED_DEVICE_FAMILY = "1,2";
|
|
377
|
+
TEST_TARGET_NAME = GeaIos;
|
|
378
|
+
}`
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const sourceChildren = sources.map((s) => `${id(`file:${rel(s.file)}`)} /* ${path.basename(s.file)} */`)
|
|
382
|
+
const uiTestSourceChildren = uiTestSources.map((s) => `${id(`file:${rel(s.file)}`)} /* ${path.basename(s.file)} */`)
|
|
383
|
+
const resourceChildren = [
|
|
384
|
+
...fontResources.map((r) => `${id(`file:${rel(r.file)}`)} /* ${path.basename(r.file)} */`),
|
|
385
|
+
...appIconResources.map((a) => `${id(`file:${rel(a.file)}`)} /* Assets.xcassets */`),
|
|
386
|
+
]
|
|
387
|
+
const frameworkChildren = frameworks.map((fw) => `${id(`file:${fw}`)} /* ${fw} */`)
|
|
388
|
+
const mainGroupChildren = [
|
|
389
|
+
...sourceChildren,
|
|
390
|
+
...uiTestSourceChildren,
|
|
391
|
+
...resourceChildren,
|
|
392
|
+
...frameworkChildren,
|
|
393
|
+
...(hasUiTests ? [`${id('file:XCTest.framework')} /* XCTest.framework */`] : []),
|
|
394
|
+
`${productGroupId} /* Products */`,
|
|
395
|
+
]
|
|
396
|
+
const sourceBuildRefs = sources.map((s) => `${id(`build:${rel(s.file)}`)} /* ${path.basename(s.file)} in Sources */`)
|
|
397
|
+
const uiTestSourceBuildRefs = uiTestSources.map((s) => `${id(`build:${rel(s.file)}`)} /* ${path.basename(s.file)} in Sources */`)
|
|
398
|
+
const resourceBuildRefs = [
|
|
399
|
+
...fontResources.map((r) => `${id(`build:${rel(r.file)}`)} /* ${path.basename(r.file)} in Resources */`),
|
|
400
|
+
...appIconResources.map((a) => `${id(`build:${rel(a.file)}`)} /* Assets.xcassets in Resources */`),
|
|
401
|
+
]
|
|
402
|
+
const frameworkBuildRefs = frameworks.map((fw) => `${id(`build:${fw}`)} /* ${fw} in Frameworks */`)
|
|
403
|
+
const uiTestFrameworkBuildRefs = hasUiTests ? [`${id('build:XCTest.framework')} /* XCTest.framework in Frameworks */`] : []
|
|
404
|
+
|
|
405
|
+
const pbxproj = `// !$*UTF8*$!
|
|
406
|
+
{
|
|
407
|
+
archiveVersion = 1;
|
|
408
|
+
classes = {};
|
|
409
|
+
objectVersion = 56;
|
|
410
|
+
objects = {
|
|
411
|
+
|
|
412
|
+
/* Begin PBXBuildFile section */
|
|
413
|
+
${buildFiles.join('\n\t\t')}
|
|
414
|
+
/* End PBXBuildFile section */
|
|
415
|
+
|
|
416
|
+
/* Begin PBXFileReference section */
|
|
417
|
+
${fileRefs.join('\n\t\t')}
|
|
418
|
+
${productRef}
|
|
419
|
+
${uiTestProductRef}
|
|
420
|
+
/* End PBXFileReference section */
|
|
421
|
+
|
|
422
|
+
/* Begin PBXFrameworksBuildPhase section */
|
|
423
|
+
${frameworksPhaseId} = {isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = (${frameworkBuildRefs.join(', ')}); runOnlyForDeploymentPostprocessing = 0; };
|
|
424
|
+
${hasUiTests ? `${uiTestFrameworksPhaseId} = {isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = (${uiTestFrameworkBuildRefs.join(', ')}); runOnlyForDeploymentPostprocessing = 0; };` : ''}
|
|
425
|
+
/* End PBXFrameworksBuildPhase section */
|
|
426
|
+
|
|
427
|
+
/* Begin PBXGroup section */
|
|
428
|
+
${mainGroupId} = {isa = PBXGroup; children = (${mainGroupChildren.join(', ')}); sourceTree = "<group>"; };
|
|
429
|
+
${productGroupId} = {isa = PBXGroup; children = (${[`${productId} /* ${appName}.app */`, ...(hasUiTests ? [`${uiTestProductId} /* GeaIosRubberBandUITests.xctest */`] : [])].join(', ')}); name = Products; sourceTree = "<group>"; };
|
|
430
|
+
/* End PBXGroup section */
|
|
431
|
+
|
|
432
|
+
${hasUiTests ? `/* Begin PBXContainerItemProxy section */
|
|
433
|
+
${uiTestProxyId} = {isa = PBXContainerItemProxy; containerPortal = ${projectId} /* Project object */; proxyType = 1; remoteGlobalIDString = ${targetId}; remoteInfo = GeaIos; };
|
|
434
|
+
/* End PBXContainerItemProxy section */` : ''}
|
|
435
|
+
|
|
436
|
+
/* Begin PBXNativeTarget section */
|
|
437
|
+
${targetId} = {isa = PBXNativeTarget; buildConfigurationList = ${configListTargetId}; buildPhases = (${sourcesPhaseId}, ${frameworksPhaseId}, ${resourcesPhaseId}); buildRules = (); dependencies = (); name = GeaIos; productName = ${q(appName)}; productReference = ${productId} /* ${appName}.app */; productType = "com.apple.product-type.application"; };
|
|
438
|
+
${hasUiTests ? `${uiTestTargetId} = {isa = PBXNativeTarget; buildConfigurationList = ${uiTestConfigListId}; buildPhases = (${uiTestSourcesPhaseId}, ${uiTestFrameworksPhaseId}); buildRules = (); dependencies = (${uiTestDependencyId}); name = GeaIosRubberBandUITests; productName = GeaIosRubberBandUITests; productReference = ${uiTestProductId} /* GeaIosRubberBandUITests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; };` : ''}
|
|
439
|
+
/* End PBXNativeTarget section */
|
|
440
|
+
|
|
441
|
+
/* Begin PBXProject section */
|
|
442
|
+
${projectId} = {isa = PBXProject; attributes = {LastUpgradeCheck = 1600; TargetAttributes = {${targetId} = {CreatedOnToolsVersion = 16.0;};${hasUiTests ? ` ${uiTestTargetId} = {CreatedOnToolsVersion = 16.0; TestTargetID = ${targetId};};` : ''}};}; buildConfigurationList = ${configListProjectId}; compatibilityVersion = "Xcode 14.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = (en, Base); mainGroup = ${mainGroupId}; productRefGroup = ${productGroupId}; projectDirPath = ""; projectRoot = ""; targets = (${[targetId, ...(hasUiTests ? [uiTestTargetId] : [])].join(', ')}); };
|
|
443
|
+
/* End PBXProject section */
|
|
444
|
+
|
|
445
|
+
/* Begin PBXResourcesBuildPhase section */
|
|
446
|
+
${resourcesPhaseId} = {isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = (${resourceBuildRefs.join(', ')}); runOnlyForDeploymentPostprocessing = 0; };
|
|
447
|
+
/* End PBXResourcesBuildPhase section */
|
|
448
|
+
|
|
449
|
+
/* Begin PBXSourcesBuildPhase section */
|
|
450
|
+
${sourcesPhaseId} = {isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = (${sourceBuildRefs.join(', ')}); runOnlyForDeploymentPostprocessing = 0; };
|
|
451
|
+
${hasUiTests ? `${uiTestSourcesPhaseId} = {isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = (${uiTestSourceBuildRefs.join(', ')}); runOnlyForDeploymentPostprocessing = 0; };` : ''}
|
|
452
|
+
/* End PBXSourcesBuildPhase section */
|
|
453
|
+
|
|
454
|
+
${hasUiTests ? `/* Begin PBXTargetDependency section */
|
|
455
|
+
${uiTestDependencyId} = {isa = PBXTargetDependency; target = ${targetId} /* GeaIos */; targetProxy = ${uiTestProxyId}; };
|
|
456
|
+
/* End PBXTargetDependency section */` : ''}
|
|
457
|
+
|
|
458
|
+
/* Begin XCBuildConfiguration section */
|
|
459
|
+
${debugProjectId} = {isa = XCBuildConfiguration; buildSettings = {}; name = Debug; };
|
|
460
|
+
${releaseProjectId} = {isa = XCBuildConfiguration; buildSettings = {}; name = Release; };
|
|
461
|
+
${debugTargetId} = {isa = XCBuildConfiguration; buildSettings = ${buildSettings('Debug')}; name = Debug; };
|
|
462
|
+
${releaseTargetId} = {isa = XCBuildConfiguration; buildSettings = ${buildSettings('Release')}; name = Release; };
|
|
463
|
+
${hasUiTests ? `${uiTestDebugId} = {isa = XCBuildConfiguration; buildSettings = ${uiTestBuildSettings('Debug')}; name = Debug; };
|
|
464
|
+
${uiTestReleaseId} = {isa = XCBuildConfiguration; buildSettings = ${uiTestBuildSettings('Release')}; name = Release; };` : ''}
|
|
465
|
+
/* End XCBuildConfiguration section */
|
|
466
|
+
|
|
467
|
+
/* Begin XCConfigurationList section */
|
|
468
|
+
${configListProjectId} = {isa = XCConfigurationList; buildConfigurations = (${debugProjectId}, ${releaseProjectId}); defaultConfigurationIsVisible = 0; defaultConfigurationName = Debug; };
|
|
469
|
+
${configListTargetId} = {isa = XCConfigurationList; buildConfigurations = (${debugTargetId}, ${releaseTargetId}); defaultConfigurationIsVisible = 0; defaultConfigurationName = Debug; };
|
|
470
|
+
${hasUiTests ? `${uiTestConfigListId} = {isa = XCConfigurationList; buildConfigurations = (${uiTestDebugId}, ${uiTestReleaseId}); defaultConfigurationIsVisible = 0; defaultConfigurationName = Debug; };` : ''}
|
|
471
|
+
/* End XCConfigurationList section */
|
|
472
|
+
};
|
|
473
|
+
rootObject = ${projectId};
|
|
474
|
+
}
|
|
475
|
+
`
|
|
476
|
+
|
|
477
|
+
const scheme = `<?xml version="1.0" encoding="UTF-8"?>
|
|
478
|
+
<Scheme LastUpgradeVersion="1600" version="1.7">
|
|
479
|
+
<BuildAction parallelizeBuildables="YES" buildImplicitDependencies="YES" buildArchitectures="Automatic">
|
|
480
|
+
<BuildActionEntries>
|
|
481
|
+
<BuildActionEntry buildForTesting="YES" buildForRunning="YES" buildForProfiling="YES" buildForArchiving="YES" buildForAnalyzing="YES">
|
|
482
|
+
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="${targetId}" BuildableName="${appName}.app" BlueprintName="GeaIos" ReferencedContainer="container:GeaIos.xcodeproj"/>
|
|
483
|
+
</BuildActionEntry>
|
|
484
|
+
${hasUiTests ? `<BuildActionEntry buildForTesting="YES" buildForRunning="NO" buildForProfiling="NO" buildForArchiving="NO" buildForAnalyzing="NO">
|
|
485
|
+
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="${uiTestTargetId}" BuildableName="GeaIosRubberBandUITests.xctest" BlueprintName="GeaIosRubberBandUITests" ReferencedContainer="container:GeaIos.xcodeproj"/>
|
|
486
|
+
</BuildActionEntry>` : ''}
|
|
487
|
+
</BuildActionEntries>
|
|
488
|
+
</BuildAction>
|
|
489
|
+
${hasUiTests ? `<TestAction buildConfiguration="Debug" selectedDebuggerIdentifier="Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier="Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv="YES" shouldAutocreateTestPlan="YES">
|
|
490
|
+
<Testables>
|
|
491
|
+
<TestableReference skipped="NO">
|
|
492
|
+
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="${uiTestTargetId}" BuildableName="GeaIosRubberBandUITests.xctest" BlueprintName="GeaIosRubberBandUITests" ReferencedContainer="container:GeaIos.xcodeproj"/>
|
|
493
|
+
</TestableReference>
|
|
494
|
+
</Testables>
|
|
495
|
+
<MacroExpansion>
|
|
496
|
+
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="${targetId}" BuildableName="${appName}.app" BlueprintName="GeaIos" ReferencedContainer="container:GeaIos.xcodeproj"/>
|
|
497
|
+
</MacroExpansion>
|
|
498
|
+
</TestAction>` : ''}
|
|
499
|
+
<LaunchAction buildConfiguration="Debug" selectedDebuggerIdentifier="Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier="Xcode.DebuggerFoundation.Launcher.LLDB" launchStyle="0" useCustomWorkingDirectory="NO" ignoresPersistentStateOnLaunch="NO" debugDocumentVersioning="YES" debugServiceExtension="internal" allowLocationSimulation="YES">
|
|
500
|
+
<BuildableProductRunnable runnableDebuggingMode="0">
|
|
501
|
+
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="${targetId}" BuildableName="${appName}.app" BlueprintName="GeaIos" ReferencedContainer="container:GeaIos.xcodeproj"/>
|
|
502
|
+
</BuildableProductRunnable>
|
|
503
|
+
</LaunchAction>
|
|
504
|
+
<ProfileAction buildConfiguration="Release" shouldUseLaunchSchemeArgsEnv="YES" savedToolIdentifier="" useCustomWorkingDirectory="NO" debugDocumentVersioning="YES">
|
|
505
|
+
<BuildableProductRunnable runnableDebuggingMode="0">
|
|
506
|
+
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="${targetId}" BuildableName="${appName}.app" BlueprintName="GeaIos" ReferencedContainer="container:GeaIos.xcodeproj"/>
|
|
507
|
+
</BuildableProductRunnable>
|
|
508
|
+
</ProfileAction>
|
|
509
|
+
<AnalyzeAction buildConfiguration="Debug"/>
|
|
510
|
+
<ArchiveAction buildConfiguration="Release" revealArchiveInOrganizer="YES"/>
|
|
511
|
+
</Scheme>
|
|
512
|
+
`
|
|
513
|
+
|
|
514
|
+
fs.writeFileSync(path.join(projectPath, 'project.pbxproj'), pbxproj)
|
|
515
|
+
fs.writeFileSync(
|
|
516
|
+
path.join(projectPath, 'project.xcworkspace/contents.xcworkspacedata'),
|
|
517
|
+
'<?xml version="1.0" encoding="UTF-8"?>\n<Workspace version="1.0"><FileRef location="self:"></FileRef></Workspace>\n',
|
|
518
|
+
)
|
|
519
|
+
fs.writeFileSync(path.join(projectPath, 'xcshareddata/xcschemes/GeaIos.xcscheme'), scheme)
|
|
520
|
+
console.log(`Generated ${projectPath}`)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#import "canvas_view.h"
|
|
2
|
+
|
|
3
|
+
#include "canvas.h"
|
|
4
|
+
#include "pixel.h"
|
|
5
|
+
#include "ui/tree_internal.h"
|
|
6
|
+
|
|
7
|
+
namespace {
|
|
8
|
+
|
|
9
|
+
std::uint8_t *g_scratch = nullptr;
|
|
10
|
+
std::size_t g_scratchBytes = 0;
|
|
11
|
+
|
|
12
|
+
std::uint8_t *ensureScratch(std::size_t bytes)
|
|
13
|
+
{
|
|
14
|
+
if (g_scratchBytes < bytes) {
|
|
15
|
+
void *grown = std::realloc(g_scratch, bytes);
|
|
16
|
+
if (!grown) return nullptr;
|
|
17
|
+
g_scratch = static_cast<std::uint8_t *>(grown);
|
|
18
|
+
g_scratchBytes = bytes;
|
|
19
|
+
}
|
|
20
|
+
return g_scratch;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
} // namespace
|
|
24
|
+
|
|
25
|
+
@implementation GeaCanvasView
|
|
26
|
+
|
|
27
|
+
- (instancetype)initWithFrame:(CGRect)frame
|
|
28
|
+
{
|
|
29
|
+
self = [super initWithFrame:frame];
|
|
30
|
+
if (self) {
|
|
31
|
+
_nodeId = -1;
|
|
32
|
+
self.opaque = NO;
|
|
33
|
+
self.backgroundColor = UIColor.clearColor;
|
|
34
|
+
self.userInteractionEnabled = NO;
|
|
35
|
+
}
|
|
36
|
+
return self;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
- (void)drawRect:(CGRect)dirtyRect
|
|
40
|
+
{
|
|
41
|
+
(void)dirtyRect;
|
|
42
|
+
if (self.nodeId < 0) return;
|
|
43
|
+
auto &tree = gea::embedded::ui::Tree::instance();
|
|
44
|
+
if (self.nodeId >= tree.nodeCount()) return;
|
|
45
|
+
const auto *canvas = gea::embedded::ui::Tree::instance().canvas(self.nodeId);
|
|
46
|
+
if (!canvas) return;
|
|
47
|
+
const gea::framework::graphics::pixel::native_t *src = canvas->pixels();
|
|
48
|
+
const int w = canvas->width();
|
|
49
|
+
const int h = canvas->height();
|
|
50
|
+
if (!src || w <= 0 || h <= 0) return;
|
|
51
|
+
|
|
52
|
+
const std::size_t bytes = static_cast<std::size_t>(w) * h * 4;
|
|
53
|
+
std::uint8_t *dst = ensureScratch(bytes);
|
|
54
|
+
if (!dst) return;
|
|
55
|
+
#if GEA_EMBEDDED_PIXEL_FORMAT == GEA_PIXEL_RGBA8888
|
|
56
|
+
// native_t is already RGBA8888 (R,G,B,A bytes per pixel) — exactly the
|
|
57
|
+
// CGImage's RGBX layout — so present it straight through.
|
|
58
|
+
std::memcpy(dst, src, bytes);
|
|
59
|
+
#else
|
|
60
|
+
for (int i = 0; i < w * h; i++) {
|
|
61
|
+
int r, g, b;
|
|
62
|
+
gea::framework::graphics::pixel::unpackRgb565(src[i], &r, &g, &b);
|
|
63
|
+
dst[i * 4 + 0] = static_cast<std::uint8_t>((r * 255 + 15) / 31);
|
|
64
|
+
dst[i * 4 + 1] = static_cast<std::uint8_t>((g * 255 + 31) / 63);
|
|
65
|
+
dst[i * 4 + 2] = static_cast<std::uint8_t>((b * 255 + 15) / 31);
|
|
66
|
+
dst[i * 4 + 3] = 0xFF;
|
|
67
|
+
}
|
|
68
|
+
#endif
|
|
69
|
+
|
|
70
|
+
CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
|
|
71
|
+
CGDataProviderRef provider = CGDataProviderCreateWithData(nullptr, dst, bytes, nullptr);
|
|
72
|
+
const uint32_t bitmapInfo = static_cast<uint32_t>(kCGImageAlphaNoneSkipLast) |
|
|
73
|
+
static_cast<uint32_t>(kCGBitmapByteOrder32Big);
|
|
74
|
+
CGImageRef image = CGImageCreate(static_cast<std::size_t>(w),
|
|
75
|
+
static_cast<std::size_t>(h),
|
|
76
|
+
8,
|
|
77
|
+
32,
|
|
78
|
+
static_cast<std::size_t>(w) * 4,
|
|
79
|
+
cs,
|
|
80
|
+
bitmapInfo,
|
|
81
|
+
provider,
|
|
82
|
+
nullptr,
|
|
83
|
+
false,
|
|
84
|
+
kCGRenderingIntentDefault);
|
|
85
|
+
if (image) {
|
|
86
|
+
UIImage *uiImage = [UIImage imageWithCGImage:image scale:1.0 orientation:UIImageOrientationUp];
|
|
87
|
+
[uiImage drawInRect:self.bounds];
|
|
88
|
+
CGImageRelease(image);
|
|
89
|
+
}
|
|
90
|
+
CGDataProviderRelease(provider);
|
|
91
|
+
CGColorSpaceRelease(cs);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#ifdef __OBJC__
|
|
4
|
+
#import <UIKit/UIKit.h>
|
|
5
|
+
@class UIFont;
|
|
6
|
+
#endif
|
|
7
|
+
|
|
8
|
+
namespace gea::ios {
|
|
9
|
+
|
|
10
|
+
#ifdef __OBJC__
|
|
11
|
+
// Returns a UIKit font for the generated framework font-family id. The id is
|
|
12
|
+
// resolved back to the CSS family name, then matched against fonts registered
|
|
13
|
+
// from the app bundle. Unknown ids fall back to the system font.
|
|
14
|
+
UIFont *fontForId(int fontId, CGFloat sizePt);
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
} // namespace gea::ios
|