src/tool/run.bend
ezx@1.11.0.0 · 379 lines · 34 definitions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21import Base
22import snap@1.4.0.0/main.bend as R
23import ezx@1.11.0.0/src/io/file.bend as F
24import ezx@1.11.0.0/src/lock/run.bend as Run
25import ezx@1.11.0.0/src/lock/plan.bend as LP
26import ezx@1.11.0.0/src/lock/up.bend as Up
27import ezx@1.11.0.0/src/git/git.bend as Git
28import ezx@1.11.0.0/src/fetch/run.bend as FetchRun
29import ezx@1.11.0.0/src/pkg/path.bend as P
30import ezx@1.11.0.0/src/run/bend.bend as Bend
31import ezx@1.11.0.0/src/share/cap.bend as Cap
32import ezx@1.11.0.0/src/share/say.bend as Say
33import ezx@1.11.0.0/src/share/spin.bend as Spin
34import ezx@1.11.0.0/src/share/pass.bend as Pass
35import ezx@1.11.0.0/src/tool/world.bend as TW
36import ezx@1.11.0.0/src/tool/plan.bend as TP
37
38
39
40
41
42def line0(ls: List<&2, String>) -> String:
43 match ls:
44 case []:
45 ""
46 case h <> _t:
47 h
48
49
50
51
52
53
54def said.next(hit: Bool, +found: String, go: Unit -> String) -> String:
55 match hit:
56 case True{}:
57 found
58 case False{}:
59 go(Unit{})
60
61
62def said.rev(ls: List<&2, String>) -> String:
63 match ls:
64 case []:
65 ""
66 case +h <> t:
67 said.next(Git.is_rev(h), h, _u => said.rev(t))
68
69
70def line.abs(+line: String) -> Bool:
71 String.eq(String.take(line, 1n), "/")
72
73
74def said.abs(ls: List<&2, String>) -> String:
75 match ls:
76 case []:
77 ""
78 case +h <> t:
79 said.next(line.abs(h), h, _u => said.abs(t))
80
81
82def said.when(ok: Bool, read: Unit -> String) -> String:
83 match ok:
84 case True{}:
85 read(Unit{})
86 case False{}:
87 ""
88
89
90def said.head(+out: String) -> String:
91 said.when(R.ok(out), _u => said.rev(String.lines(R.text(out))))
92
93
94def said.top(+out: String) -> String:
95 said.when(R.ok(out), _u => said.abs(String.lines(R.text(out))))
96
97
98
99
100def located(+abs: String) -> IO(TW.Answer):
101 do IO<TW.Answer>:
102 +top : String <- R.exec(["git", "-C", abs, "rev-parse", "--show-toplevel"])
103 +head : String <- R.exec(["git", "-C", abs, "rev-parse", "HEAD"])
104 +st : String <- R.exec(["git", "-C", abs, "status", "--porcelain",
105 "--untracked-files=normal"])
106 return TW.Located{abs, said.top(top), said.head(head), R.ok(st),
107 String.trim(R.text(st))}
108
109
110def locate.real(ok: Bool, +path: String, +text: String) -> IO(TW.Answer):
111 match ok:
112 case False{}:
113 IO.pure(TW.Answer, TW.Miss{"ez: " ++ path ++ ": " ++ String.trim(text)})
114 case True{}:
115 located(String.trim(text))
116
117
118def locate(+path: String) -> IO(TW.Answer):
119 do IO<TW.Answer>:
120 +out : String <- R.exec(["realpath", path])
121 locate.real(R.ok(out), path, R.text(out))
122
123
124def answer(ask: TW.Ask) -> IO(TW.Answer):
125 match ask:
126 case TW.Read{path}:
127 do IO<TW.Answer>:
128 m : Maybe<&2, String> <- F.read(path)
129 return TW.Plain{m}
130 case TW.Exec{path}:
131 do IO<TW.Answer>:
132 +out : String <- R.exec(["test", "-x", path])
133 return TW.Is{R.ok(out)}
134 case TW.Locate{path}:
135 locate(path)
136
137
138def answer.all(asks: List<&2, TW.Ask>) -> IO(List<&2, TW.Reply>):
139 match asks:
140 case []:
141 IO.pure(List<&2, TW.Reply>, [])
142 case +h <> t:
143 do IO<List<&2, TW.Reply>>:
144 a : TW.Answer <- answer(h)
145 rest : List<&2, TW.Reply> <- answer.all(t)
146 return TW.Reply{h, a} <> rest
147
148
149def more(world: TW.World, rs: List<&2, TW.Reply>, us: List<&2, Up.Reply>) -> TW.World:
150 TW.World{a, t, want, here, env, bend, ledger, lock, replies, ups} = world
151 TW.World{a, t, want, here, env, bend, ledger, lock, List.append(&2, TW.Reply, replies, rs),
152 List.append(&2, Up.Reply, ups, us)}
153
154
155
156
157
158
159def exec.lay(layout: TP.Lay) -> IO(Unit):
160 match layout:
161 case TP.Still{}:
162 IO.pure(Unit, Unit{})
163 case TP.Lay{dir, ws}:
164 do IO<Unit>:
165 _rm : String <- R.exec(["rm", "-rf", dir])
166 Run.exec.all("", ws)
167
168
169
170def compile.said(ok: Bool, +why: String, +text: String) -> IO(Unit):
171 match ok:
172 case True{}:
173 IO.pure(Unit, Unit{})
174 case False{}:
175 do IO<Unit>:
176 IO.print_err(String.trim_end(why ++ text))
177 IO.die(Unit, 1, "")
178
179
180def compile(+lib: String, +file: String, +out: String) -> IO(Unit):
181 do IO<Unit>:
182 +on : Bool <- Cap.ok()
183 Cap.warn.err(on)
184 +g : String <- Cap.gb()
185 _mk : String <- R.exec(["mkdir", "-p", P.dir(out)])
186 +bin : String <- R.exec(Cap.argv(on, g, lib, ["bend", file, "-o", out]))
187 compile.said(R.ok(bin), Cap.why(bin, g), R.text(bin))
188
189
190
191
192def exec.lib(fetch: Bool, +top: String, +lib: String) -> IO(Unit):
193 match fetch:
194 case True{}:
195 FetchRun.fill(top, lib)
196 case False{}:
197 do IO<Unit>:
198 _mk : String <- R.exec(["mkdir", "-p", lib])
199 return Unit{}
200
201
202
203def exec.make(making: TP.Make) -> IO(Unit):
204 match making:
205 case TP.Skip{}:
206 IO.pure(Unit, Unit{})
207 case TP.Reuse{name}:
208 IO.print_err(Say.reused(name))
209 case TP.Make{+top, +lib, fetch, file, out, at, key, +name}:
210 do IO<Unit>:
211 exec.lib(fetch, top, lib)
212 +s : Say.Spin <- Spin.hold(Say.building(name))
213 compile(lib, file, out)
214 Spin.done(s)
215 Run.write(at, key)
216
217
218def tell(ls: List<&2, String>) -> IO(Unit):
219 match ls:
220 case []:
221 IO.pure(Unit, Unit{})
222 case h <> t:
223 do IO<Unit>:
224 IO.print_err(h)
225 tell(t)
226
227def link.after(ok: Bool, +at: String, +text: String, says: List<&2, String>) -> IO(Unit):
228 match ok:
229 case False{}:
230 IO.die(Unit, 1, "ez: " ++ at ++ ": " ++ String.trim(text))
231 case True{}:
232 tell(says)
233
234
235
236
237
238def exec.finish(fin: TP.Finish) -> IO(U32):
239 match fin:
240 case TP.Idle{}:
241 IO.pure(U32, 0)
242 case TP.Launch{bin, args}:
243 Pass.run(List.append(&2, String, [bin], args))
244 case TP.Link{bin, +dir, +name, says}:
245 +at = dir ++ "/" ++ name
246 do IO<U32>:
247 IO.print_err(Say.linking(at))
248 _mk : String <- R.exec(["mkdir", "-p", dir])
249 +ln : String <- R.exec(["ln", "-sfn", bin, at])
250 link.after(R.ok(ln), at, R.text(ln), says)
251 return 0
252
253
254
255def exec.plan(+pl: TP.Plan) -> IO(Unit):
256 TP.Plan{l, m, f, o} = pl
257 do IO<Unit>:
258 exec.lay(l)
259 exec.make(m)
260 s : U32 <- exec.finish(f)
261 +code : U32 <- IO.pure(U32, TP.code(TP.exit(pl), s))
262 Run.end.at(U32.is_eq(code, 0), code, LP.why(o))
263
264
265
266
267def loop.step(st: TP.Step, world: TW.World, go: TW.World -> IO(Unit)) -> IO(Unit):
268 match st:
269 case TP.Asking{asks, ups}:
270 do IO<Unit>:
271 rs : List<&2, TW.Reply> <- answer.all(asks)
272 us : List<&2, Up.Reply> <- Run.answer.ups("", ups)
273 go(more(world, rs, us))
274 case TP.Run{pl}:
275 exec.plan(pl)
276
277
278
279
280def rounds() -> Nat:
281 U32.to_nat(100)
282
283
284
285
286def loop(fuel: Nat, +world: TW.World) -> IO(Unit):
287 match fuel:
288 case 0n:
289 IO.die(Unit, 1, "ez: ez tool kept asking questions")
290 case 1n+f:
291 loop.step(TP.step(world), world, w => loop(f, w))
292
293
294def var(name: String) -> IO(String):
295 do IO<String>:
296 r : Result<&1, &1, U32 & String, String> <- IO.get_env(name)
297 return Run.env.text(r)
298
299
300
301def bend.is() -> IO(String):
302 do IO<String>:
303 +out : String <- Bend.version()
304 return Bool.pick(String, R.ok(out), line0(String.lines(String.trim(R.text(out)))), "")
305
306
307def env() -> IO(TW.Env):
308 do IO<TW.Env>:
309 +b : String <- var("EZ_TOOL_BIN")
310 +x : String <- var("XDG_BIN_HOME")
311 +c : String <- var("XDG_CACHE_HOME")
312 +h : String <- var("HOME")
313 +p : String <- var("PATH")
314 return TW.Env{b, x, c, h, p}
315
316
317
318def go(
319 act: TW.Act,
320 +target: String,
321 +want: String,
322 +here: String,
323 +vars: TW.Env,
324 +bend: String,
325 +led: Maybe<&2, String>,
326 +lock: Maybe<&2, String>
327) -> IO(Unit):
328 loop(rounds(), TW.World{act, target, want, here, vars, bend, led, lock, [], []})
329
330
331def run(act: TW.Act, +target: String, +want: String) -> IO(Unit):
332 do IO<Unit>:
333 +pwd : String <- R.exec(["pwd"])
334 +vars : TW.Env <- env()
335 +bend : String <- bend.is()
336 led : Maybe<&2, String> <- F.read("ez.toml")
337 lock : Maybe<&2, String> <- F.read("ez.lock.toml")
338 go(act, target, want, String.trim(R.text(pwd)), vars, bend, led, lock)
339
340
341def sync.all(
342 ns: List<&2, String>,
343 +here: String,
344 +vars: TW.Env,
345 +bend: String,
346 +led: Maybe<&2, String>,
347 +lock: Maybe<&2, String>
348) -> IO(Unit):
349 match ns:
350 case []:
351 IO.pure(Unit, Unit{})
352 case +h <> t:
353 do IO<Unit>:
354 go(TW.Install{}, h, "", here, vars, bend, led, lock)
355 sync.all(t, here, vars, bend, led, lock)
356
357
358
359def sync.go(
360 +synced: TP.Sync,
361 +here: String,
362 +vars: TW.Env,
363 +bend: String,
364 +led: Maybe<&2, String>,
365 +lock: Maybe<&2, String>
366) -> IO(Unit):
367 do IO<Unit>:
368 sync.all(TP.sync.names(synced), here, vars, bend, led, lock)
369 Run.end(TP.sync.outcome(synced))
370
371
372def sync() -> IO(Unit):
373 do IO<Unit>:
374 +pwd : String <- R.exec(["pwd"])
375 +vars : TW.Env <- env()
376 +bend : String <- bend.is()
377 +led : Maybe<&2, String> <- F.read("ez.toml")
378 +lock : Maybe<&2, String> <- F.read("ez.lock.toml")
379 sync.go(TP.sync(led, lock), String.trim(R.text(pwd)), vars, bend, led, lock)