ranger-compiler 3.2.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +2463 -0
  2. package/LICENSE +28 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +1651 -1895
  5. package/dist/Lang.rgr +10946 -5852
  6. package/dist/README.md +3 -2
  7. package/dist/api.d.ts +1548 -27
  8. package/dist/api.js +70572 -38295
  9. package/dist/git-http.mjs +126 -0
  10. package/dist/lib/ACEEditor.rgr +2 -0
  11. package/dist/lib/Ajax.rgr +2 -0
  12. package/dist/lib/CmdParams.rgr +2 -0
  13. package/dist/lib/Crypto.rgr +2 -0
  14. package/dist/lib/DOMLib.rgr +2 -0
  15. package/dist/lib/Engine3D.rgr +2 -0
  16. package/dist/lib/ImmutableVector.rgr +2 -0
  17. package/dist/lib/IndexedDB.rgr +2 -0
  18. package/dist/lib/IsoDate/DateMath.rgr +2 -0
  19. package/dist/lib/IsoDate/IsoCalendar.rgr +2 -0
  20. package/dist/lib/IsoDate/IsoDateParse.rgr +2 -0
  21. package/dist/lib/IsoDateLib.rgr +2 -0
  22. package/dist/lib/JSON.rgr +4906 -48
  23. package/dist/lib/JinxProcess.rgr +2 -0
  24. package/dist/lib/RangerProcess.rgr +2 -0
  25. package/dist/lib/Regex/RegexMatch.rgr +2 -0
  26. package/dist/lib/RegexLib.rgr +2 -0
  27. package/dist/lib/SQL.rgr +2 -0
  28. package/dist/lib/ServiceLib.rgr +2 -0
  29. package/dist/lib/Shell.rgr +326 -0
  30. package/dist/lib/Storage.rgr +2 -0
  31. package/dist/lib/Time.rgr +2 -0
  32. package/dist/lib/Timers.rgr +2 -0
  33. package/dist/lib/TypedArrays.rgr +2 -0
  34. package/dist/lib/ViewLib.rgr +2 -0
  35. package/dist/lib/WebLib.rgr +2 -0
  36. package/dist/lib/WebServerLib.rgr +2 -0
  37. package/dist/lib/apple/AppleAppBuilder.rgr +572 -0
  38. package/dist/lib/apple/AppleAppSpec.rgr +201 -0
  39. package/dist/lib/apple/AppleDevice.rgr +295 -0
  40. package/dist/lib/apple/AppleDeviceDoctor.rgr +453 -0
  41. package/dist/lib/apple/AppleSigning.rgr +379 -0
  42. package/dist/lib/apple/AppleSimulator.rgr +248 -0
  43. package/dist/lib/apple/AppleTarget.rgr +185 -0
  44. package/dist/lib/apple/AppleToolchain.rgr +458 -0
  45. package/dist/lib/apple/README.md +311 -0
  46. package/dist/lib/apple/apple_test.rgr +669 -0
  47. package/dist/lib/core/README.md +251 -0
  48. package/dist/lib/core/RgBase.rgr +313 -0
  49. package/dist/lib/core/RgNum.rgr +653 -0
  50. package/dist/lib/core/RgText.rgr +680 -0
  51. package/dist/lib/core/RgU32.rgr +309 -0
  52. package/dist/lib/ranger-dir.rgr +2 -0
  53. package/dist/lib/shell_test.rgr +193 -0
  54. package/dist/lib/stdlib.rgr +1176 -665
  55. package/dist/lib/stdops.rgr +2 -0
  56. package/dist/lib/zip/Inflate.rgr +675 -0
  57. package/dist/lib/zip/ZipBuffer.rgr +358 -0
  58. package/dist/package.json +1 -1
  59. package/dist/rgrc.js +76333 -40647
  60. package/dist/stdops.rgr +2 -0
  61. package/package.json +1121 -339
@@ -0,0 +1,185 @@
1
+ ; SPDX-License-Identifier: MIT
2
+
3
+ ; =============================================================================
4
+ ; AppleTarget.rgr -- what "build it for an iPhone" actually has to name.
5
+ ; =============================================================================
6
+ ;
7
+ ; Xcode hides four separate decisions behind one scheme pop-up, and a build
8
+ ; driven from the command line has to make all four itself:
9
+ ;
10
+ ; the SDK iphoneos, iphonesimulator, watchos, watchsimulator. This is
11
+ ; what `xcrun --sdk NAME --show-sdk-path` resolves and what
12
+ ; swiftc compiles against.
13
+ ; the triple arm64-apple-ios15.0-simulator. The architecture, the
14
+ ; platform, the DEPLOYMENT TARGET and, for a simulator, the
15
+ ; `-simulator` environment. Leave the environment off and the
16
+ ; compiler silently builds for a real device, which links but
17
+ ; will not run on the simulator.
18
+ ; the families iPhone, iPad, or both -- one number list in Info.plist, and
19
+ ; the only difference between "an iPhone app" and "an iPad
20
+ ; app" once the binary exists. A watch app is its own family
21
+ ; and its own kind of bundle.
22
+ ; signing a simulator build is signed ad hoc (identity "-"), which
23
+ ; needs nothing installed. A device build needs a real
24
+ ; identity and a provisioning profile, and this file only
25
+ ; carries the name of them.
26
+ ;
27
+ ; None of this needs Xcode the application. It needs the toolchain Xcode (or
28
+ ; the Command Line Tools) installs, which is what `xcrun` is for.
29
+ ; =============================================================================
30
+
31
+ ; The `UIDeviceFamily` numbers, which is the vocabulary Info.plist uses for
32
+ ; "which device is this for".
33
+ class AppleDeviceFamily {
34
+ sfn iphone:int () {
35
+ return 1
36
+ }
37
+ sfn ipad:int () {
38
+ return 2
39
+ }
40
+ sfn tv:int () {
41
+ return 3
42
+ }
43
+ sfn watch:int () {
44
+ return 4
45
+ }
46
+ }
47
+
48
+ ; One buildable combination: an SDK, a triple, and what the resulting bundle
49
+ ; says it runs on.
50
+ class AppleTarget {
51
+
52
+ ; A short name for this combination, used in log lines and build paths:
53
+ ; "ios-simulator", "ios-device", "watchos-simulator", "watchos-device".
54
+ def key:string ""
55
+ ; What `xcrun --sdk` is given.
56
+ def sdk:string ""
57
+ ; The platform part of the triple: "ios" or "watchos".
58
+ def platform:string ""
59
+ ; The deployment target, which goes INTO the triple, not beside it.
60
+ def minVersion:string ""
61
+ ; "arm64" or "x86_64". A simulator target is built for whatever the Mac
62
+ ; running the build is, because the simulator runs native code.
63
+ def arch:string "arm64"
64
+ def isSimulator:boolean true
65
+ ; UIDeviceFamily. Two entries is a universal iPhone/iPad app.
66
+ def families:[int]
67
+ ; The codesigning identity. "-" is ad hoc, which is all a simulator wants.
68
+ def signIdentity:string "-"
69
+
70
+ Constructor () {
71
+ }
72
+
73
+ ; iPhone and iPad, on the simulator. The default, because it is the one
74
+ ; combination that needs nothing but an installed Xcode -- no developer
75
+ ; account, no device, no provisioning profile.
76
+ sfn iosSimulator:AppleTarget (arch:string) {
77
+ def t:AppleTarget (new AppleTarget)
78
+ t.key = "ios-simulator"
79
+ t.sdk = "iphonesimulator"
80
+ t.platform = "ios"
81
+ t.minVersion = "15.0"
82
+ t.arch = arch
83
+ t.isSimulator = true
84
+ t.families = ([] _:int ( 1 2 ))
85
+ t.signIdentity = "-"
86
+ return t
87
+ }
88
+
89
+ ; A real iPhone or iPad. Needs a signing identity and a provisioning
90
+ ; profile; everything else about the build is the same.
91
+ sfn iosDevice:AppleTarget (identity:string) {
92
+ def t:AppleTarget (new AppleTarget)
93
+ t.key = "ios-device"
94
+ t.sdk = "iphoneos"
95
+ t.platform = "ios"
96
+ t.minVersion = "15.0"
97
+ t.arch = "arm64"
98
+ t.isSimulator = false
99
+ t.families = ([] _:int ( 1 2 ))
100
+ t.signIdentity = identity
101
+ return t
102
+ }
103
+
104
+ ; The watch, on the simulator. A standalone watchOS app -- no paired iPhone
105
+ ; app, which is what watchOS 6 and later allow and what makes a watch build
106
+ ; from the command line reasonable at all.
107
+ sfn watchSimulator:AppleTarget (arch:string) {
108
+ def t:AppleTarget (new AppleTarget)
109
+ t.key = "watchos-simulator"
110
+ t.sdk = "watchsimulator"
111
+ t.platform = "watchos"
112
+ t.minVersion = "9.0"
113
+ t.arch = arch
114
+ t.isSimulator = true
115
+ t.families = ([] _:int ( 4 ))
116
+ t.signIdentity = "-"
117
+ return t
118
+ }
119
+
120
+ sfn watchDevice:AppleTarget (identity:string) {
121
+ def t:AppleTarget (new AppleTarget)
122
+ t.key = "watchos-device"
123
+ t.sdk = "watchos"
124
+ t.platform = "watchos"
125
+ t.minVersion = "9.0"
126
+ t.arch = "arm64"
127
+ t.isSimulator = false
128
+ t.families = ([] _:int ( 4 ))
129
+ t.signIdentity = identity
130
+ return t
131
+ }
132
+
133
+ ; Only iPhone, or only iPad. The binary does not change; the bundle's claim
134
+ ; about itself does, and that is what the home screen and the simulator
135
+ ; read.
136
+ fn onlyPhone:void () {
137
+ families = ([] _:int ( 1 ))
138
+ }
139
+
140
+ fn onlyPad:void () {
141
+ families = ([] _:int ( 2 ))
142
+ }
143
+
144
+ ; The triple swiftc is given. The deployment target is IN it -- there is no
145
+ ; separate flag for it on this path -- and the `-simulator` environment is
146
+ ; what keeps a simulator build from being a device build that happens to
147
+ ; link.
148
+ fn swiftTriple:string () {
149
+ def base:string (arch + "-apple-" + platform + minVersion)
150
+ if isSimulator {
151
+ return (base + "-simulator")
152
+ }
153
+ return base
154
+ }
155
+
156
+ fn isWatch:boolean () {
157
+ return (platform == "watchos")
158
+ }
159
+
160
+ ; Signing a simulator bundle ad hoc is not ceremony: an unsigned bundle is
161
+ ; rejected by the simulator on recent macOS, and "-" needs nothing
162
+ ; installed to satisfy it.
163
+ fn isAdHocSigned:boolean () {
164
+ return (signIdentity == "-")
165
+ }
166
+
167
+ fn describe:string () {
168
+ def fam:[string]
169
+ for families f:int i {
170
+ if (f == 1) {
171
+ push fam "iPhone"
172
+ }
173
+ if (f == 2) {
174
+ push fam "iPad"
175
+ }
176
+ if (f == 3) {
177
+ push fam "Apple TV"
178
+ }
179
+ if (f == 4) {
180
+ push fam "Apple Watch"
181
+ }
182
+ }
183
+ return (key + " (" + (this.swiftTriple()) + ", sdk " + sdk + ", " + (join fam " + ") + ")")
184
+ }
185
+ }
@@ -0,0 +1,458 @@
1
+ ; SPDX-License-Identifier: MIT
2
+
3
+ ; =============================================================================
4
+ ; AppleToolchain.rgr -- Apple's command line tools, as methods.
5
+ ; =============================================================================
6
+ ;
7
+ ; Everything here goes through `xcrun`, which is the one program that knows
8
+ ; where the rest of them are. `xcrun swiftc` is not the same as `swiftc`: the
9
+ ; first resolves inside the selected developer directory and against the SDK
10
+ ; you name, the second is whichever Swift is on PATH and knows nothing about
11
+ ; iOS. Building for a device with the wrong one of those is the single most
12
+ ; common way a command line iOS build goes wrong, so nothing here calls a tool
13
+ ; directly.
14
+ ;
15
+ ; What this needs installed: Xcode, or the Command Line Tools plus the platform
16
+ ; SDKs. Nothing here opens Xcode, and nothing here needs a .xcodeproj.
17
+ ;
18
+ ; Every call goes through `Shell`, so a driver can be run dry -- the commands
19
+ ; are then recorded rather than executed, which is what makes an Apple build
20
+ ; pipeline testable on a machine that is not a Mac.
21
+ ; =============================================================================
22
+
23
+ Import "../Shell.rgr"
24
+ Import "AppleTarget.rgr"
25
+ Import "AppleSimulator.rgr"
26
+ Import "AppleDevice.rgr"
27
+
28
+ class AppleToolchain {
29
+
30
+ def sh:Shell
31
+ ; Cached answers. `xcrun --show-sdk-path` costs about a tenth of a second
32
+ ; and a build asks for the same SDK several times.
33
+ def sdkPaths:[string:string]
34
+ def sdkVersions:[string:string]
35
+
36
+ Constructor (shell:Shell) {
37
+ sh = shell
38
+ }
39
+
40
+ ; Is there a toolchain here at all? Asked first, so "no Xcode installed" is
41
+ ; one clear sentence rather than a swiftc failure nobody can read.
42
+ fn isAvailable:boolean () {
43
+ return (sh.isInstalled("xcrun"))
44
+ }
45
+
46
+ ; Which Xcode (or Command Line Tools) is selected. `xcode-select -p`.
47
+ fn developerDir:string () {
48
+ ; Every argument vector in this file is BUILT rather than written as
49
+ ; an inline literal: a one-element literal is flattened to its element
50
+ ; on Rust (ISSUES.md #84), and any literal is lost when the call
51
+ ; taking it is immediately dereferenced (#85). A build driver that only
52
+ ; compiled on Node would be a poor advertisement for a cross-language
53
+ ; compiler; this one is checked on seven targets.
54
+ def argv:[string]
55
+ push argv "-p"
56
+ return (sh.text("xcode-select" argv))
57
+ }
58
+
59
+ ; The Swift the toolchain will actually compile with, as one line.
60
+ fn swiftVersion:string () {
61
+ def argv:[string]
62
+ push argv "swift"
63
+ push argv "--version"
64
+ def res:ShellResult (sh.capture("xcrun" argv))
65
+ if (res.failed()) {
66
+ return ""
67
+ }
68
+ return (res.outFirstLine())
69
+ }
70
+
71
+ ; The Mac this build is running on. A simulator runs NATIVE code, so an
72
+ ; Apple Silicon Mac builds simulator binaries for arm64 and an Intel Mac
73
+ ; builds them for x86_64 -- getting this from the machine rather than from
74
+ ; a constant is why a simulator build works on both.
75
+ fn hostArch:string () {
76
+ def argv:[string]
77
+ push argv "-m"
78
+ def arch:string (sh.text("uname" argv))
79
+ if ((strlen arch) == 0) {
80
+ return "arm64"
81
+ }
82
+ if (arch == "x86_64") {
83
+ return "x86_64"
84
+ }
85
+ return "arm64"
86
+ }
87
+
88
+ ; The same question as `sdkPath`, with the failure kept. `sdkPath` answers
89
+ ; "" for anything that went wrong, which is the right shape for a build and
90
+ ; the wrong one for a diagnostic: an Xcode whose licence has not been
91
+ ; accepted, one whose platforms have not been downloaded and one that is not
92
+ ; there all answer "" and have three different fixes. xcrun says which on
93
+ ; stderr, so a report has to be able to see it.
94
+ fn probeSdk:ShellResult (sdk:string) {
95
+ def argv:[string]
96
+ push argv "--sdk"
97
+ push argv sdk
98
+ push argv "--show-sdk-path"
99
+ return (sh.capture("xcrun" argv))
100
+ }
101
+
102
+ fn probeSwift:ShellResult () {
103
+ def argv:[string]
104
+ push argv "swift"
105
+ push argv "--version"
106
+ return (sh.capture("xcrun" argv))
107
+ }
108
+
109
+ ; Where the SDK lives. swiftc is given this path, not the SDK name.
110
+ fn sdkPath:string (sdk:string) {
111
+ if (has sdkPaths sdk) {
112
+ return (unwrap (get sdkPaths sdk))
113
+ }
114
+ def argv:[string]
115
+ push argv "--sdk"
116
+ push argv sdk
117
+ push argv "--show-sdk-path"
118
+ def p:string (sh.text("xcrun" argv))
119
+ set sdkPaths sdk p
120
+ return p
121
+ }
122
+
123
+ ; The SDK version, which is not the same as the deployment target: the SDK
124
+ ; is what you build AGAINST, the deployment target is the oldest OS the
125
+ ; result runs on.
126
+ fn sdkVersion:string (sdk:string) {
127
+ if (has sdkVersions sdk) {
128
+ return (unwrap (get sdkVersions sdk))
129
+ }
130
+ def argv:[string]
131
+ push argv "--sdk"
132
+ push argv sdk
133
+ push argv "--show-sdk-version"
134
+ def v:string (sh.text("xcrun" argv))
135
+ set sdkVersions sdk v
136
+ return v
137
+ }
138
+
139
+ ; Is this platform's SDK installed? An Xcode with the iOS platform but not
140
+ ; the watchOS one is an ordinary installation, not a broken one, so a watch
141
+ ; build says so instead of failing inside swiftc.
142
+ fn hasSdk:boolean (sdk:string) {
143
+ return ((strlen (this.sdkPath(sdk))) > 0)
144
+ }
145
+
146
+ ; --- the simulators -------------------------------------------------------
147
+
148
+ fn listSimulators:[AppleSimulator] () {
149
+ def argv:[string]
150
+ push argv "simctl"
151
+ push argv "list"
152
+ push argv "devices"
153
+ push argv "available"
154
+ def res:ShellResult (sh.capture("xcrun" argv))
155
+ if (res.failed()) {
156
+ def none:[AppleSimulator]
157
+ return none
158
+ }
159
+ return (AppleSimulatorList.parse(res.out))
160
+ }
161
+
162
+ ; The simulator to install onto: on the platform this target builds for,
163
+ ; preferring one that is already booted, and preferring `nameWanted` when
164
+ ; the caller named something.
165
+ fn pickSimulator@(optional):AppleSimulator (target:AppleTarget nameWanted:string) {
166
+ def all:[AppleSimulator] (this.listSimulators())
167
+ def prefix:string "iOS"
168
+ if (target.isWatch()) {
169
+ prefix = "watchOS"
170
+ }
171
+ def onPlatform:[AppleSimulator] (AppleSimulatorList.onPlatform(all prefix))
172
+ return (AppleSimulatorList.pick(onPlatform nameWanted))
173
+ }
174
+
175
+ ; Boot it if it is not booted. Booting one that is already up is an error
176
+ ; from simctl and a no-op in fact, so the state is checked rather than the
177
+ ; failure ignored.
178
+ fn bootSimulator:boolean (dev:AppleSimulator) {
179
+ if (dev.isBooted()) {
180
+ return true
181
+ }
182
+ def argv:[string]
183
+ push argv "simctl"
184
+ push argv "boot"
185
+ push argv dev.udid
186
+ def res:ShellResult (sh.capture("xcrun" argv))
187
+ if (res.ok()) {
188
+ return true
189
+ }
190
+ ; "Unable to boot device in current state: Booted" is a race with
191
+ ; something else having booted it, which is success as far as this is
192
+ ; concerned.
193
+ if ((indexOf res.err "current state: Booted") >= 0) {
194
+ return true
195
+ }
196
+ return false
197
+ }
198
+
199
+ ; Bring the Simulator application to the front. Without this the device is
200
+ ; running and there is no window, which looks exactly like a build that did
201
+ ; nothing.
202
+ fn openSimulatorUi:boolean () {
203
+ def argv:[string]
204
+ push argv "-a"
205
+ push argv "Simulator"
206
+ def res:ShellResult (sh.capture("open" argv))
207
+ return (res.ok())
208
+ }
209
+
210
+ ; Wait until the device is finished booting. Installing into a device that
211
+ ; is still starting fails intermittently, which is the worst kind of
212
+ ; failure to debug.
213
+ fn waitForBoot:boolean (dev:AppleSimulator) {
214
+ def argv:[string]
215
+ push argv "simctl"
216
+ push argv "bootstatus"
217
+ push argv dev.udid
218
+ def res:ShellResult (sh.capture("xcrun" argv))
219
+ return (res.ok())
220
+ }
221
+
222
+ fn installApp:ShellResult (dev:AppleSimulator appPath:string) {
223
+ def argv:[string]
224
+ push argv "simctl"
225
+ push argv "install"
226
+ push argv dev.udid
227
+ push argv appPath
228
+ return (sh.capture("xcrun" argv))
229
+ }
230
+
231
+ fn launchApp:ShellResult (dev:AppleSimulator bundleId:string) {
232
+ def argv:[string]
233
+ push argv "simctl"
234
+ push argv "launch"
235
+ push argv "--terminate-running-process"
236
+ push argv dev.udid
237
+ push argv bundleId
238
+ return (sh.capture("xcrun" argv))
239
+ }
240
+
241
+ ; The app's own log, filtered to this bundle. The watch and the phone both
242
+ ; write here, and it is the only way to see a print() from inside a
243
+ ; simulator without attaching a debugger.
244
+ fn streamLog:ShellResult (dev:AppleSimulator bundleId:string) {
245
+ def predicate:string ("subsystem CONTAINS \"" + bundleId + "\" OR process CONTAINS \"" + bundleId + "\"")
246
+ def argv:[string]
247
+ push argv "simctl"
248
+ push argv "spawn"
249
+ push argv dev.udid
250
+ push argv "log"
251
+ push argv "stream"
252
+ push argv "--level"
253
+ push argv "debug"
254
+ push argv "--predicate"
255
+ push argv predicate
256
+ return (sh.run("xcrun" argv))
257
+ }
258
+
259
+ ; --- real devices ---------------------------------------------------------
260
+
261
+ ; Attached iPhones and iPads, as raw text. `devicectl` ships with Xcode 15
262
+ ; and later; older toolchains need a third party installer, which this does
263
+ ; not pretend to wrap.
264
+ fn hasDeviceCtl:boolean () {
265
+ def argv:[string]
266
+ push argv "devicectl"
267
+ push argv "--version"
268
+ def res:ShellResult (sh.capture("xcrun" argv))
269
+ return (res.ok())
270
+ }
271
+
272
+ ; The iPhones and iPads that are plugged in, parsed.
273
+ fn listConnectedDevices:[AppleDeviceInfo] () {
274
+ def res:ShellResult (this.listDevices())
275
+ if (res.failed()) {
276
+ def none:[AppleDeviceInfo]
277
+ return none
278
+ }
279
+ return (AppleDeviceList.parse(res.out))
280
+ }
281
+
282
+ ; The one to install onto: connected, and matching `nameWanted` when the
283
+ ; caller named something.
284
+ fn pickDevice@(optional):AppleDeviceInfo (nameWanted:string) {
285
+ def all:[AppleDeviceInfo] (this.listConnectedDevices())
286
+ return (AppleDeviceList.pick(all nameWanted))
287
+ }
288
+
289
+ fn listDevices:ShellResult () {
290
+ def argv:[string]
291
+ push argv "devicectl"
292
+ push argv "list"
293
+ push argv "devices"
294
+ return (sh.capture("xcrun" argv))
295
+ }
296
+
297
+ ; Start it on the device. `devicectl` needs the bundle id, not the path --
298
+ ; the app is already installed by this point -- and `--console` keeps the
299
+ ; process attached so its output lands here, which is what a test build on
300
+ ; a cable is for.
301
+ fn launchOnDevice:ShellResult (udid:string bundleId:string attach:boolean) {
302
+ def argv:[string]
303
+ push argv "devicectl"
304
+ push argv "device"
305
+ push argv "process"
306
+ push argv "launch"
307
+ push argv "--device"
308
+ push argv udid
309
+ if attach {
310
+ push argv "--console"
311
+ } {
312
+ push argv "--terminate-existing"
313
+ }
314
+ push argv bundleId
315
+ if attach {
316
+ return (sh.run("xcrun" argv))
317
+ }
318
+ return (sh.capture("xcrun" argv))
319
+ }
320
+
321
+ fn installOnDevice:ShellResult (udid:string appPath:string) {
322
+ def argv:[string]
323
+ push argv "devicectl"
324
+ push argv "device"
325
+ push argv "install"
326
+ push argv "app"
327
+ push argv "--device"
328
+ push argv udid
329
+ push argv appPath
330
+ return (sh.capture("xcrun" argv))
331
+ }
332
+
333
+ ; Everything the device will say about itself. This is also the cheapest
334
+ ; way to ask whether a device can be TALKED to at all: it raises the tunnel
335
+ ; if one is needed, so a failure here is a real connection failure and not
336
+ ; a stale line in a listing.
337
+ fn deviceDetails:ShellResult (udid:string) {
338
+ def argv:[string]
339
+ push argv "devicectl"
340
+ push argv "device"
341
+ push argv "info"
342
+ push argv "details"
343
+ push argv "--device"
344
+ push argv udid
345
+ return (sh.capture("xcrun" argv))
346
+ }
347
+
348
+ ; The apps already installed. Listing them needs the developer disk image,
349
+ ; so asking is how the DDI gets mounted on a device that has none: devicectl
350
+ ; personalises and mounts it, and the answer arrives a few seconds later.
351
+ ; A build that asks this first therefore fixes the commonest reason an
352
+ ; install is refused, rather than reporting it.
353
+ fn listInstalledApps:ShellResult (udid:string) {
354
+ def argv:[string]
355
+ push argv "devicectl"
356
+ push argv "device"
357
+ push argv "info"
358
+ push argv "apps"
359
+ push argv "--device"
360
+ push argv udid
361
+ return (sh.capture("xcrun" argv))
362
+ }
363
+
364
+ ; What the USB bus has on it, as raw text. Asked only when devicectl lists
365
+ ; NOTHING, because it separates "no cable" from "a cable and no pairing" --
366
+ ; and because it is the one question here that a broken CoreDevice cannot
367
+ ; get in the way of.
368
+ fn usbDeviceText:string () {
369
+ def argv:[string]
370
+ push argv "SPUSBDataType"
371
+ def res:ShellResult (sh.capture("system_profiler" argv))
372
+ if (res.failed()) {
373
+ return ""
374
+ }
375
+ return res.out
376
+ }
377
+
378
+ ; --- signing --------------------------------------------------------------
379
+
380
+ ; The identities available for signing a device build. Ad hoc signing needs
381
+ ; none of them, which is why a simulator build works on a machine with no
382
+ ; developer account at all.
383
+ fn signingIdentities:ShellResult () {
384
+ def argv:[string]
385
+ push argv "find-identity"
386
+ push argv "-v"
387
+ push argv "-p"
388
+ push argv "codesigning"
389
+ return (sh.capture("security" argv))
390
+ }
391
+
392
+ fn codesign:ShellResult (identity:string bundlePath:string entitlements:string) {
393
+ def argv:[string]
394
+ push argv "--force"
395
+ push argv "--sign"
396
+ push argv identity
397
+ if ((strlen entitlements) > 0) {
398
+ push argv "--entitlements"
399
+ push argv entitlements
400
+ }
401
+ push argv "--timestamp=none"
402
+ push argv bundlePath
403
+ return (sh.capture("codesign" argv))
404
+ }
405
+
406
+ ; --- turning a plist into what the device wants ---------------------------
407
+
408
+ ; iOS reads a binary plist perfectly well as XML, so this is optional --
409
+ ; but converting also VALIDATES, which turns an unparseable plist into a
410
+ ; build error instead of an app that silently refuses to install.
411
+ fn convertPlistToBinary:ShellResult (plistPath:string) {
412
+ def argv:[string]
413
+ push argv "-convert"
414
+ push argv "binary1"
415
+ push argv plistPath
416
+ return (sh.capture("plutil" argv))
417
+ }
418
+
419
+ fn lintPlist:ShellResult (plistPath:string) {
420
+ def argv:[string]
421
+ push argv "-lint"
422
+ push argv plistPath
423
+ return (sh.capture("plutil" argv))
424
+ }
425
+
426
+ ; --- asset catalogs -------------------------------------------------------
427
+
428
+ ; Compile an .xcassets into the Assets.car an app icon has to be in. Only
429
+ ; called when the caller has a catalog; an app with no icon builds and runs
430
+ ; without one.
431
+ fn compileAssets:ShellResult (catalog:string outDir:string target:AppleTarget) {
432
+ def platformName:string "iphonesimulator"
433
+ if (target.isWatch()) {
434
+ platformName = "watchsimulator"
435
+ if (target.isSimulator == false) {
436
+ platformName = "watchos"
437
+ }
438
+ } {
439
+ if (target.isSimulator == false) {
440
+ platformName = "iphoneos"
441
+ }
442
+ }
443
+ def argv:[string]
444
+ push argv "actool"
445
+ push argv catalog
446
+ push argv "--compile"
447
+ push argv outDir
448
+ push argv "--platform"
449
+ push argv platformName
450
+ push argv "--minimum-deployment-target"
451
+ push argv target.minVersion
452
+ push argv "--app-icon"
453
+ push argv "AppIcon"
454
+ push argv "--output-partial-info-plist"
455
+ push argv (outDir + "/assetcatalog.plist")
456
+ return (sh.capture("xcrun" argv))
457
+ }
458
+ }