░░███
█████████████ ██████ ██████ ░███████
░░███░░███░░███ ░░░░░███ ███░░███ ░███░░███
░███ ░███ ░███ ███████ ░███ ░░░ ░███ ░███
░███ ░███ ░███ ███░░███ ░███ ███ ░███ ░███
█████░███ █████░░████████░░██████ ████ █████
░░░░░ ░░░ ░░░░░ ░░░░░░░░ ░░░░░░ ░░░░ ░░░░░
Mach is a tiny build system for OCaml scripts. It compiles code on first run, subsequent runs perform incremental compilation, if needed.
Mach supports dependencies between scripts, on libraries, and on external
libraries (usually installed with opam) through #require directives.
Usage is as simple as creating an .ml file:
#require "./utils"
#require "lwt"
let () =
Lwt_main.run (Utils.greet "Mach")
and then running:
$ mach run main.ml
Below is the documentation for installation and usage:
Mach is distributed as a single mach.ml source file. It requires only OCaml
toolchain to be installed.
Install using homebrew (macOS/Linux):
$ brew tap mach-build/tap
$ brew install mach --HEAD
Apart from mach executable this install bash/zsh completion scripts and a man
page.
Requires OCaml compiler installed:
$ wget https://raw.githubusercontent.com/andreypopp/mach/refs/heads/main/_dist/mach.ml
$ ocamlopt -I +unix -o mach unix.cmxa mach.ml
Create hello.ml file:
let () =
print_endline "Hello, Mach!"
Run it with mach run command:
$ mach run hello.ml
Hello, Mach!
One can also put #!/usr/bin/env mach run -- shebang line at the top of the file:
#!/usr/bin/env mach run --
let () =
print_endline "Hello, Mach!"
Make it executable and run:
$ chmod +x hello.ml
$ ./hello.ml
Hello, Mach!
Scripts can reference other scripts using #require directive. File extensions
are omitted — Mach automatically resolves .ml and .mlx files:
#require "./utils"
let () =
Utils.greet "Mach"
Where utils.ml contains:
let greet name =
Printf.printf "Hello, %s!\n" name
Run it:
$ mach run main.ml
Hello, Mach!
You can also depend on external libraries:
#require "lwt"
let () =
let task =
Lwt_io.printf "Hello from Lwt!\n"
in
Lwt_main.run task
Run it:
$ mach run lwt_example.ml
Hello from Lwt!
External libraries require ocamlfind to be installed.
You can organize code into libraries. A library is a directory containing a
Machlib file and one or more OCaml modules.
Create a library directory structure:
mylib/
Machlib
foo.ml
bar.ml
The Machlib file can be empty, or declare dependencies using (require ...)
syntax:
(require lwt unix)
Create modules in the library:
(* mylib/foo.ml *)
let greet name = Printf.printf "Hello, %s!\n" name
(* mylib/bar.ml *)
let message = "from Bar"
Library modules can reference each other freely — Mach automatically determines the correct compilation order based on dependencies.
Use the library from a script:
#require "./mylib"
let () =
Foo.greet "World";
print_endline Bar.message
To compile without running, use mach build command:
$ mach build hello.ml
This is useful to get compilation errors without executing the code.
Both mach build and mach run commands support --watch mode that starts
watching source code for changes and rebuilds (and reruns in case of mach run) the code on each change:
$ mach run --watch hello.ml
Note that watchexec tool is required for this feature to work. Install it
using your package manager (e.g. brew install watchexec on macOS).
Install mach-lsp package for LSP support:
$ opam install mach-lsp
Configure your editor to use mach-lsp as the language server for OCaml files.
Mach supports .mlx syntax dialect out of the box:
let div ~children () =
String.concat ", " children
let () =
print_endline <div>"Hello, MLX!"</div>
Run it:
$ mach run example.mlx
The source code is at andreypopp/mach. Please open issues (and pull requests) for any bugs and/or feature requests.