ranger-compiler 3.2.0 → 3.4.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 (58) hide show
  1. package/CHANGELOG.md +2482 -0
  2. package/LICENSE +28 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +1650 -1895
  5. package/dist/Lang.rgr +10946 -5852
  6. package/dist/README.md +3 -2
  7. package/dist/api.d.ts +1157 -29
  8. package/dist/api.js +66623 -38073
  9. package/dist/lib/ACEEditor.rgr +2 -0
  10. package/dist/lib/Ajax.rgr +2 -0
  11. package/dist/lib/CmdParams.rgr +2 -0
  12. package/dist/lib/Crypto.rgr +2 -0
  13. package/dist/lib/DOMLib.rgr +2 -0
  14. package/dist/lib/Engine3D.rgr +2 -0
  15. package/dist/lib/ImmutableVector.rgr +2 -0
  16. package/dist/lib/IndexedDB.rgr +2 -0
  17. package/dist/lib/IsoDate/DateMath.rgr +2 -0
  18. package/dist/lib/IsoDate/IsoCalendar.rgr +2 -0
  19. package/dist/lib/IsoDate/IsoDateParse.rgr +2 -0
  20. package/dist/lib/IsoDateLib.rgr +2 -0
  21. package/dist/lib/JSON.rgr +4906 -48
  22. package/dist/lib/JinxProcess.rgr +2 -0
  23. package/dist/lib/RangerProcess.rgr +2 -0
  24. package/dist/lib/Regex/RegexMatch.rgr +2 -0
  25. package/dist/lib/RegexLib.rgr +2 -0
  26. package/dist/lib/SQL.rgr +2 -0
  27. package/dist/lib/ServiceLib.rgr +2 -0
  28. package/dist/lib/Shell.rgr +326 -0
  29. package/dist/lib/Storage.rgr +2 -0
  30. package/dist/lib/Time.rgr +2 -0
  31. package/dist/lib/Timers.rgr +2 -0
  32. package/dist/lib/TypedArrays.rgr +2 -0
  33. package/dist/lib/ViewLib.rgr +2 -0
  34. package/dist/lib/WebLib.rgr +2 -0
  35. package/dist/lib/WebServerLib.rgr +2 -0
  36. package/dist/lib/apple/AppleAppBuilder.rgr +572 -0
  37. package/dist/lib/apple/AppleAppSpec.rgr +201 -0
  38. package/dist/lib/apple/AppleDevice.rgr +295 -0
  39. package/dist/lib/apple/AppleDeviceDoctor.rgr +453 -0
  40. package/dist/lib/apple/AppleSigning.rgr +379 -0
  41. package/dist/lib/apple/AppleSimulator.rgr +248 -0
  42. package/dist/lib/apple/AppleTarget.rgr +185 -0
  43. package/dist/lib/apple/AppleToolchain.rgr +458 -0
  44. package/dist/lib/apple/README.md +311 -0
  45. package/dist/lib/apple/apple_test.rgr +669 -0
  46. package/dist/lib/core/README.md +251 -0
  47. package/dist/lib/core/RgBase.rgr +313 -0
  48. package/dist/lib/core/RgNum.rgr +653 -0
  49. package/dist/lib/core/RgText.rgr +680 -0
  50. package/dist/lib/core/RgU32.rgr +309 -0
  51. package/dist/lib/ranger-dir.rgr +2 -0
  52. package/dist/lib/shell_test.rgr +193 -0
  53. package/dist/lib/stdlib.rgr +1176 -665
  54. package/dist/lib/stdops.rgr +2 -0
  55. package/dist/package.json +1 -1
  56. package/dist/rgrc.js +72955 -40753
  57. package/dist/stdops.rgr +2 -0
  58. package/package.json +1121 -339
@@ -0,0 +1,453 @@
1
+ ; SPDX-License-Identifier: MIT
2
+
3
+ ; =============================================================================
4
+ ; AppleDeviceDoctor.rgr -- why the phone will not take the app, in order.
5
+ ; =============================================================================
6
+ ;
7
+ ; A device build has a long chain in front of it, and every link answers a
8
+ ; different question with the same silence: nothing installs. The chain, from
9
+ ; the end a person can do nothing about to the end they can:
10
+ ;
11
+ ; xcrun -> a selected Xcode -> devicectl -> the iphoneos SDK ->
12
+ ; a cable -> a pairing -> Developer Mode -> a mounted developer disk image ->
13
+ ; a signing identity -> a provisioning profile that lists THIS device
14
+ ;
15
+ ; This walks it and stops at the first link that is not there, because every
16
+ ; check after a broken one answers about a machine that does not exist. What it
17
+ ; prints is the check, what it found, and what to do -- the last of which is the
18
+ ; only part a person actually needs.
19
+ ;
20
+ ; Two of the links repair themselves and are therefore repaired rather than
21
+ ; reported:
22
+ ;
23
+ ; * A device listed as "available (paired)" has no tunnel open AT THAT
24
+ ; MOMENT. Asking it anything opens one. So this asks.
25
+ ; * A device with no developer disk image mounted gets one the first time
26
+ ; anything wants a developer service. So this wants one.
27
+ ;
28
+ ; Those two are the reason this file exists: both look exactly like a broken
29
+ ; cable in `devicectl list devices`, and neither is.
30
+ ;
31
+ ; Everything here goes through `Shell`, so a dry run records the questions
32
+ ; instead of asking them, and the parsing underneath is in AppleDevice.rgr,
33
+ ; where it is a pure function over text and is tested on a machine with nothing
34
+ ; plugged in.
35
+ ; =============================================================================
36
+
37
+ Import "AppleToolchain.rgr"
38
+ Import "AppleSigning.rgr"
39
+
40
+ ; One link in the chain: what was asked, what came back, and what to do when
41
+ ; the answer was no.
42
+ class AppleCheckResult {
43
+
44
+ def name:string ""
45
+ def ok:boolean false
46
+ ; What was found, for the line this prints. Short.
47
+ def detail:string ""
48
+ ; What to do about it. Empty on a check that passed.
49
+ def remedy:[string]
50
+ ; A failure that makes every later check meaningless. A warning does not.
51
+ def fatal:boolean true
52
+
53
+ Constructor () {
54
+ }
55
+ }
56
+
57
+ class AppleDeviceDoctor {
58
+
59
+ def sh:Shell
60
+ def tools:AppleToolchain
61
+ def signing:AppleSigning
62
+ def checks:[AppleCheckResult]
63
+
64
+ ; What to check FOR. The bundle id decides which provisioning profile
65
+ ; counts; "" skips the signing half, which is what a plain "can this
66
+ ; machine see my phone" run wants.
67
+ def bundleId:string ""
68
+ ; A name substring or an identifier, as --device takes.
69
+ def deviceWanted:string ""
70
+ ; Where a profile is unpacked while it is read.
71
+ def workDir:string "tmp"
72
+
73
+ ; Lines the caller can add to the "no device" advice -- the simulator
74
+ ; commands of whichever port this is. Generic advice cannot know them.
75
+ def simulatorHint:[string]
76
+
77
+ ; The identifier that came out of it. "" when no device is usable.
78
+ def deviceId:string ""
79
+ ; The same device as one line, for a caller that wants to say which one it
80
+ ; picked without printing the whole report.
81
+ def deviceDescription:string ""
82
+ ; The device details, once they have been read.
83
+ def details@(optional):AppleDeviceDetails
84
+
85
+ Constructor (shell:Shell) {
86
+ sh = shell
87
+ tools = (new AppleToolchain (shell))
88
+ signing = (new AppleSigning (shell))
89
+ }
90
+
91
+ ; ---------------------------------------------------------------- results
92
+
93
+ fn add:AppleCheckResult (name:string ok:boolean detail:string) {
94
+ def c:AppleCheckResult (new AppleCheckResult)
95
+ c.name = name
96
+ c.ok = ok
97
+ c.detail = detail
98
+ push checks c
99
+ return c
100
+ }
101
+
102
+ fn failed:boolean () {
103
+ def bad:boolean false
104
+ for checks c:AppleCheckResult i {
105
+ if (c.ok == false) {
106
+ if c.fatal {
107
+ bad = true
108
+ }
109
+ }
110
+ }
111
+ return bad
112
+ }
113
+
114
+ ; ----------------------------------------------------------------- checks
115
+
116
+ ; The toolchain. `xcode-select -p` pointing at CommandLineTools is the one
117
+ ; that is worth naming: every tool a device build needs is present there
118
+ ; EXCEPT the ones a device build needs, so the failures it produces read
119
+ ; like a broken Xcode rather than a wrong path.
120
+ fn checkToolchain:boolean () {
121
+ if ((tools.isAvailable()) == false) {
122
+ def c:AppleCheckResult (this.add("xcrun" false "not on this machine"))
123
+ push c.remedy "Install Xcode. The Command Line Tools alone cannot build for a device:"
124
+ push c.remedy "they carry no iphoneos SDK and no devicectl."
125
+ return false
126
+ }
127
+ def dir:string (tools.developerDir())
128
+ def onTools:boolean ((indexOf dir "CommandLineTools") >= 0)
129
+ if onTools {
130
+ def c:AppleCheckResult (this.add("xcode-select" false dir))
131
+ push c.remedy "The selected developer directory is the Command Line Tools, which have"
132
+ push c.remedy "neither the iphoneos SDK nor devicectl. Point it at Xcode:"
133
+ push c.remedy ""
134
+ push c.remedy " sudo xcode-select -s /Applications/Xcode.app/Contents/Developer"
135
+ return false
136
+ }
137
+ this.add("xcode-select" true dir)
138
+
139
+ if ((tools.hasDeviceCtl()) == false) {
140
+ def c:AppleCheckResult (this.add("devicectl" false "not in this Xcode"))
141
+ push c.remedy "devicectl lives inside Xcode.app and needs Xcode 15 or later. An older"
142
+ push c.remedy "Xcode cannot install onto a device from the command line at all."
143
+ return false
144
+ }
145
+ this.add("devicectl" true "present")
146
+
147
+ def sdk:ShellResult (tools.probeSdk("iphoneos"))
148
+ if (sdk.failed()) {
149
+ def c:AppleCheckResult (this.add("iphoneos SDK" false "not found"))
150
+ push c.remedy "xcrun cannot find the iphoneos SDK:"
151
+ push c.remedy ""
152
+ push c.remedy (sdk.describeFailure())
153
+ push c.remedy ""
154
+ push c.remedy "An Xcode that has never been opened has not accepted its licence yet:"
155
+ push c.remedy ""
156
+ push c.remedy " sudo xcodebuild -license accept"
157
+ return false
158
+ }
159
+ this.add("iphoneos SDK" true (sdk.outFirstLine()))
160
+ return true
161
+ }
162
+
163
+ ; A device to talk to. Both failures here -- nothing listed, and nothing
164
+ ; listed that can be used -- get different advice, because a cable that is
165
+ ; not plugged in and a phone that is asleep are not the same problem.
166
+ fn checkDevice:boolean () {
167
+ def all:[AppleDeviceInfo] (tools.listConnectedDevices())
168
+ if ((array_length all) == 0) {
169
+ def c:AppleCheckResult (this.add("device" false "devicectl lists none"))
170
+ def usb:string (tools.usbDeviceText())
171
+ def seesPhone:boolean false
172
+ if ((indexOf usb "iPhone") >= 0) {
173
+ seesPhone = true
174
+ }
175
+ if ((indexOf usb "iPad") >= 0) {
176
+ seesPhone = true
177
+ }
178
+ if seesPhone {
179
+ c.detail = "on USB, not paired"
180
+ push c.remedy "The Mac sees the device on USB but devicectl does not know it, so the"
181
+ push c.remedy "pairing is what is missing. Unlock the device, then:"
182
+ push c.remedy ""
183
+ push c.remedy " xcrun devicectl list devices"
184
+ push c.remedy ""
185
+ push c.remedy "and answer \"Trust\" on the device. If no prompt appears, the old pairing"
186
+ push c.remedy "is in the way: Settings > General > Transfer or Reset > Reset >"
187
+ push c.remedy "Reset Location & Privacy, then unplug and plug it back in."
188
+ } {
189
+ c.detail = "nothing on USB either"
190
+ push c.remedy "Nothing that looks like an iPhone or an iPad is on the USB bus, so this"
191
+ push c.remedy "is the cable or the port rather than anything on the Mac. A charge-only"
192
+ push c.remedy "cable is the usual answer -- it has no data lines and looks identical."
193
+ push c.remedy "Try another cable, and a port on the Mac itself rather than a hub."
194
+ }
195
+ this.addSimulatorHint(c)
196
+ return false
197
+ }
198
+
199
+ def pickedOpt@(optional):AppleDeviceInfo (AppleDeviceList.pick(all deviceWanted))
200
+ if (null? pickedOpt) {
201
+ def what:string "no usable device"
202
+ if ((strlen deviceWanted) > 0) {
203
+ what = ("nothing matches \"" + deviceWanted + "\"")
204
+ }
205
+ def c:AppleCheckResult (this.add("device" false what))
206
+ push c.remedy "devicectl lists these:"
207
+ push c.remedy ""
208
+ for all d:AppleDeviceInfo i {
209
+ push c.remedy (" " + (d.describe()))
210
+ }
211
+ push c.remedy ""
212
+ if ((strlen deviceWanted) > 0) {
213
+ push c.remedy "--device takes a substring of the name or the identifier itself."
214
+ } {
215
+ push c.remedy "A device listed as \"unavailable\" is one devicectl cannot reach: it is"
216
+ push c.remedy "asleep, in recovery, or the pairing has gone stale. Unlock it and plug"
217
+ push c.remedy "it in again; if it stays unavailable, pair it once more:"
218
+ push c.remedy ""
219
+ push c.remedy " xcrun devicectl manage unpair --device IDENTIFIER"
220
+ push c.remedy " xcrun devicectl manage pair --device IDENTIFIER"
221
+ }
222
+ this.addSimulatorHint(c)
223
+ return false
224
+ }
225
+ def picked:AppleDeviceInfo (unwrap pickedOpt)
226
+ deviceId = picked.identifier
227
+ deviceDescription = (picked.describe())
228
+ this.add("device" true deviceDescription)
229
+ return true
230
+ }
231
+
232
+ fn addSimulatorHint:void (c:AppleCheckResult) {
233
+ if ((array_length simulatorHint) == 0) {
234
+ return
235
+ }
236
+ push c.remedy ""
237
+ for simulatorHint line:string i {
238
+ push c.remedy line
239
+ }
240
+ }
241
+
242
+ ; Can it be TALKED to, and is it in a state that accepts an install? This
243
+ ; is where a device listed as "available (paired)" stops being a question:
244
+ ; asking for its details raises the tunnel, so an answer here IS the proof.
245
+ fn checkReachable:boolean () {
246
+ def res:ShellResult (tools.deviceDetails(deviceId))
247
+ if (res.failed()) {
248
+ def c:AppleCheckResult (this.add("connection" false "no tunnel"))
249
+ push c.remedy "devicectl could not open a connection to the device:"
250
+ push c.remedy ""
251
+ push c.remedy (res.describeFailure())
252
+ push c.remedy ""
253
+ push c.remedy "The tunnel is a real network connection even over a cable, so the three"
254
+ push c.remedy "things that break it are a firewall, a VPN, and a stuck daemon:"
255
+ push c.remedy ""
256
+ push c.remedy " 1. System Settings > Network > Firewall: turn off \"Block all incoming\""
257
+ push c.remedy " connections\" while you install."
258
+ push c.remedy " 2. Disconnect any VPN or network filter (Little Snitch, LuLu, a"
259
+ push c.remedy " corporate client) and try again."
260
+ push c.remedy " 3. Reset the daemons, then unplug and plug the cable back in:"
261
+ push c.remedy ""
262
+ push c.remedy " sudo pkill -9 remoted; sudo pkill -9 remotepairingd; sudo pkill -9 usbmuxd"
263
+ push c.remedy ""
264
+ push c.remedy "Keep the device unlocked while it reconnects."
265
+ return false
266
+ }
267
+ def d:AppleDeviceDetails (AppleDeviceDetails.parse(res.out))
268
+ details = d
269
+ def says:string (d.marketingName + " iOS " + d.osVersion)
270
+ this.add("connection" true (trim says))
271
+
272
+ if ((d.developerModeEnabled()) == false) {
273
+ def c:AppleCheckResult (this.add("developer mode" false d.developerMode))
274
+ push c.remedy "Developer Mode is off on the device. Nothing can be installed onto it"
275
+ push c.remedy "until it is on, and it can only be turned on ON THE DEVICE -- Apple made"
276
+ push c.remedy "it a physical confirmation on purpose, so no command here can do it."
277
+ push c.remedy ""
278
+ push c.remedy " Settings > Privacy & Security > Developer Mode > on,"
279
+ push c.remedy " then restart the device and confirm after unlocking."
280
+ push c.remedy ""
281
+ push c.remedy "The menu entry only appears once a developer tool has tried to connect."
282
+ push c.remedy "If it is not there, leave the cable in, run this check once more, and"
283
+ push c.remedy "look again."
284
+ return false
285
+ }
286
+ this.add("developer mode" true "enabled")
287
+ return true
288
+ }
289
+
290
+ ; The developer disk image. A device without one answers `ddiServicesAvailable:
291
+ ; false` and refuses every install, and the repair is to ask it for something
292
+ ; that needs the image -- so this asks, and only reports when the asking
293
+ ; failed.
294
+ fn checkDeveloperImage:boolean () {
295
+ def mounted:boolean false
296
+ if (!null? details) {
297
+ def d:AppleDeviceDetails (unwrap details)
298
+ mounted = d.ddiAvailable
299
+ }
300
+ if mounted {
301
+ this.add("developer image" true "mounted")
302
+ return true
303
+ }
304
+ def res:ShellResult (tools.listInstalledApps(deviceId))
305
+ if (res.ok()) {
306
+ this.add("developer image" true "mounted on demand")
307
+ return true
308
+ }
309
+ def c:AppleCheckResult (this.add("developer image" false "cannot be mounted"))
310
+ push c.remedy "The device has no developer disk image and one could not be mounted:"
311
+ push c.remedy ""
312
+ push c.remedy (res.describeFailure())
313
+ push c.remedy ""
314
+ push c.remedy "Since iOS 17 that image is personalised per device, which makes mounting"
315
+ push c.remedy "it a network call to Apple. A VPN, a proxy or a firewall blocking that"
316
+ push c.remedy "call produces exactly this. Disconnect them and try again."
317
+ push c.remedy ""
318
+ push c.remedy "If the image itself is missing, Xcode has not finished downloading its"
319
+ push c.remedy "components -- check that this directory is not empty:"
320
+ push c.remedy ""
321
+ push c.remedy " /Applications/Xcode.app/Contents/Resources/CoreDeviceDDIs"
322
+ push c.remedy ""
323
+ push c.remedy "Either way the mount can also be done without Xcode:"
324
+ push c.remedy ""
325
+ push c.remedy " pip3 install -U pymobiledevice3"
326
+ push c.remedy " sudo python3 -m pymobiledevice3 mounter auto-mount"
327
+ return false
328
+ }
329
+
330
+ ; Signing. Checked last because it is the only link that is about the app
331
+ ; rather than about the machine, and because a profile is matched against
332
+ ; the device the checks above found.
333
+ fn checkSigning:boolean () {
334
+ def ids:[AppleSigningIdentity] (signing.findIdentities())
335
+ if ((array_length ids) == 0) {
336
+ def c:AppleCheckResult (this.add("identity" false "none in the keychain"))
337
+ push c.remedy "There is no codesigning identity to sign a device build with."
338
+ push c.remedy ""
339
+ push c.remedy "A free Apple ID can only be turned into one by Xcode: open it once,"
340
+ push c.remedy "Settings > Accounts, add the Apple ID, and build any project to this"
341
+ push c.remedy "device. Everything after that is command line again."
342
+ push c.remedy ""
343
+ push c.remedy "With a paid account the whole thing can stay on the command line --"
344
+ push c.remedy "fastlane cert and fastlane sigh write the certificate and the profile"
345
+ push c.remedy "where this looks for them."
346
+ return false
347
+ }
348
+ def idOpt@(optional):AppleSigningIdentity (AppleSigning.pickIdentity(ids ""))
349
+ if (null? idOpt) {
350
+ def c:AppleCheckResult (this.add("identity" false "none usable"))
351
+ push c.remedy "The keychain has certificates but none that can sign a device build."
352
+ push c.remedy "`security find-identity -v -p codesigning` lists what is there."
353
+ return false
354
+ }
355
+ def id:AppleSigningIdentity (unwrap idOpt)
356
+ this.add("identity" true (id.describe()))
357
+
358
+ if ((strlen bundleId) == 0) {
359
+ return true
360
+ }
361
+ def profOpt@(optional):AppleProvisioningProfile (signing.pickProfile(bundleId deviceId workDir))
362
+ if (null? profOpt) {
363
+ def c:AppleCheckResult (this.add("profile" false ("none covers " + bundleId)))
364
+ push c.remedy ("No provisioning profile on this machine covers " + bundleId)
365
+ push c.remedy "for this device. A profile has to name the device, so one made for a"
366
+ push c.remedy "different phone does not count."
367
+ push c.remedy ""
368
+ push c.remedy "Open Xcode once, make an empty iOS app with this bundle id, pick your"
369
+ push c.remedy "team, and build it to this device. That writes the profile, and this"
370
+ push c.remedy "build finds it from then on. A free account's profile expires after"
371
+ push c.remedy "seven days and the same visit renews it."
372
+ push c.remedy ""
373
+ push c.remedy "--profile=PATH takes one directly if you have it from elsewhere."
374
+ return false
375
+ }
376
+ def prof:AppleProvisioningProfile (unwrap profOpt)
377
+ this.add("profile" true (prof.describe()))
378
+ return true
379
+ }
380
+
381
+ ; --------------------------------------------------------------- the walk
382
+
383
+ ; Every link, in order, stopping at the first that is missing. True when a
384
+ ; device build can go ahead.
385
+ fn run:boolean () {
386
+ if ((this.checkToolchain()) == false) {
387
+ return false
388
+ }
389
+ if ((this.checkDevice()) == false) {
390
+ return false
391
+ }
392
+ if ((this.checkReachable()) == false) {
393
+ return false
394
+ }
395
+ if ((this.checkDeveloperImage()) == false) {
396
+ return false
397
+ }
398
+ return (this.checkSigning())
399
+ }
400
+
401
+ ; The part that runs before a build rather than instead of one: the device
402
+ ; links only, without the signing checks the build itself is about to do.
403
+ ; Cheap enough to run every time -- one listing and one question to the
404
+ ; device -- and it is what mounts the developer image, so a build that
405
+ ; calls this does not fail on a missing one.
406
+ fn preflight:boolean () {
407
+ if ((this.checkDevice()) == false) {
408
+ return false
409
+ }
410
+ if ((this.checkReachable()) == false) {
411
+ return false
412
+ }
413
+ return (this.checkDeveloperImage())
414
+ }
415
+
416
+ ; ---------------------------------------------------------------- report
417
+
418
+ sfn pad:string (s:string width:int) {
419
+ def out:string s
420
+ def i:int (strlen s)
421
+ while (i < width) {
422
+ out = (out + " ")
423
+ i = i + 1
424
+ }
425
+ return out
426
+ }
427
+
428
+ ; One line per check, then the advice for the one that failed. Only the
429
+ ; first failure gets advice: the checks after it were never run, and a
430
+ ; wall of remedies for problems nobody has is how a diagnostic stops being
431
+ ; read.
432
+ fn report:void () {
433
+ for checks c:AppleCheckResult i {
434
+ def mark:string " ok "
435
+ if (c.ok == false) {
436
+ mark = " FAIL"
437
+ }
438
+ print (mark + " " + (AppleDeviceDoctor.pad(c.name 18)) + c.detail)
439
+ }
440
+ def shown:boolean false
441
+ for checks c:AppleCheckResult i {
442
+ if (shown == false) {
443
+ if (c.ok == false) {
444
+ shown = true
445
+ print ""
446
+ for c.remedy line:string li {
447
+ print line
448
+ }
449
+ }
450
+ }
451
+ }
452
+ }
453
+ }