ranger-compiler 3.1.1 → 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.
- package/CHANGELOG.md +2527 -0
- package/LICENSE +28 -0
- package/LICENSE-MIT +21 -0
- package/README.md +1650 -1875
- package/dist/Lang.rgr +10946 -5662
- package/dist/README.md +3 -2
- package/dist/api.d.ts +2023 -61
- package/dist/api.js +68930 -27355
- package/dist/lib/ACEEditor.rgr +2 -0
- package/dist/lib/Ajax.rgr +2 -0
- package/dist/lib/CmdParams.rgr +2 -0
- package/dist/lib/Crypto.rgr +2 -0
- package/dist/lib/DOMLib.rgr +2 -0
- package/dist/lib/Engine3D.rgr +2 -0
- package/dist/lib/ImmutableVector.rgr +2 -0
- package/dist/lib/IndexedDB.rgr +2 -0
- package/dist/lib/IsoDate/DateMath.rgr +2 -0
- package/dist/lib/IsoDate/IsoCalendar.rgr +2 -0
- package/dist/lib/IsoDate/IsoDateParse.rgr +2 -0
- package/dist/lib/IsoDateLib.rgr +2 -0
- package/dist/lib/JSON.rgr +4906 -48
- package/dist/lib/JinxProcess.rgr +2 -0
- package/dist/lib/RangerProcess.rgr +2 -0
- package/dist/lib/Regex/RegexMatch.rgr +2 -0
- package/dist/lib/RegexLib.rgr +2 -0
- package/dist/lib/SQL.rgr +2 -0
- package/dist/lib/ServiceLib.rgr +2 -0
- package/dist/lib/Shell.rgr +326 -0
- package/dist/lib/Storage.rgr +2 -0
- package/dist/lib/Time.rgr +2 -0
- package/dist/lib/Timers.rgr +2 -0
- package/dist/lib/TypedArrays.rgr +2 -0
- package/dist/lib/ViewLib.rgr +2 -0
- package/dist/lib/WebLib.rgr +2 -0
- package/dist/lib/WebServerLib.rgr +2 -0
- package/dist/lib/apple/AppleAppBuilder.rgr +572 -0
- package/dist/lib/apple/AppleAppSpec.rgr +201 -0
- package/dist/lib/apple/AppleDevice.rgr +295 -0
- package/dist/lib/apple/AppleDeviceDoctor.rgr +453 -0
- package/dist/lib/apple/AppleSigning.rgr +379 -0
- package/dist/lib/apple/AppleSimulator.rgr +248 -0
- package/dist/lib/apple/AppleTarget.rgr +185 -0
- package/dist/lib/apple/AppleToolchain.rgr +458 -0
- package/dist/lib/apple/README.md +311 -0
- package/dist/lib/apple/apple_test.rgr +669 -0
- package/dist/lib/core/README.md +251 -0
- package/dist/lib/core/RgBase.rgr +313 -0
- package/dist/lib/core/RgNum.rgr +653 -0
- package/dist/lib/core/RgText.rgr +680 -0
- package/dist/lib/core/RgU32.rgr +309 -0
- package/dist/lib/ranger-dir.rgr +27 -5
- package/dist/lib/shell_test.rgr +193 -0
- package/dist/lib/stdlib.rgr +1176 -665
- package/dist/lib/stdops.rgr +2 -0
- package/dist/package.json +1 -1
- package/dist/rgrc.js +73013 -37819
- package/dist/stdops.rgr +2 -0
- package/package.json +1121 -205
package/dist/lib/JinxProcess.rgr
CHANGED
package/dist/lib/RegexLib.rgr
CHANGED
package/dist/lib/SQL.rgr
CHANGED
package/dist/lib/ServiceLib.rgr
CHANGED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
; SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
; =============================================================================
|
|
4
|
+
; Shell.rgr -- running other command line programs from Ranger.
|
|
5
|
+
; =============================================================================
|
|
6
|
+
;
|
|
7
|
+
; The compiler gives one primitive for this, `run_process_result`, which answers
|
|
8
|
+
; ([exit code, stdout, stderr]) as three strings. This file is the face you
|
|
9
|
+
; should actually write against: a result object instead of three strings read
|
|
10
|
+
; out by index, a working directory that is remembered rather than repeated, a
|
|
11
|
+
; log of every command line that was asked for, and a DRY RUN.
|
|
12
|
+
;
|
|
13
|
+
; The dry run is the reason a driver written on top of this can be tested at
|
|
14
|
+
; all. A build that shells out to somebody else's toolchain cannot be checked on
|
|
15
|
+
; a machine that does not have that toolchain -- but the DECISIONS it makes (which
|
|
16
|
+
; program, which arguments, in which order, with which working directory) are
|
|
17
|
+
; the part that has bugs, and those can be checked anywhere. Set `dryRun` and
|
|
18
|
+
; every call records its command line and answers success without running
|
|
19
|
+
; anything.
|
|
20
|
+
;
|
|
21
|
+
; There is no shell in the middle of any of this. The arguments are passed as a
|
|
22
|
+
; vector, so a path with a space in it, an argument that starts with a dash and
|
|
23
|
+
; a filename containing a `*` all arrive at the program exactly as written. The
|
|
24
|
+
; `commandLine` text this file builds is for READING -- a log line, an error
|
|
25
|
+
; message, a dry run transcript -- and is quoted well enough to paste back into
|
|
26
|
+
; a shell, but nothing is ever executed through one.
|
|
27
|
+
;
|
|
28
|
+
; def sh:Shell (new Shell)
|
|
29
|
+
; sh.cwd = "/path/to/project"
|
|
30
|
+
; def argv:[string]
|
|
31
|
+
; push argv "rev-parse"
|
|
32
|
+
; push argv "HEAD"
|
|
33
|
+
; def r:ShellResult (sh.capture("git" argv))
|
|
34
|
+
; if (r.ok()) {
|
|
35
|
+
; print (r.outText())
|
|
36
|
+
; }
|
|
37
|
+
;
|
|
38
|
+
; Build the argument vector rather than writing it as an inline literal: an
|
|
39
|
+
; array literal handed straight to a call is lost when the call result is
|
|
40
|
+
; immediately dereferenced, and a one-element one is flattened on Rust
|
|
41
|
+
; (ISSUES.md #84, #85).
|
|
42
|
+
;
|
|
43
|
+
; =============================================================================
|
|
44
|
+
|
|
45
|
+
; What a finished command answers: its exit code, whatever it printed, and the
|
|
46
|
+
; command line it was asked to run (so an error can name it without the caller
|
|
47
|
+
; having to keep it).
|
|
48
|
+
class ShellResult {
|
|
49
|
+
|
|
50
|
+
def code:int 0
|
|
51
|
+
def out:string ""
|
|
52
|
+
def err:string ""
|
|
53
|
+
def command:string ""
|
|
54
|
+
; True when the command was recorded but never run, which is what a dry run
|
|
55
|
+
; does. A skipped command answers ok() -- a dry run is not a failure -- so
|
|
56
|
+
; this is how a caller tells "it worked" from "nothing happened".
|
|
57
|
+
def skipped:boolean false
|
|
58
|
+
|
|
59
|
+
fn ok:boolean () {
|
|
60
|
+
return (code == 0)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
fn failed:boolean () {
|
|
64
|
+
return (code != 0)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
; stdout without the trailing newline every command line program writes.
|
|
68
|
+
fn outText:string () {
|
|
69
|
+
return (trim out)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fn errText:string () {
|
|
73
|
+
return (trim err)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fn outLines:[string] () {
|
|
77
|
+
def body:string (trim out)
|
|
78
|
+
if ((strlen body) == 0) {
|
|
79
|
+
def empty:[string]
|
|
80
|
+
return empty
|
|
81
|
+
}
|
|
82
|
+
return (strsplit body "\n")
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
; The first line of stdout, which is the whole answer for most of the
|
|
86
|
+
; version and locate style tools a build driver asks things of.
|
|
87
|
+
fn outFirstLine:string () {
|
|
88
|
+
def lines:[string] (this.outLines())
|
|
89
|
+
if ((array_length lines) == 0) {
|
|
90
|
+
return ""
|
|
91
|
+
}
|
|
92
|
+
return (trim (itemAt lines 0))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
; What went wrong, in one line, for an error message: the exit code, the
|
|
96
|
+
; command, and whichever of stderr/stdout has something to say.
|
|
97
|
+
fn describeFailure:string () {
|
|
98
|
+
def why:string (trim err)
|
|
99
|
+
if ((strlen why) == 0) {
|
|
100
|
+
why = (trim out)
|
|
101
|
+
}
|
|
102
|
+
def head:string ("command failed (exit " + code + "): " + command)
|
|
103
|
+
if ((strlen why) == 0) {
|
|
104
|
+
return head
|
|
105
|
+
}
|
|
106
|
+
return (head + "\n" + why)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
; A working directory, a log, and a switch that turns every call into a record
|
|
111
|
+
; of what would have run.
|
|
112
|
+
class Shell {
|
|
113
|
+
|
|
114
|
+
; The directory commands run in. "" means the one this program is in.
|
|
115
|
+
def cwd:string ""
|
|
116
|
+
; Record commands instead of running them. Every call answers exit 0 with
|
|
117
|
+
; no output and `skipped` set.
|
|
118
|
+
def dryRun:boolean false
|
|
119
|
+
; Print each command line before running it.
|
|
120
|
+
def verbose:boolean false
|
|
121
|
+
; Extra environment entries for every command this Shell runs, as
|
|
122
|
+
; "NAME=VALUE". Merged over the ones this program was started with rather
|
|
123
|
+
; than replacing them, so a child never loses PATH.
|
|
124
|
+
def env:[string]
|
|
125
|
+
; Every command line this Shell was asked for, in order, dry run or not.
|
|
126
|
+
def commands:[string]
|
|
127
|
+
; The exit code of the last command that actually ran.
|
|
128
|
+
def lastCode:int 0
|
|
129
|
+
|
|
130
|
+
Constructor () {
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
; Set an environment variable for every command this Shell runs from here
|
|
134
|
+
; on. A toolchain driver needs this more often than it looks: SDKROOT,
|
|
135
|
+
; DEVELOPER_DIR and RANGER_LIB are all read out of the environment by the
|
|
136
|
+
; programs they configure, and none of them can be passed as an argument.
|
|
137
|
+
fn setEnv:void (name:string value:string) {
|
|
138
|
+
def prefix:string (name + "=")
|
|
139
|
+
def kept:[string]
|
|
140
|
+
for env pair:string i {
|
|
141
|
+
if ((indexOf pair prefix) != 0) {
|
|
142
|
+
push kept pair
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
push kept (prefix + value)
|
|
146
|
+
env = kept
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
; Run a program and let its output land on ours as it happens. This is what
|
|
150
|
+
; you want for a compiler or a linker: the progress is the point, and there
|
|
151
|
+
; is nothing to parse afterwards.
|
|
152
|
+
fn run:ShellResult (program:string argv:[string]) {
|
|
153
|
+
return (this.exec(program argv false))
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
; Run a program and keep what it printed. `out` and `err` come back in the
|
|
157
|
+
; result and nothing reaches this program's own output.
|
|
158
|
+
fn capture:ShellResult (program:string argv:[string]) {
|
|
159
|
+
return (this.exec(program argv true))
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
; The trimmed stdout of a captured run, for the many tools whose entire
|
|
163
|
+
; answer is one line. A failure answers "" rather than throwing, so a caller
|
|
164
|
+
; that only wants the happy path does not have to unwrap anything.
|
|
165
|
+
fn text:string (program:string argv:[string]) {
|
|
166
|
+
def res:ShellResult (this.capture(program argv))
|
|
167
|
+
if (res.failed()) {
|
|
168
|
+
return ""
|
|
169
|
+
}
|
|
170
|
+
return (res.outText())
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
; Did this program run and succeed? Output is captured and dropped, which is
|
|
174
|
+
; what you want when the question is only "does this work here".
|
|
175
|
+
fn succeeds:boolean (program:string argv:[string]) {
|
|
176
|
+
def res:ShellResult (this.capture(program argv))
|
|
177
|
+
return (res.ok())
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
; Where a program is on PATH, or absent when it is not installed. An
|
|
181
|
+
; argument with a "/" in it is a path already and is answered as it stands
|
|
182
|
+
; when the file is there -- the same rule a shell follows.
|
|
183
|
+
fn which@(optional):string (program:string) {
|
|
184
|
+
; One optional, assigned where an answer is found and returned once.
|
|
185
|
+
; Several `def x@(optional):string value` in branches is the obvious
|
|
186
|
+
; way to write this, and the Rust writer turns the initialiser into
|
|
187
|
+
; `Some(program).to_string()`, so it is written the way that survives
|
|
188
|
+
; every target.
|
|
189
|
+
def found@(optional):string
|
|
190
|
+
def slashAt:int (indexOf program "/")
|
|
191
|
+
if (slashAt >= 0) {
|
|
192
|
+
def dirPart:string (path_dirname program)
|
|
193
|
+
def namePart:string (this.baseName(program))
|
|
194
|
+
if (file_exists dirPart namePart) {
|
|
195
|
+
found = program
|
|
196
|
+
}
|
|
197
|
+
return found
|
|
198
|
+
}
|
|
199
|
+
def pathVar@(optional):string (env_var "PATH")
|
|
200
|
+
if (null? pathVar) {
|
|
201
|
+
return found
|
|
202
|
+
}
|
|
203
|
+
def pathText:string (unwrap pathVar)
|
|
204
|
+
; ":" everywhere but Windows, which uses ";" -- and where a ":" is also
|
|
205
|
+
; the drive letter separator inside every entry, so the presence of a
|
|
206
|
+
; ";" is what decides rather than the platform.
|
|
207
|
+
def sep:string ":"
|
|
208
|
+
if ((indexOf pathText ";") >= 0) {
|
|
209
|
+
sep = ";"
|
|
210
|
+
}
|
|
211
|
+
def dirs:[string] (strsplit pathText sep)
|
|
212
|
+
for dirs dir:string i {
|
|
213
|
+
if (null? found) {
|
|
214
|
+
if ((strlen dir) > 0) {
|
|
215
|
+
if (file_exists dir program) {
|
|
216
|
+
found = (dir + "/" + program)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return found
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
; Is this program installed at all? Asked before a build starts, so a
|
|
225
|
+
; missing toolchain is one clear message instead of a confusing failure
|
|
226
|
+
; three commands later.
|
|
227
|
+
fn isInstalled:boolean (program:string) {
|
|
228
|
+
def found@(optional):string (this.which(program))
|
|
229
|
+
if (null? found) {
|
|
230
|
+
return false
|
|
231
|
+
}
|
|
232
|
+
return true
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
; The command line as a human would write it. For logs, errors and dry run
|
|
236
|
+
; transcripts only -- nothing is executed through a shell.
|
|
237
|
+
fn commandLine:string (program:string argv:[string]) {
|
|
238
|
+
def parts:[string]
|
|
239
|
+
; The environment first, the way a shell would take it, so a logged or
|
|
240
|
+
; dry-run line can be pasted back into a terminal and mean the same
|
|
241
|
+
; thing.
|
|
242
|
+
for env pair:string i {
|
|
243
|
+
def at:int (indexOf pair "=")
|
|
244
|
+
if (at > 0) {
|
|
245
|
+
def name:string (substring pair 0 at)
|
|
246
|
+
def value:string (substring pair (at + 1) (strlen pair))
|
|
247
|
+
push parts (name + "=" + (this.quote(value)))
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
push parts (this.quote(program))
|
|
251
|
+
for argv a:string i {
|
|
252
|
+
push parts (this.quote(a))
|
|
253
|
+
}
|
|
254
|
+
return (join parts " ")
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
; Wrap an argument in single quotes when it holds anything a shell would
|
|
258
|
+
; treat as syntax, so the logged line can be pasted back into a terminal.
|
|
259
|
+
fn quote:string (a:string) {
|
|
260
|
+
if ((strlen a) == 0) {
|
|
261
|
+
return "''"
|
|
262
|
+
}
|
|
263
|
+
; Character classes as a string to look in rather than as code point
|
|
264
|
+
; arithmetic: `charAt` answers a `char`, and what a `char` IS differs
|
|
265
|
+
; enough between targets that the arithmetic form does not compile
|
|
266
|
+
; everywhere. `indexOf` over a one-character substring does.
|
|
267
|
+
def safe:string "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/._-=:+,@"
|
|
268
|
+
def plain:boolean true
|
|
269
|
+
def i:int 0
|
|
270
|
+
def n:int (strlen a)
|
|
271
|
+
while (i < n) {
|
|
272
|
+
def ch:string (substring a i (i + 1))
|
|
273
|
+
if ((indexOf safe ch) < 0) {
|
|
274
|
+
plain = false
|
|
275
|
+
}
|
|
276
|
+
i = i + 1
|
|
277
|
+
}
|
|
278
|
+
if plain {
|
|
279
|
+
return a
|
|
280
|
+
}
|
|
281
|
+
; Single quotes, because a POSIX shell re-reads nothing inside them.
|
|
282
|
+
; A single quote in the argument itself ends the run, is escaped on its
|
|
283
|
+
; own, and a new run is opened: the '"'"'-'\''-'"'"' dance every shell
|
|
284
|
+
; script eventually writes by hand.
|
|
285
|
+
def q:string "'"
|
|
286
|
+
def escaped:[string] (strsplit a q)
|
|
287
|
+
def glue:string ("'" + "\\" + "'" + "'")
|
|
288
|
+
return (q + (join escaped glue) + q)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
; The last path segment, which is the program name when a caller passed a
|
|
292
|
+
; path rather than a name.
|
|
293
|
+
fn baseName:string (p:string) {
|
|
294
|
+
def parts:[string] (strsplit p "/")
|
|
295
|
+
def cnt:int (array_length parts)
|
|
296
|
+
if (cnt == 0) {
|
|
297
|
+
return p
|
|
298
|
+
}
|
|
299
|
+
return (itemAt parts (cnt - 1))
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
; Everything above lands here: log the line, honour the dry run, run it,
|
|
303
|
+
; and turn the three strings the compiler answers into a result object.
|
|
304
|
+
fn exec:ShellResult (program:string argv:[string] capture:boolean) {
|
|
305
|
+
def line:string (this.commandLine(program argv))
|
|
306
|
+
push commands line
|
|
307
|
+
if verbose {
|
|
308
|
+
print "$ " + line
|
|
309
|
+
}
|
|
310
|
+
def res:ShellResult (new ShellResult)
|
|
311
|
+
res.command = line
|
|
312
|
+
if dryRun {
|
|
313
|
+
res.skipped = true
|
|
314
|
+
res.code = 0
|
|
315
|
+
return res
|
|
316
|
+
}
|
|
317
|
+
def raw:[string] (run_process_result program argv cwd capture env)
|
|
318
|
+
def codeText:string (itemAt raw 0)
|
|
319
|
+
def parsed@(optional):int (to_int codeText)
|
|
320
|
+
res.code = (?? parsed (0 - 1))
|
|
321
|
+
res.out = (itemAt raw 1)
|
|
322
|
+
res.err = (itemAt raw 2)
|
|
323
|
+
lastCode = res.code
|
|
324
|
+
return res
|
|
325
|
+
}
|
|
326
|
+
}
|
package/dist/lib/Storage.rgr
CHANGED
package/dist/lib/Time.rgr
CHANGED
package/dist/lib/Timers.rgr
CHANGED
package/dist/lib/TypedArrays.rgr
CHANGED
package/dist/lib/ViewLib.rgr
CHANGED
package/dist/lib/WebLib.rgr
CHANGED