Developing Spices Guide

A spice is a Turmeric package that other projects depend on. This guide covers every step of authoring one: project structure, declaring exports, wrapping C libraries with :cmake-deps, testing, versioning, and publishing.

See Consuming Spices if you only need to add an existing spice to your project.


When to Create a Spice

Create a spice when you want to:

For code shared only within a single project, use the module system -- no separate package is needed.


Scaffolding the Project

tur new tur-mylib --lib
cd tur-mylib

This creates:

tur-mylib/
  build.tur        -- package manifest
  tur.lock         -- empty lock file (commit to VCS)
  src/
    lib.tur        -- stub exported function
  .gitignore
  README.md

By convention, name the package with a tur- prefix. Consumers drop the prefix as the import alias: tur-geom becomes geom, tur-math becomes math.


The build.tur Manifest

The manifest filename can be either build.tur (plain s-expression) or build.tur.sweet (sweet-exp syntax). Both are equivalent everywhere the toolchain consults the manifest -- walk-up discovery, workspace member resolution, transitive :spices deps, and the cwd-relative tur add / tur fetch paths all accept either. When both files exist in the same directory the plain build.tur takes precedence. tur init --sweet scaffolds the sweet variant.

A complete library manifest:

(defpackage tur-mylib
  :name        "tur-mylib"
  :version     "0.1.0"
  :description "A brief description of my library"
  :license     "MIT"
  :authors     ["Your Name <you@example.com>"]
  :repository  "https://github.com/you/tur-mylib"

  :exports {
    "mylib/core" ["some-fn" "another-fn"]
    "mylib/util" ["helper-fn"]
  })

Key manifest fields

Field Required Notes
:name Yes Must match [a-z][a-z0-9-]*
:version Yes Semver: MAJOR.MINOR.PATCH
:description Recommended One-line summary
:license Recommended SPDX identifier (e.g. "MIT")
:authors Recommended "Name <email>" list
:repository Recommended URL to the canonical Git repo
:exports Yes (library) Map of module path to exported symbol names
:spices If needed Turmeric package dependencies
:cmake-deps If needed C/C++ library dependencies
:build-opts Rarely :c-flags / :link-libs, plus :c-sources / :c-includes for vendored C

Declaring Exports

The :exports map controls what is visible to consumers. Only listed symbols are part of the public API; everything else is private.

:exports {
  "mylib/types" ["Coord" "Rect" "Color"]
  "mylib/draw"  ["draw-rect" "draw-circle" "draw-line"]
  "mylib/io"    ["read-file" "write-file"]
}

Each key ("mylib/types") becomes an importable path for consumers:

(import mylib/types :refer [Coord Rect])
(import mylib/draw  :refer [draw-rect])

Internal helpers that should not be exposed are simply omitted from :exports.


Source Layout

Follow this layout so that module paths and file paths align without extra configuration:

tur-mylib/
  build.tur
  tur.lock
  src/
    types.tur       -- exports "mylib/types"
    draw.tur        -- exports "mylib/draw"
    io.tur          -- exports "mylib/io"
    internal/
      helpers.tur   -- not exported; only imported by other src files
  tests/
    types_test.tur
    draw_test.tur

The module path "mylib/types" resolves to src/types.tur. A nested path "mylib/net/http" resolves to src/net/http.tur.


Multi-file Spices: defmodule + import

When a spice spans more than one source file, use defmodule with an explicit (export ...) list and (import <other-module> :refer [...]) to wire the files together. The module system handles cross-file symbol sharing correctly at every scale.

The correct approach

Each file declares its own defmodule, exports what it offers, and imports what it needs from siblings:

;; src/mylib/types.tur
(defmodule mylib/types
  (export Widget WidgetResult)
  (defstruct Widget [value :int])
  ...)
;; src/mylib/core.tur
(defmodule mylib/core
  (export make-widget widget-value)
  (import mylib/types :refer [Widget WidgetResult])

  (defn make-widget [v :int] :Widget ...)
  (defn widget-value [w :Widget] :int ...))

tur check, tur emit-c, and tur build all resolve intra-spice imports automatically through the auto-spice include path -- no -I flags needed. See Per-file Commands Inside a Spice.

Anti-pattern: cross-file type stubs

An older pattern copies function bodies across files so each file compiles in isolation without imports:

;; src/mylib/io.tur -- ANTI-PATTERN
;; Stub copy of make-widget from core.tur; real body is return NULL.
(defn make-widget [v :int] #{Unsafe} :Widget
  ```c
  return NULL;
  ```)

(defn read-widget [path :cstr] :Widget ...)  ;; real implementation

This pattern fails in three ways:

  • Silent breakage: stubs with return NULL or no-op bodies mean any code path that hits the stub produces garbage or does nothing -- with no diagnostic.
  • Linker errors when combined: two files that both define make-widget as a static C function produce duplicate-symbol link failures the moment they are compiled together (via (load ...) or tur build).
  • Per-file checks appear clean: each file is self-contained so tur check/tur emit-c on a single file passes while the combined build fails.

The scscm spice used this pattern across all five of its source files. It was eliminated in the 2026-05 import refactor by converting each file to defmodule + import. See scscm-spice-import-refactor-plan.md for the full migration. If you encounter the stub pattern in other spices, the fix is the same: add a (defmodule ...) + (export ...) header and replace each stub block with (import <module> :refer [...]).


Depending on Other Spices

Add Turmeric spice dependencies the same way any project does:

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

This produces:

:spices {
  "math" {:url    "https://github.com/rjungemann/turmeric-spices"
          :ref    "math-v0.1.0"
          :subdir "spices/math"}
}

Mark spices that are only needed for tests :optional true so consumers are not forced to fetch them:

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

Cross-spice Development in a Workspace

When your spices live in the same workspace (a parent directory with a build.tur that lists all members under :members), you can import one sibling from another without publishing a release or running tur fetch.

Option A: workspace-member auto-resolution (no manifest entry needed)

If both spices are already listed in the workspace build.tur:

;; turmeric-spices/build.tur
(defpackage turmeric-spices
  :members ["spices/watch" "spices/notebook" ...])

Then notebook can (import watch/watch ...) directly -- the resolver walks up to the workspace build.tur, finds that watch is a listed member, and adds its src/ to the search path automatically.

# No tur fetch or symlink, and no lock entry required:
cd spices/notebook
tur check src/notebook/cli.tur   # resolves watch/watch via workspace

The first time an undeclared sibling import resolves, tur prints a one-time advisory:

warning: import 'watch/watch' resolved via workspace sibling
         'spices/watch'; declare it in :spices for release builds.
         (set TUR_DEBUG_RESOLVER=1 for full resolver tracing)

To confirm a name is a workspace member before importing:

tur add --workspace watch   # exits 0, prints "no manifest entry needed"
tur add --workspace typo    # fails if 'typo' is not in :members

Option B: explicit :path dep (works outside workspaces too)

Add a :path entry pointing at the sibling spice directory:

tur add ../watch --path

This writes to build.tur:

:spices {
  "watch" {:path "../watch"}
}

The resolver immediately adds ../watch/src to the include path. No tur fetch step is required, and no lock entry is written for this dep. The path is resolved relative to the directory containing build.tur.

tur check src/notebook/cli.tur   # resolves watch/watch via :path

If the declared :path does not exist on disk (or contains no build.tur), tur fetch reports a hard error rather than silently ignoring the missing dep.

tur fetch --dry-run for verification

To confirm how each dep will be classified before committing to a real fetch:

tur fetch --dry-run
# skip :path          watch
# skip workspace member  alpha
# fetch  URL          ansi  https://github.com/...  ansi-v0.1.4
# summary: 1 fetch, 2 skipped (local)

Local-source deps are never contacted or locked. Only URL deps appear in tur.lock.

Release: switch to a URL dep for external consumers

The workspace auto-resolution and :path entries are local-development shortcuts. For a spice that will be published and consumed outside the workspace, add a URL entry alongside (or instead of) the local one:

:spices {
  "watch" {:url    "https://github.com/rjungemann/turmeric-spices"
           :ref    "watch-v0.1.0"
           :subdir "spices/watch"}
}

External consumers run tur fetch once to clone the ref and populate tur.lock. Inside the workspace, a URL :spices entry whose name matches a workspace member is resolved locally instead -- no spurious fetch occurs for deps you are developing in the same workspace.


Wrapping a C Library with :cmake-deps

The :cmake-deps block tells tur build to fetch and compile a C library via CMake, then inject the resulting include dirs and link flags into the compilation step. You write only Turmeric; CMake is an implementation detail.

Declaring the dependency

:cmake-deps {
  "sqlite3" {:url     "https://github.com/sqlite/sqlite"
             :ref     "version-3.47.2"
             :options {:BUILD_SHARED_LIBS "OFF"}}
}

:options sets CMake cache variables (-DKEY=VALUE). Common patterns:

Declaring C symbols in Turmeric

Use include-c to pull in the C header and extern-c to declare symbols:

;; src/db.tur
(include-c "sqlite3.h")

(extern-c sqlite3_open   [:cstr :ptr] :int)
(extern-c sqlite3_close  [:ptr]       :int)
(extern-c sqlite3_exec   [:ptr :cstr :ptr :ptr :ptr] :int)
(extern-c sqlite3_errmsg [:ptr] :cstr)

(defn db-open [path :cstr] (result :ptr)
  (let [db-ptr (make-ptr)]
    (let [rc (sqlite3_open path db-ptr)]
      (if (= rc 0)
        (ok (deref-ptr db-ptr))
        (err (cstr->str (sqlite3_errmsg (deref-ptr db-ptr))))))))

extern-c trusts the signature you give it -- double-check it against the actual C header. No -I or -L flags are needed in your source; tur build injects them from cmake/spice-deps-manifest.json.

Inline C for small wrappers

When a binding is simpler to write directly in C, use an inline-C block:

(defn db-last-insert-rowid [db :ptr] :int
  ```c
  return (int)sqlite3_last_insert_rowid((sqlite3*)db);
  ```)

The closing ``` and its enclosing ) must be on the same line. See the inline-C style rule for why.

:cmake-name and :targets overrides

When the CMake find_package name or target name differs from the key in :cmake-deps, supply overrides:

:cmake-deps {
  "sqlite" {:url        "https://github.com/sqlite/sqlite"
            :ref        "version-3.47.2"
            :cmake-name "SQLite3"
            :targets    ["SQLite::SQLite3"]}
}

System-first resolution with :prefer-system

Heavy native libraries (mbedTLS, raylib, sqlite, libpq) are often already installed system-wide via Homebrew / apt / dnf. Building them from source on every clean tur fetch is slow. Add :prefer-system true to try CMake's find_package first and fall back to the source build only when no system copy is found:

:cmake-deps {
  "mbedtls" {:prefer-system true                ;; try find_package first
             :cmake-name    "MbedTLS"           ;; name passed to find_package
             :cmake-version "3.0"               ;; optional minimum version
             :targets       ["MbedTLS::mbedtls"
                             "MbedTLS::mbedx509"
                             "MbedTLS::mbedcrypto"]
             :url           "https://github.com/Mbed-TLS/mbedtls"  ;; fallback
             :ref           "v3.6.2"
             :options       {:ENABLE_PROGRAMS "OFF"
                             :USE_STATIC_MBEDTLS_LIBRARY "ON"}}
}

Behaviour:

  • :prefer-system true requires :cmake-name -- it is the name handed to find_package. Omitting it is a hard manifest error.
  • :cmake-version is optional; when present it becomes the minimum version in find_package(<name> <version> QUIET).
  • When the system copy is found, the dep's include/link flags come from the imported target's INTERFACE_INCLUDE_DIRECTORIES and $<TARGET_FILE_DIR:...> -- skipping both the clone and the source build. Otherwise the existing FetchContent block runs exactly as before.
  • The generated spice-deps-manifest.json records "resolved_via": "system" or "fetch" per dep, and tur.lock records :resolved-via "system" (with no git SHA -- the system package manager owns the version) or the usual :url/:ref/:resolved row for the fetch path.

:prefer-system is opt-in; deps without it keep their FetchContent-only behaviour unchanged.

Forcing the source build (--refetch)

To pin a build to the source copy and bypass any system package -- useful for reproducible CI artefacts -- pass --refetch (or set TUR_FETCH_FORCE_FETCH=1 in the environment):

tur fetch --refetch          # ignore system copies, always build from source

This disables the find_package short-circuit for every :prefer-system dep in the manifest.

Caveats. A binary linked against a Homebrew/apt shared library will fail at runtime on a machine without that library installed; pin the source build (or --refetch) for portable artefacts. See tur-fetch-system-first-plan.md for the design rationale and open questions.

For the full :cmake-deps field reference, the generated SpiceDeps.cmake format, the spice-deps-manifest.json schema, and hash locking, see the CMake/CPM integration notes.


Vendoring C Sources (:c-sources / :c-includes)

:cmake-deps is the right tool for a real native library that already has a CMake (or system-package) presence. But some C is too small for that ceremony: a single-file header library (stb_image, miniaudio), a four-file FFT (KissFFT), a hand-tuned kernel. For those, vendor the .c straight into the spice and let the spice build compile and link it -- no CMake, no fetch step.

Add two keys under :build-opts:

(defpackage tur-signal
  :name "tur-signal"
  :build-opts #fx{
    :c-includes ["c/kissfft"]            ;; -I dirs (manifest-relative)
    :c-sources  ["c/kissfft/kiss_fft.c"  ;; .c files compiled + linked
                 "c/kissfft/kiss_fftr.c"
                 "c/glue/fft_shim.c"]
  }
  :exports #map{ "signal/fft" [fft-forward fft-inverse] })

Rules

  • Manifest-relative paths only. Both :c-sources and :c-includes resolve relative to the directory holding build.tur. An absolute path is a hard error -- if you need a system include path, declare a :link-libs / :cmake-deps dependency instead.
  • Validated at manifest load. Each :c-sources entry must exist on disk and end in .c, .cc, or .cpp; each :c-includes entry must be an existing directory. A typo fails the build immediately with a diagnostic pointing at the offending entry, not with an opaque cc error later.
  • :c-includes reach the spice's own C only. The -I dirs are visible both to the vendored .c and to inline-C blocks in this spice's .tur modules (so an inline-C block can #include "kissfft/kiss_fftr.h"). They are not exported to consumers -- vendored headers are an implementation detail.
  • :c-sources propagate across :spices deps. If spice B vendors a .c and spice A depends on B, building A links B's vendored sources into A's binary automatically. Consumers see B only through its .tur exports, so a consumer's inline-C should extern-declare any vendored symbol it calls rather than relying on B's private headers.
  • C++ is accepted but not auto-configured. .cc / .cpp entries are allowed (some vendor libraries are C++ wearing a C name), but the build does not switch to a C++ driver for you -- add -x c++ to :c-flags if needed.
  • No platform conditionals. Every listed source is compiled on every platform; gate platform-specific code with #ifdef inside the source, as vendor libraries already do.

Layout: vendored C goes under c/, never src/

The manifest-driven build walks src/ looking for .tur modules; do not put .c files there. Keep vendored C in its own c/ tree:

tur-signal/
  build.tur
  src/
    signal/fft.tur        ;; .tur modules (walked by the build)
  c/
    kissfft/
      kiss_fft.c          ;; vendored sources (listed in :c-sources)
      kiss_fft.h          ;; vendored headers (dir listed in :c-includes)
    glue/
      fft_shim.c

When to reach for this vs :cmake-deps

Vendor a .c when the library is small, single-purpose, and has no native package-manager presence -- you would otherwise be copy-pasting a header into an inline-C block anyway. Reach for :cmake-deps when the library is large, has its own build system, or is commonly installed system-wide (where :prefer-system saves a source build). If a library needs CMake or autotools to configure itself, it is not a vendoring candidate.


Writing the Public API

Every exported symbol should have a ;;; docstring immediately above its definition. The standard format (from CLAUDE.md):

;;; db-open -- open an SQLite database file.
;;;
;;; Parameters:
;;;   path -- filesystem path to the database file (created if absent)
;;;
;;; Returns:
;;;   (ok db) on success, (err message) if the file cannot be opened
;;;
;;; Example:
;;;   (match (db-open "app.db")
;;;     (ok db) (println "opened")
;;;     (err m) (println "failed:" m))
;;;
;;; Since: Phase P2
(defn db-open [path :cstr] (result :ptr)
  ...)

Exported symbols without docstrings will be omitted from tur run docs output.

Module docstring (optional)

A spice module can optionally include a module-level docstring at the very top of the file, before the first defn, defmacro, defstruct, definstance, or defopaque. Place a contiguous ;;; block followed by a ;; comment line (which acts as the separator):

;;; myspice/db -- SQLite database bindings.
;;;
;;; Thin wrapper around libsqlite3; provides open/close/query/exec with
;;; result-typed error handling.
;;;
;;; Since: Phase P2
;; ---- SQLite bindings ----
(extern-c sqlite3_open ...)

Without a module docstring the page renders with no description block -- the per-symbol cards still appear normally.

Style: avoid cons ... 0 chains in examples

In README quick-starts and docstring examples, do not show runtime list values as (cons x (cons y 0)) chains. The trailing 0 is the nil-of-list footgun -- new readers have no way to tell that 0 means "end of list," and the chain itself reads in reversed nesting order.

Use the list macro instead. It expands to the same tcons/tnil cells, so it's a drop-in for any API that today takes a :int cons list:

;; Avoid
(group-by f (cons "g" 0))
(plot (cons (axes) (cons (function f) 0)))

;; Prefer
(group-by f (list "g"))
(plot (list (axes) (function f)))

Pair-cons ((cons key value) with no trailing 0) is fine -- a two-element pair is a clear, idiomatic data shape. The footgun is the nil-terminated chain.

If your spice API can take a Vec instead of a cons list, prefer that and document it with (vec-of ...). The end goal is for cons to disappear from quick-start surfaces entirely.

Typed variadic rest parameters

A & rest :T parameter type-checks against T even when T is a user-defined type -- a defopaque newtype, struct, ADT, or type application. The rest element type is resolved to its full type and each argument is checked by identity at the call site, so you can give a variadic API a real handle type instead of an untyped :int:

(defopaque Route :int)

;; Each rest arg must be a Route; a raw :int or a different opaque is rejected.
(defn launch [& routes :Route] :ptr<void>
  ...)

The old workaround of declaring the rest as :int and casting handles back inside the body is no longer needed. For an interface that mixes distinct handle types (e.g. middlewares and routes), use two explicit :list<T> parameters rather than one untyped rest -- a single & rest is one homogeneous element type by design.


Testing Your Spice

Add tur-test as an optional dependency and place test files in tests/:

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

A test file using tur-test:

(import test/assert :refer [assert-eq assert-ok assert-err])
(import test/suite  :refer [describe it])
(import test/runner :refer [run-all])
(import mylib/core  :refer [some-fn])

(describe "some-fn"
  (it "returns the expected value"
    (assert-eq (some-fn 10) 42))
  (it "returns err for invalid input"
    (assert-err (some-fn nil))))

(run-all)

Run the test suite:

tur test

For the full testing API see test-runner-contract.md.


Versioning


Publishing

As a standalone repository

git tag v0.1.0
git push && git push --tags

Consumers then add your spice with:

tur add https://github.com/you/tur-mylib --ref v0.1.0

Contributing to turmeric-spices

The turmeric-spices monorepo accepts spices that meet the bar for the ecosystem. To contribute:

  1. Fork the monorepo and add your spice under spices/<name>/.
  2. Add it to the workspace root build.tur :members list.
  3. Include a README.md and at least one test file.
  4. Open a pull request.

After merging, tag the spice's first release:

git tag math-v0.1.0
git push --tags

Consumers use --subdir spices/<name> when adding:

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

Spice registry (future)

Once pkg.turmeric-lang.org launches, tur publish will register the package and consumers will use tur add spice/<name> without a Git URL.


Per-file Commands Inside a Spice

tur build <dir> and tur run (project mode) know about the spice they live in because they start by reading build.tur. For tur build <dir> that means: descend into src/ (recursively, including nested src/<pkg>/ trees), skip the manifest itself, compile every module, resolve the include path from the project's own src/ plus each :spices dep's src/, and verify each declared :exports module has a backing source file -- failing loudly otherwise.

Generated .c/.h/.o and the final library/exe land under <spice-root>/build/{obj,bin,lib}/ by default. Override per-build with --build-dir <dir> / -B <dir>, with the TUR_BUILD_DIR env var, or durably with :build-dir "<path>" in build.tur (path is relative to the manifest dir). Precedence runs CLI flag > env > manifest > default. The build dir is auto-created with a .gitignore of *, so its contents never leak into VCS even if the dir itself gets tracked. (See manifest-driven-build-descent-plan.md.) The per-file subcommands tur check, tur emit-c, tur emit-h, tur build <file>, and tur run <file> get the same module resolution automatically -- they walk up from the input file looking for a sibling build.tur and add that spice's src/ (and its :spices deps' src/) to the include path.

This means editors, format-on-save hooks, LSP clients, and quick "compile this one file" loops work without per-spice configuration:

cd spices/frame
tur check src/frame/frame.tur     # resolves intra-spice imports
tur emit-c src/frame/schema.tur   # same
tur build src/frame/quickstart.tur -o /tmp/qs  # compiles one file, keeps the binary
tur run src/frame/quickstart.tur  # builds and executes

The walk-up is capped at 16 ancestor directories, so a stray build.tur far above your working tree won't accidentally win.

-I for extra directories

Explicit -I <dir> flags still work and take priority over auto-discovered paths -- useful for fixtures, vendored copies, or when you want to test against a different version of a dep:

tur check -I vendor/alternate src/main.tur

-I accepts both the spaced (-I path) and concatenated (-Ipath) forms.

--no-auto-spice escape hatch

If you need to compile a file as if no spice exists around it (rare -- typically only useful for resolver-fixture tests or when an unrelated build.tur is in the ancestor chain), pass --no-auto-spice:

tur --no-auto-spice check tests/fixtures/resolver/input.tur

This restores the pre-auto-discovery behavior: only the input file's own directory, the stdlib, and any explicit -I paths are searched.

When auto-discovery does not apply


Supporting CMake Consumers

If you want C and C++ projects to consume your spice via CMake or CPM without knowing anything about Turmeric, run:

tur emit-cmake

This reads build.tur and generates CMakeLists.txt, <name>Config.cmake, and helper modules. Commit those files, tag the release, and a CPM consumer can add your library with:

CPMAddPackage(
  NAME    tur-mylib
  URL     https://github.com/you/tur-mylib/archive/refs/tags/v0.1.0.tar.gz
  VERSION 0.1.0
)
target_link_libraries(my_app PRIVATE tur-mylib::all)

For the complete step-by-step see Using a Turmeric library from CMake.


Emscripten / WASM Support

Once tur build --target wasm lands, all cmake-deps spices will get WASM builds automatically when the underlying C library supports Emscripten. To make your spice Emscripten-compatible:


Global Spices as Libraries (v2)

A spice installed globally with tur install is currently usable as a command-line tool only: its :bin entries are symlinked into ~/.local/bin/ and become available as tur-<cmd> (or via the tur <cmd> fallthrough). The same install is not automatically visible as a library to other projects -- the global spices/ root is left out of the default module-resolution path to preserve build reproducibility.

A future v2 will let a project opt in to consuming a globally-installed spice as a library by naming it in its build.tur:

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

tur fetch would then validate the global install exists at a matching version and record its resolved SHA in tur.lock. A project-level :global-policy knob would decide whether a missing global install gets auto-installed or errors out.

This is deferred; until it ships, a spice that wants to be reused as a library should be added the normal way with tur add. See the global-spice-install plan for the full design sketch.


Release Checklist


Diagnostics to Watch For

TUR-D0001 -- no leading colons inside (fn ...) types

Leading colons inside a (fn ...) type expression are deprecated and the compiler emits TUR-D0001 wherever they appear. Write the new form when declaring function-typed parameters, return types, or higher-kinded abstractions in your spice:

;; Wrong (TUR-D0001):
(defn map-fn [^fat g :(fn [int] int) n :int] :int (g n))

;; Right:
(defn map-fn [^fat g :(fn [int] int) n :int] :int (g n))

The structural name : type colon (the one separating a parameter name from its type) is unaffected -- the rule only forbids colons inside a (fn ...) type. If you are migrating an older spice forward, run the codemod from #270 (bf3445e5) over your tree, or fix the hits by hand.

Name mangling and inline-C

If your spice exposes any inline-C bodies that reference sibling Turmeric defns by name, use the __TUR_CNAME_<source-name>__ splice rather than hand-spelling the mangled C identifier; the mangling scheme is reversible and injective (#275) but is still an internal detail. See name-mangling-guide.md for the encoding and c-integration-guide.md "Inline C blocks" for the splice form.


See Also