1 # ws/hop: where a whole-workspace command runs (EZ-WS-2). `ez lock`, `ez 2 # fetch` and `ez doctor` act on the whole workspace from any member, so run in 3 # a member they run again at the root, with the same words, and end as that 4 # run ends. Run at the root, or in a project of its own, they run where they 5 # are. Which of the three it is, and the line that runs again, are decided 6 # here; ws/run.bend only reads what the search needs and runs what this says. 7 import Base 8 import ../pkg/path.bend as P 9 import ./workspace.bend as W 10 import ./root.bend as R 11 12 # where a command runs: where it is, again at the root's directory, or 13 # nowhere, because the search for the root was refused 14 type Hop is Data: 15 Here{} 16 Hop{dir: String} 17 Stop{why: String} 18 19 # the root a member found, once it is known whether that is where ez runs 20 def hop.within(+top: String, root: List<&2, String>, rel: List<&2, String>) -> Hop: 21 match rel: 22 case []: 23 Here{} 24 case _hd <> _tl: 25 Hop{P.join(top, W.segs.text(root))} 26 27 # where a command runs, from what the search found. `top` is the top of the 28 # checkout, as git printed it. 29 def hop(+top: String, found: R.Found) -> Hop: 30 match found: 31 case R.Alone{}: 32 Here{} 33 case R.Within{root, rel}: 34 hop.within(top, root, rel) 35 case R.Broken{why}: 36 Stop{why} 37 38 # the program as it was started, found again from another directory: a path 39 # with a `/` in it taken from where ez was started, and a bare name looked up 40 # on PATH as it was 41 def program(+pwd: String, +prog: String) -> String: 42 Bool.pick(String, String.contains(prog, "/"), P.anchor(pwd, prog), prog) 43 44 # the line that runs the same command at the root: a shell that changes to the 45 # root's directory and hands every word on as it was, with no word read by the 46 # shell, which takes them as its positional parameters 47 def line(+dir: String, +pwd: String, +prog: String, words: List<&2, String>) -> List<&2, String>: 48 List.append(&2, String, 49 ["sh", "-c", "cd -- \"$1\" && shift && exec \"$@\"", "sh", dir, program(pwd, prog)], words) 50 51 # --------------------------------------------------------------------------- 52 # the library a member's own command uses 53 54 # the BEND_LIB a command run in a member uses: the one the environment sets, 55 # or, when it sets none, the root's `.ez/lib`, where the workspace's lock lays 56 # its packages 57 type Lib is Data: 58 Keep{} 59 Set{lib: String} 60 61 # the root a member found, once it is known whether that is where ez runs 62 def lib.within(+top: String, root: List<&2, String>, rel: List<&2, String>) -> Lib: 63 match rel: 64 case []: 65 Keep{} 66 case _hd <> _tl: 67 Set{P.join(P.join(top, W.segs.text(root)), ".ez/lib")} 68 69 # the library, once it is known whether the environment sets one 70 def lib.found(set: Bool, +top: String, found: R.Found) -> Lib: 71 match set: 72 case True{}: 73 Keep{} 74 case False{}: 75 match found: 76 case R.Alone{}: 77 Keep{} 78 case R.Within{root, rel}: 79 lib.within(top, root, rel) 80 case R.Broken{_why}: 81 Keep{} 82 83 # the library a member's own command uses: `env` is BEND_LIB as the 84 # environment has it, "" for unset 85 def lib(+top: String, found: R.Found, +env: String) -> Lib: 86 lib.found(Bool.not(String.is_empty(env)), top, found) 87 88 # the line that runs the same command where it is, with BEND_LIB set 89 def lib.line(+lib: String, +pwd: String, +prog: String, words: List<&2, String>) -> List<&2, String>: 90 List.append(&2, String, 91 ["sh", "-c", "BEND_LIB=\"$1\"; export BEND_LIB; shift; exec \"$@\"", "sh", lib, program(pwd, prog)], words) 92 93 # --------------------------------------------------------------------------- 94 # the lock a member's own command reads 95 96 # the root a member found, once it is known whether that is where ez runs 97 def lock.within(+top: String, root: List<&2, String>, rel: List<&2, String>) -> String: 98 match rel: 99 case []: 100 "ez.lock.toml" 101 case _hd <> _tl: 102 P.join(P.join(top, W.segs.text(root)), "ez.lock.toml") 103 104 # the lock a command run in a directory reads: the root's, which is the only 105 # one a workspace has, or its own in a project of its own and at the root 106 def lock(+top: String, found: R.Found) -> String: 107 match found: 108 case R.Alone{}: 109 "ez.lock.toml" 110 case R.Within{root, rel}: 111 lock.within(top, root, rel) 112 case R.Broken{_why}: 113 "ez.lock.toml"