Skip to content

Tools

Projects and brine.toml

A Brine project is a folder with a brine.toml at its root and its main file at src/main.br. Commands run inside the folder (or any folder below it) work on the project without arguments. A single .br file works too: every command takes a file instead, and its build goes to a cache folder in your temp directory.

Terminal window
brine new hello
Created project `hello`
cd hello
brine run
hello/
├── .git/
├── .gitignore
├── AGENTS.md
├── README.md
├── brine.toml
└── src/
└── main.br
File Contents
brine.toml the project’s name and version
src/main.br a starter program with a par comprehension and a test
AGENTS.md instructions for coding agents: the commands to run and the rules that most often trip people up
README.md a title and the two commands to build and test
.gitignore /target

brine new <name> makes the folder; brine init uses the current one, names the project after it, and keeps any of these files that already exist. Both run git init unless the folder is already inside a Git repository (and skip it quietly if git isn’t installed).

The starter program:

src/main.br
# Welcome to brine! Run this program with `brine run`.
fn greet(name: str) -> str:
return f"Hello, {name}!"
fn main():
print(greet("brine"))
# parallel by default: this runs across all your cores
let squares = par [i * i for i in 0..10]
print(squares)
test "greeting":
assert greet("you") == "Hello, you!"
Terminal window
cd hello
brine run
Built hello v0.1.0 [dev] in 2.05s
Running target/dev/hello.exe
Hello, brine!
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

The file brine new writes:

brine.toml
[package]
name = "hello"
version = "0.1.0"

These are all the keys Brine reads:

Section Key Default Meaning
[package] name (required) the project’s name: the executable is target/<profile>/<name>, and it appears in status lines
[package] version "0.1.0" shown in status lines (Built hello v0.1.0)
[build] main "src/main.br" the main file, relative to brine.toml
[check] typed false true requires a written type on every let, var and for variable

A project that uses all of them:

brine.toml
[package]
name = "tool"
version = "1.2.0"
[build]
main = "app/cli.br"
[check]
typed = true

name is required:

error: brine.toml: missing `name` in [package]

version is free text. Brine doesn’t compare versions yet (there is no package manager; brine add is on the roadmap).

main names the file that holds fn main. Modules load relative to the main file’s folder: with main = "app/cli.br", import util loads app/util.br.

typed = true makes every variable need a written type, for teams and agents that want types spelled out. It applies to check, build, run and test; the --typed flag does the same for one command, or for a single file. A missing type is E0337:

error[E0337]: `s` has no written type
--> sum.br:2:9
|
2 | var s = 0
| ^ add `: int`
|
= note: this project asks for a type on every variable (`typed = true` in brine.toml, or `--typed`)
= run `brine explain E0337` for more about this error

The value must be true or false:

error: brine.toml: `typed` in [check] must be true or false, not `yes`

Brine reads a small subset of TOML: [section] headers and key = "string" or key = value lines, with # comments. Keys it doesn’t know are ignored, so a newer brine.toml still works with an older Brine.

Without a file argument, Brine looks for brine.toml in the current directory, then in each parent. So brine run works from src/ or any subfolder. A file or directory argument overrides this:

Command Builds
brine run the project around the current directory
brine run tools/gen.br that one file, as its own program
brine run ../other the project in ../other (it has a brine.toml)
brine run scripts scripts/main.br, if scripts/ has a main.br and no brine.toml

Project builds go to target/, one folder per profile:

target/
├── cache/ objects and the compiled runtime
│ ├── obj-0548db582edbf74a/
│ ├── rt-2fdf516b1c19bd8c/
│ └── ...
├── debug/
│ ├── hello.brine-hash
│ └── hello.exe
├── dev/
│ ├── hello.brine-hash
│ └── hello.exe
└── release/
├── hello.brine-hash
└── hello.exe
Folder Made by
target/dev/ brine run, brine build (the default profile)
target/release/ --release
target/debug/ --debug, brine debug
target/dev-c/, target/release-c/, target/debug-c/ a build with BRINE_BACKEND=c
target/dev-native/, target/release-native/, target/debug-native/ a build with BRINE_BACKEND=native
target/cache/ intermediate files for every profile

Test builds sit next to the program as <name>-tests (target/dev/hello-tests.exe). On Linux and macOS executables have no .exe.

A build with BRINE_BACKEND set always gets its own folder, even when it picks the backend the profile would use anyway, so it can never replace an executable a default build is running.

target/cache/ holds:

  • rt-<hash>/: the runtime (brine_rt.c, brine_rt.h) and its object file, compiled once per runtime version, C compiler and flags. This is why the first build in a new project takes about two seconds and later ones a fraction of one.
  • obj-<hash>/: the object files of one output, each named by a hash of what it was made from. An edit recompiles only the objects whose input changed. Old objects are pruned once there are many more than the build needs.

Next to each executable, <name>.brine-hash records a hash of the objects and runtime it was linked from. When a build produces the same hash and the executable exists, Brine skips the link and prints Fresh instead of Built.

Deleting target/ is always safe: the next build makes it again.

Every command takes a .br file instead of a project. Imports load next to that file.

Terminal window
brine run panic.br
brine check sum.br
brine test t.br
brine build tool.br # writes ./tool.exe (./tool on Linux and macOS)

brine build writes a single file’s executable in the current directory, the way a C compiler does. brine run, brine test and brine debug put theirs in a cache folder under your temp directory:

<temp>/brine-cache/
├── 465ef71a47ea2199/ one folder per source file (a hash of its absolute path)
│ ├── dev/argv.exe
│ └── debug/argv.exe
├── obj-.../ object files, shared by all single-file builds
└── rt-.../ the compiled runtime

<temp> is %TEMP% on Windows (GetTempPath2W) and $TMPDIR (else /tmp) on Linux and macOS. Nothing removes old folders from brine-cache; delete it whenever you like.

Project Single file
main file src/main.br or [build] main the file you pass
executable name [package] name the file name without .br
brine build output target/<profile>/<name> ./<name>
brine run output target/<profile>/<name> <temp>/brine-cache/<hash>/<profile>/<name>
cache target/cache/ <temp>/brine-cache/
[check] typed from brine.toml only with --typed
brine fmt without arguments every .br file in the project every .br file under the current directory