A small task runner
Commands live in fach.bnml next to your project.
Requires Odin. Config is BNML. The CLI is a thin wrapper around package fach.
fach init
fach run build
fach list
fach get name
fach dep
Build
From the repository root:
odin build cmd/fach -out:fach
Put the binary on your PATH, or run it as ./fach.
CLI
fach reads fach.bnml from the current directory. Blank lines and whole-line ; comments are ignored. fach init writes a starter file instead of reading one.
fach init
—
Write a starter fach.bnml. Errors if one already exists.
fach run <name>
project, entrypoints
Run a public entrypoint.
fach list
entrypoints
List public entrypoints.
fach get <path>
project
Print a leaf project value — name, out.dir, and so on.
fach dep
deps
List deps.
private is optional. run only takes a public name; a public entrypoint can inline a private one with [[name]].
Config
Four roots, numbered indent. Descriptions on an entrypoint line show up in fach list; the same pattern on a dep is a hint for fach dep.
0 project
1 name: hello
1 version: 0.1.0
0 deps
1 odin: The Odin compiler
0 entrypoints
1 build: compile the binary
2 cmds
3 odin build . -out:{{name}}
2 regardless
3 echo done
0 private
1 clean
2 cmds
3 rm -rf {{name}}
fach init writes a thinner starter:
; A new fach.bnml
0 project
0 deps
0 entrypoints
Entrypoints
Each public or private entrypoint has cmds (required) and optional regardless. regardless always runs after cmds; every line runs; the exit code is still that of cmds.
cmds share one shell, so cd and export persist into later cmds and into regardless.
Command text is the BNML key. If you need a colon in the command, write it as key: value — BNML splits on the first unescaped colon, and fach joins the key and value back with : .
Inside commands
{{path}}- Substitute a leaf
projectvalue, quoted for the shell. Dots walk children:{{out.dir}}. [name]- As a whole command, inline that public entrypoint.
[[name]]- As a whole command, inline a
privateentrypoint. [[- A literal
[.
[name] and [[name]] only inline when the whole line is that ref. cd src && [build] is not an inline; use two children (cd src, then [build]).
Public and private inlines share a cycle check. Missing values, missing entrypoints, and missing privates are errors.
Example
fach init writes a starter file; this example is fuller. Copy it to a project root as fach.bnml, then try fach list, fach dep, fach get name, fach get out.dir, and fach run check.
; Example fach.bnml. `fach init` writes a starter file; this example is fuller.
; Copy into a project root as fach.bnml, or start from `fach init`, then:
; fach list
; fach dep
; fach get name
; fach get out.dir
; fach run check
0 project
1 name: hello
1 version: 0.1.0
1 out
2 dir: build
0 deps
1 odin: The Odin compiler
1 sh: POSIX shell
0 entrypoints
1 build: print a fake build
2 cmds
3 echo building {{name}} {{version}} into {{out.dir}}
2 regardless
3 echo done
1 test: print a fake test
2 cmds
3 echo tests
3 echo note: colons are kept
1 check: setup, then test, then build
2 cmds
3 [[setup]]
3 [test]
3 [build]
0 private
1 setup
2 cmds
3 echo private setup for {{name}}
3 echo a literal bracket: [[name]
Library
package fach is the library; the CLI is a wrapper around it. Imports are relative to the importing file. Default config path is fach.bnml (CONFIG_FILE). Pass another path to init or load if you want.
Error.kind == .None is success; Error.text is an allocated message.
import fach "path/to/fach"
file, err := fach.load()
if err.kind != .None {
fmt.eprintln(err.text)
return
}
defer fach.destroy(&file)
code, err := fach.run(file, "build")
init
Error
Write a starter file (STARTER). Error if it already exists.
load
File, Error
Read and parse a fach.bnml.
destroy
—
Free a loaded File.
get
string, Error
Leaf project value. Needs project.
list_deps
[]Dep, Error
Dep names and hints. Needs deps.
list_entrypoints
[]Entrypoint, Error
Public names and descriptions. Needs entrypoints.
run
int, Error
Run a public entrypoint. Needs project and entrypoints.
run’s int is the shell exit code. A non-.None Error is a fach failure — missing schema, unknown name, bad entrypoint — not that exit code.
Strings from get, Dep, and Entrypoint are borrowed from the File; keep it alive. delete the slices from list_deps and list_entrypoints.
Layout
fach.odin
Library (package fach).
cmd/fach/main.odin
CLI.
examples/
Sample fach.bnml.
vendor/bnml/
BNML parser.