Toolchain Commands
The flint CLI has six commands. That is the entire toolchain.
flint new
Scaffold a new project:
flint new blink
Creates:
blink/
flint.toml
src/
main.fl
The generated project targets rp2040 with board = "pico" by default.
flint fmt
Format your source into canonical Flint style:
flint fmt # format project in current directory
flint fmt --path my_project # format a specific project path
The formatter is opinionated and non-negotiable, like gofmt. Run it before committing. There is no configuration.
Key formatting rules:
- 4-space indentation
- 100-character line limit
- One blank line between top-level items
- Brace on the same line as the construct
usedeclarations at the top of the file, sorted
flint check
Type-check and semantically validate your project without building:
flint check # check project in current directory
flint check --path my_project # check a specific project
flint check --json # emit diagnostics as JSON
check runs the full frontend: lex, parse, resolve, and type-check. It is fast, so use it often. No artifacts are written.
The --json flag is useful for editor integrations and CI scripts:
flint check --json | jq '.diagnostics[] | select(.severity == "error")'
flint build
Compile and produce artifacts:
flint build # build project in current directory
flint build my_project # build a project by path
flint build --output-format uf2 # produce a UF2
flint build --output-format elf # produce an ELF
flint build --output-format bin # produce a raw binary
flint build --show-layout # show section layout and symbol table
flint build --show-layout --json # same, as structured JSON
Successful output:
compiling project `blink` (rp2040-thumb-pico)
compiled successfully in 0.01s
binary image: 1,108 bytes (1.08KB)
output: `build/blink.uf2` 2,560 bytes (2.5KB)
With --show-layout:
compiling project `blink` (rp2040-thumb-pico)
compiled successfully in 0.01s
binary image: 1,108 bytes (1.08KB)
output: `build/blink.uf2` 2,560 bytes (2.5KB)
layout:
total size: 1,108 bytes (1.08KB)
base address: 0x10000000
entry address: 0x100001C1
output: uf2 (2,560 bytes (2.5KB)) -> build/blink.uf2
sections:
0x10000000 +0x000000 256 bytes boot2 (second-stage bootloader)
0x10000100 +0x000100 192 bytes vector_table (48 vectors)
0x100001C0 +0x0001C0 660 bytes text (reset handler and reachable functions)
0x100001C0 +0x0001C0 504 bytes reset_handler (startup and clock bring-up)
symbols:
0x100001C0 +0x0001C0 504 bytes reset_handler (entry)
0x100003B8 +0x0003B8 40 bytes src/main.fl::main (src/main.fl)
0x100003E0 +0x0003E0 44 bytes std/time.fl::sleep_ms (std/time.fl)
0x1000040C +0x00040C 44 bytes micro/gpio.fl::into_output (micro/gpio.fl)
0x10000438 +0x000438 28 bytes micro/gpio.fl::toggle (micro/gpio.fl)
--show-layout breaks down exactly what landed in your binary. Sections show how flash is organized. Symbols show every function that made it into the image, its address, and its size. Notice only the functions your program actually calls are listed. Everything else was discarded. Useful for understanding your flash budget or tracking down an unexpected size increase.
MCU Recursion
On MCU targets, recursive function calls are an error by default. Stack overflow is unrecoverable on most MCUs, and recursion makes stack usage unbounded and impossible to analyze statically.
This is not a novel restriction. The JSF C++ Coding Standards, used for safety-critical avionics software on platforms like the F-35, explicitly prohibit recursion for exactly the same reason: if you cannot bound the stack at compile time, you cannot guarantee the system will not corrupt itself at runtime. Flint enforces this by default and lets you opt out only when you know what you are doing.
To opt into recursion intentionally:
flint build --allow-recursion
Or set it in flint.toml:
allow-recursion = true
flint lsp
Start the language server for editor integration:
flint lsp # start the server (stdio transport)
flint lsp --log-file /tmp/lsp.log # write debug log to a file
flint lsp --trace protocol # trace protocol messages to stderr
Editors launch this automatically. You normally do not run it yourself. See Language Server for supported features and editor setup.
flint flash
Reserved for direct device flashing. Not yet implemented.
In the meantime, use one of these approaches:
- UF2 drag-and-drop: BOOTSEL mode, copy
.uf2to the drive. picotool:picotool load build/blink.uf2 --forceprobe-rs:probe-rs run --chip RP2040 build/blink.elf- OpenOCD:
openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg ...
Common Workflows
# Quick check during development
flint check
# Build and flash (UF2 method)
flint build --output-format uf2
cp build/blink.uf2 /Volumes/RPI-RP2/
# CI check
flint check --json
# Debug build with verbose layout
flint build --output-format elf --show-layout
Diagnostics
Flint errors include file, line, and column. Errors are reported to stderr. The --json flag on check and build emits structured diagnostic objects for tooling:
{
"diagnostics": [
{
"severity": "error",
"message": "use of moved value: `packet`",
"file": "src/main.fl",
"line": 14,
"column": 5
}
]
}