This guide covers two audiences:
tur from a tagged release or via
Homebrew, and need to know what's in the tarball and which subcommands
work outside the source repo.There are five supported paths today. If you expect to switch between releases (bisecting a regression, matching a spice's required version), the version manager (Option 1) automates the manual prebuilt-binary dance of Option 2.
tvm)The bundled Turmeric Version Manager installs,
caches, and switches between releases per-shell -- the nvm/rustup
model. Bootstrap it once from a checkout:
sh tvm/install.sh # installs into ~/.tvm and wires up your shell rc
Then, in a new shell:
tvm install 0.36.0 # download + SHA-256 verify + cache a prebuilt release
tvm use 0.36.0 # activate it for this shell
tvm alias default 0.36.0 # make it the default for new shells
tvm ls-remote # list versions available to download
tvm run 0.17.0 --version # one-shot invoke without switching (great for bisects)
tvm install consumes the same GitHub Release tarballs described in
Option 2, verifies them against the release's sha256sums.txt, and
extracts each version under ~/.tvm/versions/<v>/. When no prebuilt
asset exists for a tag (older than the prebuild matrix, or an
unpublished platform), tvm install --build <v> falls back to a CMake
source build. See tvm/README.md for the full
command set, including .tur-version auto-switching.
For every tag matching v* pushed to the repository, a
GitHub Release is
published with four archives and a sha256sums.txt:
turmeric-vX.Y.Z-linux-x86_64.tar.gz
turmeric-vX.Y.Z-linux-aarch64.tar.gz
turmeric-vX.Y.Z-macos-arm64.tar.gz
turmeric-vX.Y.Z-windows-x86_64.zip
sha256sums.txt
Windows ships a .zip rather than a .tar.gz because Explorer opens one and
not the other, and tur.exe is statically linked against the MinGW support
libraries, so the archive has no DLLs beside it and needs nothing on PATH to
start.
It also unpacks to a prefix layout rather than the flat one the other archives use:
bin/tur.exe
lib/libturt_runtime.a, libturi.a
include/turi/*.h
share/turmeric/stdlib/
Keep that shape. tur finds its runtime archive at <exe_dir>/../lib and its
standard library at <exe_dir>/../share/turmeric/stdlib; flattening the tree
leaves it unable to compile anything (see
docs/reported/release-archive-cannot-compile.md, which also tracks the other
platforms, still on the flat layout). Put <extracted>/bin on PATH, or
symlink bin/tur.exe -- the walk-up resolves through a symlink either way.
Windows also needs a C toolchain to compile anything. tur.exe runs on its
own, but tur build and tur run invoke cc, and tur jit reads the UCRT
headers that come with it. Install MSYS2 and its
UCRT64 gcc:
pacman -S mingw-w64-ucrt-x86_64-gcc
then run tur from a UCRT64 shell, or put C:\msys64\ucrt64\bin on PATH.
Without it tur run reports cc invocation failed. (TUR_JIT_SYS_INCLUDE
overrides where the JIT looks for those headers, if yours live elsewhere.)
Pick the tarball for your platform, verify, and extract:
# Apple Silicon macOS example. Adjust the URL for your platform/version.
TAG=v0.36.0
ARCH=macos-arm64
curl -fLO "https://github.com/rjungemann/turmeric/releases/download/${TAG}/turmeric-${TAG}-${ARCH}.tar.gz"
curl -fLO "https://github.com/rjungemann/turmeric/releases/download/${TAG}/sha256sums.txt"
# Verify just the file you downloaded:
shasum -a 256 -c <(grep "${ARCH}" sha256sums.txt)
# Extract somewhere stable:
mkdir -p ~/.local/turmeric
tar -xzf "turmeric-${TAG}-${ARCH}.tar.gz" -C ~/.local/turmeric
# Make `tur` runnable:
ln -s ~/.local/turmeric/tur ~/.local/bin/tur # ensure ~/.local/bin is on PATH
The macOS binary is unsigned. On first run macOS will quarantine it:
xattr -d com.apple.quarantine ~/.local/turmeric/tur
There is no precompiled Intel-Mac (macos-x86_64) tarball. Intel-Mac
users should use Option 3 or Option 4.
brew install --HEAD rjungemann/turmeric
# (if a tap isn't published yet:)
brew install --HEAD https://raw.githubusercontent.com/rjungemann/turmeric/main/Formula/turmeric.rb
The formula builds from the latest commit on main (CMake source build,
~10s on modern hardware). It installs:
tur at <prefix>/bin/tur<prefix>/share/turmeric/stdlib/On Apple Silicon, <prefix> is /opt/homebrew; on Intel macOS and
Linuxbrew it's /usr/local or /home/linuxbrew/.linuxbrew.
There is currently no stable (versioned) Homebrew formula -- only
--HEAD. A pinned url/sha256 stanza will be added once a stable
release line is established.
git clone https://github.com/rjungemann/turmeric.git
cd turmeric
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5
cmake --build build -j # debug build, lands at build/tur
cmake -S . -B build-release -DCMAKE_BUILD_TYPE=Release -DCMAKE_POLICY_VERSION_MINIMUM=3.5
cmake --build build-release -j # optimized build, drops `tur` in build-release/
You'll need CMake 3.20+, a C99 compiler, and libedit (for the REPL).
See devcontainer-guide.md for a fully-scripted
Linux dev environment.
After extracting, the tarball lays out like:
.
|-- tur # the CLI
|-- libturi.a # static library for C embedding
|-- include/turi/ # public headers (eval.h, env.h, value.h, fiber.h)
`-- stdlib/ # the standard library (~137 .tur files)
tur finds stdlib/ via a probe defined in src/main.c
(resolve_stdlib_root), in this order:
TUR_STDLIB_DIR environment variable, if set.tur's directory looking for stdlib/macros.tur
(matches when stdlib/ sits next to the binary -- the tarball layout).<exe_dir>/../share/turmeric/stdlib/macros.tur (the Homebrew layout).If you move tur somewhere without an adjacent stdlib/, set
TUR_STDLIB_DIR to the directory containing the stdlib .tur files.
tur --versiontur interpret <file.tur> -- tree-walking interpreter; no C compiletur repl -- interactive REPLtur check <file.tur> -- type checking onlytur doc <symbol> -- documentation lookuptur explain <code> -- diagnostic-code lookuptur format -- source formatterlibturi.a + include/turi/tur run <file.tur> and tur build <file.tur>These go through the C-codegen path and link against runtime sources
referenced by autolink markers in stdlib (e.g. stdlib/hamt.tur contains
/* __tur_autolink__: src/runtime/hamt.c -Isrc/runtime */). The compiler
anchors those relative paths at the located turmeric root (the parent of
the resolved stdlib/), but the release tarball ships no src/runtime/
next to its stdlib/, so a tur run from a downloaded release fails
with:
clang: error: no such file or directory: 'src/runtime/hamt.c'
tur: cc invocation failed (status 256)
This is a known limitation, tracked in
docs/release-binaries-plan.md under
"Discovered during execution: runtime sources also missing". Until it's
resolved, the --interpret and library-embedding paths are the
fully-supported uses of a downloaded release.
The repository ships a Dockerfile that builds tur from
the local source tree and packages it into a self-contained Ubuntu 22.04 image.
This is the easiest path on Linux if you do not want to install CMake or deal
with libedit versions.
Build the image (run from the repository root):
docker build -t turmeric .
The multi-stage build compiles a Release binary and copies only the binary, stdlib, and C runtime sources into the final image (~200 MB).
REPL:
docker run --rm -it turmeric
Interpret a file (no C compiler involved):
docker run --rm -v "$(pwd)":/workspace turmeric \
tur interpret /workspace/hello.tur
Compile and run a file:
docker run --rm -v "$(pwd)":/workspace turmeric \
tur run /workspace/hello.tur
Type-check a file:
docker run --rm -v "$(pwd)":/workspace turmeric \
tur check /workspace/hello.tur
The image sets TUR_STDLIB_DIR and wraps the tur binary in a small shell
script so that tur run/tur build can resolve the C runtime sources (which
are referenced by relative paths at compile time). Mount your project with
-v "$(pwd)":/workspace and pass the absolute container path to your file.
The release pipeline lives at .github/workflows/release.yml and is
triggered automatically on git push of any tag matching v*.
The /cut-minor-release and /cut-major-release skills drive the full
flow with preconditions and confirmations. Experiment expiry is
advisory and never blocks a release: the skills surface any registry
entry whose expires_at is at or before the version being cut and then
proceed; the author follows up by graduating the experiment (delete its
row in src/runtime/experiments.c; the feature becomes always-on),
shelving it, or bumping expires_at with a one-line rationale. See
experimental-flags-guide.md.
VERSION (the single source of truth -- read by
CMake, baked into tur --version, and reused by web/ and other
build outputs). The version string is plain MAJOR.MINOR.PATCH,
no leading v.sh
git tag v0.36.0 # match the new VERSION
git push origin main
git push origin v0.36.0sha256sums.txt.For each matrix leg (linux-x86_64, linux-aarch64, macos-arm64):
libedit (Apple/Linux differ on package manager).tur --version as a smoke test.tur + libturi.a + include/turi/*.h + stdlib/ into
a tar.gz.Windows is a separate job rather than a fourth matrix leg: every step needs
shell: msys2 {0} and the toolchain arrives through setup-msys2, neither of
which fits the matrix. It does the same six steps, plus one that has no
equivalent elsewhere -- verifying the binary is self-contained. A default
MinGW build needs libwinpthread-1.dll and edit.dll out of the MSYS2 tree,
and off an MSYS2 PATH such a binary exits 127 with no output and no
diagnostic. So the job both reads the import table (ldd, rejecting anything
resolved inside the MSYS2 tree) and actually runs tur --version with MSYS2
stripped from PATH, which is the failure a user would hit.
A final job downloads all artifacts, generates sha256sums.txt, and
publishes the release with auto-generated notes.
The release job has if: always() && needs.build.result != 'cancelled',
so if one matrix leg fails (e.g. a future runner image breaks Linux
aarch64), the other binaries still ship -- you'll get a partial release
that you can re-run or supplement.
To test changes to release.yml without burning real version numbers,
use a throwaway tag like v0.0.0-test1:
git tag v0.0.0-test1
git push origin v0.0.0-test1
# Watch the run:
gh run list --workflow=release.yml --limit 1
gh run watch <run-id>
# Clean up afterwards:
gh release delete v0.0.0-test1 --cleanup-tag --yes
git tag -d v0.0.0-test1
Increment the suffix (-test2, -test3, ...) per iteration so each
failed attempt's history is preserved.
matrix.<leg>.os to a current
GitHub-hosted runner.command not found. A semicolon in a
CMake flag is being interpreted by the shell. Wrap the value with
literal double-quotes in the matrix value
(cmake_extra: '"-Dfoo=a;b"') so the shell treats it as one argument
after ${{ matrix.cmake_extra }} substitution.actions/checkout or actions/upload-artifact Node-20 warning.
Informational until June 2026. Bump the action to the latest major
when an upstream Node-24 release ships, or opt in early with the
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true env var on the job.brew test turmeric fails with unbound variable: when. The
formula did not install stdlib/ under <prefix>/share/turmeric/.
Confirm the formula's install block contains
(share/"turmeric").install "stdlib".Formula/turmeric.rb -- the Homebrew formula..github/workflows/release.yml -- the release pipeline.src/main.c (resolve_stdlib_root) -- the stdlib-discovery logic
that makes both the tarball and Homebrew layouts work without code
changes.