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.
Create a spice when you want to:
tur add.For code shared only within a single project, use the module system -- no separate package is needed.
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.
build.tur ManifestThe 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"]
})
| 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 |
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.
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.
defmodule + importWhen 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.
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.
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:
return NULL or no-op bodies mean any
code path that hits the stub produces garbage or does nothing -- with no
diagnostic.make-widget
as a static C function produce duplicate-symbol link failures the moment
they are compiled together (via (load ...) or tur build).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 [...]).
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}
}
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.
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
: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 verificationTo 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.
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.
:cmake-depsThe :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.
: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:
:BUILD_SHARED_LIBS "OFF":BUILD_EXAMPLES "OFF", :YYJSON_BUILD_TESTS "OFF"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.
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 overridesWhen 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"]}
}
:prefer-systemHeavy 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).INTERFACE_INCLUDE_DIRECTORIES and $<TARGET_FILE_DIR:...>
-- skipping both the clone and the source build. Otherwise the existing FetchContent block
runs exactly as before.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.
--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.
: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] })
: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.: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..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.#ifdef inside the source, as
vendor libraries already do.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
:cmake-depsVendor 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.
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.
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.
cons ... 0 chains in examplesIn 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.
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.
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.
MAJOR.MINOR.PATCH.vMAJOR.MINOR.PATCH for standalone repos (v0.1.0).turmeric-spices monorepo use the per-spice tag
format <spice>-vMAJOR.MINOR.PATCH (math-v0.1.0, sqlite-v0.2.1).v0.2.0-alpha.1.MAJOR for breaking API changes, MINOR for additive changes,
PATCH for bug fixes.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
The turmeric-spices monorepo accepts spices that meet the bar for the ecosystem. To contribute:
spices/<name>/.build.tur :members list.README.md and at least one test file.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
Once pkg.turmeric-lang.org launches, tur publish will register the
package and consumers will use tur add spice/<name> without a Git URL.
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 directoriesExplicit -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 hatchIf 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.
tur build <dir> -- already configures itself from the directory's
build.tur (reads the manifest, descends into src/, and resolves the
include path); the per-file walk-up does not apply. Note that the
single-file tur build <file> does auto-discover (it joins the
per-file family above); only the directory form is manifest-driven.tur format <file> -- the formatter doesn't resolve imports, so
include paths are irrelevant.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.
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:
#ifdef __EMSCRIPTEN__ in inline-C blocks to handle differences
(e.g. skipping native TLS when the browser provides its own TLS stack).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.
tur build --release passes with no warningstur test passes:exports;;; docstring (summary + params + returns + example)tur emit-cmake succeeds if you intend to support CMake consumersCHANGELOG.md entry writtengit tag v0.1.0 && git push --tags(fn ...) typesLeading 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.
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.
build.tur manifest reference and tur CLIextern-c, include-c, inline-C blockstur emit-cmake