This chapter walks you through installing Mako, creating your first project, understanding the project structure, and setting up your editor.
To build and run Mako programs you need:
apt install clang on
Linux, or LLVM on Windows)Optional dependencies for full standard library support:
From a source checkout:
make install
This installs the mako binary to ~/.local/bin/mako and runtime headers to
~/.local/share/mako/runtime. Make sure ~/.local/bin is in your PATH.
Alternatively, use the install script directly:
./scripts/install.sh
Verify the installation:
mako version
# mako version mako0.1.0 darwin/arm64
The --version flag produces the same output:
mako --version
# mako version mako0.1.0 darwin/arm64
For verbose output including the git commit (when available):
mako version -v
If you want to build from the repository:
git clone https://github.com/mako-lang/mako.git
cd mako
cargo build --release
You can run the compiler directly without installing:
cargo run --release -- version
cargo run --release -- run examples/hello.mko
Then install when ready:
make install
cargo build --release
.\scripts\install.ps1
mako version
On Windows, ensure that clang is available in your PATH. The easiest way is to install the LLVM toolchain from the official LLVM releases page.
The compiler looks for runtime headers at $PREFIX/share/mako/runtime. If you
installed to a non-standard location, set the MAKO_RUNTIME environment
variable:
export MAKO_RUNTIME=/opt/mako/runtime
After installation, run mako doctor to verify your environment is correctly
configured:
mako doctor
This checks:
mako binary is in PATH and executableIf anything is misconfigured, mako doctor prints actionable guidance on how
to fix it.
Create a file called hello.mko:
fn main() {
print("hello from mako")
}
Run it:
mako run hello.mko
# hello from mako
That is the entire workflow. mako run compiles the source to C, invokes clang,
and executes the resulting binary in one step.
For anything beyond a single file, use mako init to scaffold a project:
mako init myapp --name myapp
cd myapp
This creates:
myapp/
mako.toml -- project manifest
main.mko -- entry point
Run the generated project:
mako run main.mko
For an HTTP-oriented service layout:
mako init mysvc --backend
cd mysvc
mako run main.mko
This generates a project with HTTP server boilerplate and route handlers.
For a project with multiple members (library + application):
mako init myws --workspace
cd myws
mako check .
mako run -p app
The mako.toml file is the project manifest. It declares the project name,
version, dependencies, and build configuration:
[package]
name = "myapp"
version = "0.1.0"
[dependencies]
# path dependencies
utils = { path = "../utils" }
# registry dependencies (when available)
# json = "1.0"
[build]
# parallel compilation jobs
jobs = 8
When you run mako build main.mko in a directory with a mako.toml, the
binary name is derived from the package name.
| Command | What it does |
|---|---|
mako run file.mko |
Compile and execute in one step |
mako check file.mko |
Type-check without producing a binary (fast) |
mako build file.mko |
Compile to a native binary |
mako build --release file.mko |
Optimized build (-O3 -flto) |
mako build -j 8 file.mko |
Parallel object compilation |
mako test examples/testing |
Run the test suite |
mako fmt file.mko |
Format source to canonical style |
Incremental compilation is on by default. The compiler caches intermediate artifacts and only recompiles translation units that have changed. This makes the edit-compile-run loop fast even for larger projects.
For production deployment, always use --release:
mako build --release main.mko
This enables -O3 optimization and link-time optimization (-flto), producing
a smaller, faster binary. The resulting binary is a single statically-linked
executable suitable for deployment anywhere.
Here is a slightly more involved example showing functions, types, and control flow:
fn main() {
print("Fibonacci calculator")
let n = 10
print_int(fib(n))
let mut sum = 0
for i in range n {
sum = sum + fib(i)
}
print_int(sum)
}
fn fib(n: int) -> int {
if n <= 1 {
return n
}
return fib(n - 1) + fib(n - 2)
}
mako run fib.mko
# Fibonacci calculator
# 55
# 88
Most projects need more than one file. Mako handles this with import -- you
point at a file, and its functions become available. When you mako run main.mko,
the compiler automatically pulls in everything that's imported.
Start with two files side by side:
// lib.mko
fn add(a: int, b: int) -> int {
return a + b
}
fn greet(name: string) -> string {
return "hi " + name
}
// main.mko
import "./lib.mko"
fn main() {
print_int(lib_add(2, 3))
print(lib_greet("mako"))
}
mako run main.mko
# 5
# hi mako
Without an alias, the imported file's functions merge into your scope directly.
By convention, prefix them (e.g. lib_add) to avoid collisions.
Give the import a name and access everything through that namespace:
// main.mko
import "./lib.mko" as lib
fn main() {
print_int(lib.add(2, 3))
}
This is especially useful when you import multiple files that might have overlapping function names.
Say you're building a small service. Start with mako init, then add files
as you go:
mako init myservice --name myservice
cd myservice
myservice/
mako.toml
main.mko
routes.mko # you add this
db.mko # you add this
// db.mko
fn db_init() {
print("database ready")
}
fn db_count() -> int {
return 42
}
// routes.mko
fn routes_health(c: int) {
let _ = http_respond_json(c, 200, "{\"ok\":true}")
}
// main.mko
import "./routes.mko"
import "./db.mko"
fn main() {
db_init()
let fd = http_bind(8080)
print("listening on :8080")
// handle requests...
}
When you have several imports, group them into a single block:
import (
"./routes.mko"
"./db.mko"
"strings"
)
The formatter (mako fmt) will automatically rewrite multiple single import
lines into this grouped form.
Mako ships with a standard library. Import its modules by name (no ./ prefix):
import "strings"
import "net/http"
fn main() {
print(strings.trim(" hello "))
}
File imports work great within a single project. When you want to share code across projects, or your codebase grows large enough to need separate build units, reach for packages and workspaces -- covered in Chapter 10: Packages.
A Mako extension is available that provides:
.mko filesmako check for inline diagnosticsmako fmtInstall it from the extensions marketplace or point your editor at the .mko
grammar file in the repository under editors/vscode/.
Add .mko filetype detection to your configuration:
autocmd BufRead,BufNewFile *.mko set filetype=mako
For LSP integration, configure the Mako language server in your LSP client settings.
mako fmt on save. This keeps all code in canonical
style and avoids formatting debates.mako check for rapid feedback without a full build.file:line:col: message format will show
inline errors.# Check your installation
mako version
mako doctor
# Create and run a project
mako init hello --name hello
cd hello
mako run main.mko
# Type-check without building
mako check main.mko
# Build a release binary
mako build --release main.mko
# Format your code
mako fmt main.mko
# Run tests
mako test examples/testing
"clang: command not found"
Install clang. On macOS: xcode-select --install. On Ubuntu/Debian:
sudo apt install clang. On Fedora: sudo dnf install clang.
"runtime headers not found"
Either run make install again or set MAKO_RUNTIME to point at the runtime
directory.
"permission denied" on ~/.local/bin/mako
Ensure ~/.local/bin exists and is writable:
mkdir -p ~/.local/bin
make install
Build cache issues
If you suspect stale cache artifacts, clean and rebuild:
mako build --clean main.mko
You now have Mako installed, know how to create projects, and can build and run programs. The next chapter is a comprehensive tour of the language syntax.
Next: Language Tour.