Consuming Spices Guide

A spice is a Turmeric package that your project depends on. This guide covers how to find, add, fetch, and import spices -- including pure-Turmeric spices and C library bindings via :cmake-deps.

See Developing Spices if you want to create and publish a spice of your own.


Official First-Party Spices

The canonical source for official spices is the turmeric-spices monorepo. Its packages include:

Spice Description C deps?
tur-test Testing framework: describe/it/TAP output None
tur-math 2D/3D vector and matrix math None
tur-sqlite SQLite3 bindings SQLite amalgamation
tur-raylib Raylib graphics and input Raylib
tur-json JSON parsing and serialization yyjson
tur-http Async HTTP/HTTPS client mbedTLS
tur-regex PCRE2 regular expression bindings PCRE2

The web stack (tur-httpd, tur-template, tur-tourist, tur-tls, tur-ws-client/tur-ws-server) and the data/DSP spices (tur-frame, tur-stats, tur-signal, tur-ecs, ...) live there too -- see their per-spice guides. Each spice lives in its own subdirectory and is versioned independently with a per-package tag: <spice>-vMAJOR.MINOR.PATCH.


Adding Spices

Official spices from turmeric-spices

Use tur add with --subdir to pull a spice from the monorepo:

tur add https://github.com/rjungemann/turmeric-spices \
  --ref test-v0.1.0 --subdir spices/test --name test

tur add https://github.com/rjungemann/turmeric-spices \
  --ref math-v0.1.0 --subdir spices/math --name math

tur add https://github.com/rjungemann/turmeric-spices \
  --ref sqlite-v0.1.0 --subdir spices/sqlite --name sqlite

tur add https://github.com/rjungemann/turmeric-spices \
  --ref json-v0.1.0 --subdir spices/json --name json

Each command updates build.tur and tur.lock automatically.

Third-party spices from a Git URL

tur add https://github.com/alice/tur-geom --ref v0.2.1

The spice name defaults to the last path segment with any tur- prefix stripped (tur-geom becomes geom). Override with --name:

tur add https://github.com/alice/tur-geom --ref v0.2.1 --name geometry

If --ref is omitted the tool resolves to HEAD and warns:

Warning: no --ref specified; resolved to HEAD (a1b2c3d4).
Pin with: tur add https://github.com/alice/tur-geom --ref a1b2c3d4

Always pin with an explicit tag or commit SHA to keep builds reproducible.

Local path spices (development)

tur add ../tur-utils --path

Use this while actively developing a dependency alongside your project. Local path spices are not recorded in tur.lock and are never fetched from the network.

Globally installed spices

A spice you installed with tur install can be consumed as a library by declaring it :global -- there is no tur add flag for this yet, so write the entry by hand:

:spices #map{
  "notebook" #map{:global true}
}

It resolves through the install registry rather than <project>/spices/, so nothing is fetched and no tur.lock row is written (as with a :path dep). If the spice is not installed, tur fetch says so and fails rather than letting the build reach module not found. :global takes neither :url nor :path -- those name a different resolution source. See Global Spices as Libraries for the full rules.


What tur add Changes

Before adding any spice:

(defpackage my-app
  :name    "my-app"
  :version "0.1.0")

After tur add https://github.com/alice/tur-geom --ref v0.2.1:

(defpackage my-app
  :name    "my-app"
  :version "0.1.0"
  :spices #map{
    "geom" #map{:url "https://github.com/alice/tur-geom"
                :ref "v0.2.1"}
  })

Fetching and Updating

tur fetch               # download everything in tur.lock
tur fetch --update      # upgrade spices to the latest allowed versions

tur run and tur build invoke tur fetch automatically when any spice is missing. Pass --offline to skip the network and fail if a spice is absent:

tur run --offline

The Lock File

tur.lock records the resolved commit SHA and SHA-256 hash for every fetched spice. Always commit it to version control; it is what makes builds reproducible across machines and CI runs.

;;; tur.lock -- generated by tur. Do not edit by hand.
;;; Commit this file to version control for reproducible builds.

(deflockfile
  :format-version 1
  :spices #{
    "geom" #{:url        "https://github.com/alice/tur-geom"
             :ref        "v0.2.1"
             :resolved   "a1b2c3d4e5f6..."
             :sha256     "abc123..."
             :fetched-at "2026-05-22T09:00:00Z"}
    "math" #{:url        "https://github.com/rjungemann/turmeric-spices"
             :ref        "math-v0.1.0"
             :subdir     "spices/math"
             :resolved   "d6e7f8a9b0c1..."
             :sha256     "def456..."
             :fetched-at "2026-05-22T09:00:03Z"}
  })

Rules: - tur fetch creates or updates tur.lock. - tur build uses the lock and will not silently upgrade versions. - Local :path spices are not recorded in the lock file. - Builds fail when a fetched spice's hash does not match the lock entry.


Importing Spice Modules

Spice names map directly to import paths. A spice named geom that exports geom/vector and geom/matrix is imported like any other module:

(import geom/vector :refer [vector-2d cross-product])
(import geom/matrix :as mat)

(defn main [] :int
  (let [v (vector-2d 1.0 2.0)
        m (mat/mat4-identity)]
    (println v)
    0))

The compiler resolves geom/vector to spices/geom-v0.2.1/src/vector.tur. No extra configuration is needed.


Optional Spices

Mark a spice :optional true when it is only needed for tests or dev tooling. Optional spices that are absent do not cause a build error.

:spices #map{
  "test" #map{:url    "https://github.com/rjungemann/turmeric-spices"
              :ref    "test-v0.1.0"
              :subdir "spices/test"
              :optional true}
}

When tur test is run, optional spices are fetched so test files can import them. When tur build is run in production, they are skipped.

An optional spice that cannot be fetched is reported and skipped: tur fetch still writes tur.lock and exits 1, distinct from the 2 a required spice's failure earns (and the 0 of a clean fetch), so a CI job can warn on the first and fail on the second. See the exit-status table in package-management-guide.md.


C/CMake Dependencies

Some spices (like tur-sqlite or tur-raylib) wrap a C library. When you add them, tur build fetches and compiles the C library via CMake automatically -- no CMake knowledge required on your end.

You can also add a C library directly to your project without going through a spice:

tur add-cmake https://github.com/raysan5/raylib --ref 5.5 \
  --opt BUILD_SHARED_LIBS=OFF --opt BUILD_EXAMPLES=OFF

This appends a :cmake-deps entry to build.tur:

(defpackage my-app
  :name    "my-app"
  :version "0.1.0"
  :cmake-deps #map{
    "raylib" #map{:url     "https://github.com/raysan5/raylib"
                  :ref     "5.5"
                  :options #map{:BUILD_SHARED_LIBS "OFF"
                                :BUILD_EXAMPLES   "OFF"}}
  })

When tur build runs it:

  1. Generates cmake/CMakeLists.txt from the :cmake-deps block (via the automatic tur fetch).
  2. Invokes CMake to fetch and compile the C library.
  3. Reads cmake/spice-deps-manifest.json for include dirs, lib dirs, link libs, and link flags.
  4. Passes those flags to cc automatically.

Step 3's link flags are what make a Cocoa- or Objective-C-backed library (glfw, raylib) link on macOS: they carry the -framework Cocoa / -framework IOKit entries CMake records on the target, which cannot be spelled as -l. They also carry the dep's artifact by full path, so a target whose name differs from its library's (glfw's target glfw builds libglfw3.a) resolves correctly. Both are derived automatically from :targets; see :link-libs and :link-flags overrides for when you need to override them.

Declare the C symbols you need in Turmeric with extern-c (no header include is needed for the declarations themselves; an inline-C body that wants the header can hoist #include <raylib.h> to file scope with a __tur_include__ marker -- see the C integration guide):

(extern-c InitWindow        [:int :int :cstr] :void)
(extern-c CloseWindow       [] :void)
(extern-c WindowShouldClose [] :bool)
(extern-c BeginDrawing      [] :void)
(extern-c EndDrawing        [] :void)

(defn main [] :int
  (InitWindow 800 600 "Hello")
  (while (not (WindowShouldClose))
    (BeginDrawing)
    (EndDrawing))
  (CloseWindow)
  0)

No -I or -L flags are needed; tur build injects them from the manifest.

CMake deps are also tracked in tur.lock with SHA-256 hashes for integrity verification. For the generated cmake/CMakeLists.txt format, the manifest schema, security considerations, and the outbound direction (publishing a Turmeric library so CMake projects can consume it), see the CMake/CPM integration notes.


Security

sh tur audit

It lists; it does not verify. There is no signature or maintainer-key checking -- an earlier version of this bullet promised GPG keys, and no key infrastructure exists to check them against. A :path dep is reported but not flagged as unpinned: it resolves from local source and has nothing to pin, and flagging it would train you to ignore the warning that matters.


Conflict Resolution

When two spices require incompatible versions of a shared dependency the build fails with a diagnostic. There is no silent multiple-version shadowing. You must either upgrade one spice or pin versions to a compatible range.


Common Error Messages

Condition Message
No build.tur in the directory tree No build.tur found. Run tur new <name> to create a project.
Spice already in the manifest 'geom' is already a dependency. To change its ref, edit the :spices entry for 'geom' in build.tur, then run tur fetch --update.
Network failure Failed to reach https://github.com/...: <reason>
Ref not found Ref 'v99.0.0' not found in https://github.com/alice/tur-geom
SHA mismatch on re-fetch Integrity check failed for 'geom'. Run tur fetch --force to re-download.

A spice can declare which compiler versions it supports with :tur-version; if your tur is below that floor the build stops with TUR-E0621 (above a declared ceiling is TUR-W0623, a warning, and a malformed range in the spice's own manifest is TUR-E0622) -- see Declaring a compiler version range.


CLI Quick Reference

# Add a Turmeric spice
tur add <url>
tur add <url> --ref <tag-or-sha>
tur add <url> --ref <ref> --subdir <path> --name <alias>
tur add <path> --path

# Add a C/CMake dependency
tur add-cmake <url> --ref <ref>
tur add-cmake <url> --ref <ref> --opt KEY=VALUE

# Fetch and update
tur fetch
tur fetch --update

# Build and run
tur build
tur build --release
tur run
tur run --release
tur run --offline
tur test

See Also