Liking cljdoc? Tell your friends :D

com.blockether.nativeresolver

Find, cache and bind a prebuilt native library — the boring half of every Clojure FFM wrapper, factored out.

A library like com.blockether/imaging ships a Rust cdylib. The cdylib itself is published as per-platform artifacts (imaging-native-darwin-arm64, …), each carrying prebuilds/<platform>/<file> on the classpath. At runtime the wrapper must decide WHICH file to dlopen, and it must work in three very different situations: a dev pointing at a freshly cargo builded library, a fat uberjar or GraalVM native image with the library bundled, and a plain deps.edn user who only declared the pure-Clojure jar.

That resolution order is identical in every such library, so it lives here:

  1. An explicit path — <LIB>_NATIVE_PATH env or <group>.<lib>.native.path system property. Used verbatim, no existence check, no download.
  2. A bundled classpath resource prebuilds/<platform>/<file>. This is the uberjar / native-image path; a resource inside a jar is copied to a temp file (a dlopen needs a real path), a directory resource is used in place.
  3. A runtime download: the <lib>-native-<platform> artifact resolved through clojure.tools.deps (so Maven repos, mirrors and ~/.m2/settings.xml are honoured), extracted into ~/.cache/clj-<lib>/<version>/<platform>/ and cached there. Disable with <LIB>_DISABLE_DOWNLOAD=1.

tools.deps is reached through requiring-resolve, so it is only touched on step 3 and never becomes a hard dependency (it does not fit in a native image). This library itself has ZERO dependencies.

Everything is driven by a spec map built by spec; every derived name has a default and can be overridden:

(def ^:private lib (nr/spec {:lib "imaging"})) ;; => env IMAGING_NATIVE_PATH / IMAGING_CACHE_DIR / IMAGING_DISABLE_DOWNLOAD ;; prop com.blockether.imaging.native.path / .cache-dir / .disable-download ;; file libimaging_c.dylib | libimaging_c.so | imaging_c.dll ;; artifact com.blockether/imaging-native-<platform> ;; version from the classpath resource imaging/VERSION ;; cache ~/.cache/clj-imaging

And the binding half:

(defonce ^:private fns (delay (nr/bind! lib {:version [:ptr "imaging_version"] :decode [:ptr "imaging_decode" :ptr :i64] :free [nil "imaging_free_string" :ptr]})))

(nr/invoke @fns :decode seg (count bs))

Run the JVM with --enable-native-access=ALL-UNNAMED so the foreign linker may load the library without a restricted-method warning. Requires JDK 22+ (the final FFM API); JDK 25 is what we test on.

Find, cache and bind a prebuilt native library — the boring half of every
Clojure FFM wrapper, factored out.

A library like `com.blockether/imaging` ships a Rust cdylib. The cdylib itself
is published as per-platform artifacts (`imaging-native-darwin-arm64`, …), each
carrying `prebuilds/<platform>/<file>` on the classpath. At runtime the wrapper
must decide WHICH file to `dlopen`, and it must work in three very different
situations: a dev pointing at a freshly `cargo build`ed library, a fat uberjar
or GraalVM native image with the library bundled, and a plain `deps.edn` user
who only declared the pure-Clojure jar.

That resolution order is identical in every such library, so it lives here:

  1. An explicit path — `<LIB>_NATIVE_PATH` env or `<group>.<lib>.native.path`
     system property. Used verbatim, no existence check, no download.
  2. A bundled classpath resource `prebuilds/<platform>/<file>`. This is the
     uberjar / native-image path; a resource inside a jar is copied to a
     temp file (a `dlopen` needs a real path), a directory resource is used
     in place.
  3. A runtime download: the `<lib>-native-<platform>` artifact resolved
     through `clojure.tools.deps` (so Maven repos, mirrors and
     `~/.m2/settings.xml` are honoured), extracted into
     `~/.cache/clj-<lib>/<version>/<platform>/` and cached there. Disable with
     `<LIB>_DISABLE_DOWNLOAD=1`.

tools.deps is reached through `requiring-resolve`, so it is only touched on
step 3 and never becomes a hard dependency (it does not fit in a native image).
This library itself has ZERO dependencies.

Everything is driven by a spec map built by `spec`; every derived name has a
default and can be overridden:

  (def ^:private lib (nr/spec {:lib "imaging"}))
  ;; => env IMAGING_NATIVE_PATH / IMAGING_CACHE_DIR / IMAGING_DISABLE_DOWNLOAD
  ;;    prop com.blockether.imaging.native.path / .cache-dir / .disable-download
  ;;    file libimaging_c.dylib | libimaging_c.so | imaging_c.dll
  ;;    artifact com.blockether/imaging-native-<platform>
  ;;    version from the classpath resource imaging/VERSION
  ;;    cache ~/.cache/clj-imaging

And the binding half:

  (defonce ^:private fns
    (delay (nr/bind! lib {:version [:ptr "imaging_version"]
                          :decode  [:ptr "imaging_decode" :ptr :i64]
                          :free    [nil  "imaging_free_string" :ptr]})))

  (nr/invoke @fns :decode seg (count bs))

Run the JVM with `--enable-native-access=ALL-UNNAMED` so the foreign linker may
load the library without a restricted-method warning. Requires JDK 22+ (the
final FFM API); JDK 25 is what we test on.
raw docstring

artifactclj

(artifact spec)
(artifact {:keys [group artifact-prefix]} platform-id)

The per-platform native artifact as a qualified symbol, e.g. com.blockether/imaging-native-darwin-arm64.

The per-platform native artifact as a qualified symbol, e.g.
`com.blockether/imaging-native-darwin-arm64`.
sourceraw docstring

bind!clj

(bind! spec sigs)
(bind! spec sigs arena)

Resolve the library and bind every signature in sigs, returning {key MethodHandle}.

A signature is [ret sym-name & arg-layouts]ret nil for void:

(bind! spec {:version [:ptr "imaging_version"] :decode [:ptr "imaging_decode" :ptr :i64] :free [nil "imaging_free_string" :ptr]})

Do this once, behind a delay/defonce, so the library is opened lazily.

Resolve the library and bind every signature in `sigs`, returning
`{key MethodHandle}`.

A signature is `[ret sym-name & arg-layouts]` — `ret` nil for void:

  (bind! spec {:version [:ptr "imaging_version"]
               :decode  [:ptr "imaging_decode" :ptr :i64]
               :free    [nil  "imaging_free_string" :ptr]})

Do this once, behind a `delay`/`defonce`, so the library is opened lazily.
sourceraw docstring

boolclj

source

cache-rootclj

(cache-root {:keys [cache-dir cache-name] :as spec})

Where downloaded libraries are cached: <LIB>_CACHE_DIR env, <group>.<lib>.cache-dir property, the spec's :cache-dir, else ~/.cache/clj-<lib>.

Where downloaded libraries are cached: `<LIB>_CACHE_DIR` env,
`<group>.<lib>.cache-dir` property, the spec's `:cache-dir`, else
`~/.cache/clj-<lib>`.
sourceraw docstring

chrclj

source

downcallclj

(downcall lookup sym-name desc)

One MethodHandle for symbol sym-name in lookup with descriptor desc.

One `MethodHandle` for symbol `sym-name` in `lookup` with descriptor `desc`.
sourceraw docstring

extract-native!clj

(extract-native! jar-path res dest)

Copy the entry res out of the jar at jar-path to dest, creating parents. Returns dest.

Copy the entry `res` out of the jar at `jar-path` to `dest`, creating parents.
Returns `dest`.
sourceraw docstring

f32clj

source

f64clj

source

fdclj

(fd ret & args)

A FunctionDescriptor: ret (a layout keyword or nil for void) then the argument layouts.

A `FunctionDescriptor`: `ret` (a layout keyword or nil for void) then the
argument layouts.
sourceraw docstring

i16clj

source

i32clj

source

i64clj

source

i8clj

source

invokeclj

(invoke handles k & args)

Call the handle at k in a bind! map.

Call the handle at `k` in a `bind!` map.
sourceraw docstring

layoutclj

(layout x)

A MemoryLayout from a keyword (:ptr :i8 :i16 :i32 :i64 :f32 :f64 :bool :char plus :addr :int :long :size :float :double :u32 :u64 :byte :short aliases) or a MemoryLayout passed through.

A `MemoryLayout` from a keyword (`:ptr :i8 :i16 :i32 :i64 :f32 :f64 :bool
:char` plus `:addr :int :long :size :float :double :u32 :u64 :byte :short`
aliases) or a `MemoryLayout` passed through.
sourceraw docstring

library-file-nameclj

(library-file-name os base)

The platform's file name for the cdylib base base ("imaging_c" -> libimaging_c.dylib / libimaging_c.so / imaging_c.dll).

The platform's file name for the cdylib base `base` ("imaging_c" ->
libimaging_c.dylib / libimaging_c.so / imaging_c.dll).
sourceraw docstring

library-pathclj

(library-path spec)

Just the resolved Path — see resolve-library.

Just the resolved `Path` — see `resolve-library`.
sourceraw docstring

lookupclj

(lookup spec)
(lookup spec arena)

A SymbolLookup over the resolved library.

The arena defaults to Arena/ofAuto — GC-managed and process-lifetime, deliberately NOT Arena/ofShared: a shared arena is incompatible with Truffle runtime compilation, so a native image that also embeds GraalPy fails to build with "Arena.ofShared is not supported with runtime compilations". ofAuto needs no flag and keeps the lookup and its downcall handles alive as long as they are reachable.

A `SymbolLookup` over the resolved library.

The arena defaults to `Arena/ofAuto` — GC-managed and process-lifetime,
deliberately NOT `Arena/ofShared`: a shared arena is incompatible with Truffle
runtime compilation, so a native image that also embeds GraalPy fails to build
with "Arena.ofShared is not supported with runtime compilations". `ofAuto`
needs no flag and keeps the lookup and its downcall handles alive as long as
they are reachable.
sourceraw docstring

platformclj

(platform)

This JVM's platform as {:os :arch :id}, e.g. {:os "darwin" :arch "arm64" :id "darwin-arm64"}.

:os is one of darwin/linux/windows, :arch one of arm64/x64. Throws ex-info on anything else — a native library that has no build for the host should say so loudly rather than guess.

This JVM's platform as `{:os :arch :id}`, e.g.
`{:os "darwin" :arch "arm64" :id "darwin-arm64"}`.

`:os` is one of darwin/linux/windows, `:arch` one of arm64/x64. Throws
`ex-info` on anything else — a native library that has no build for the host
should say so loudly rather than guess.
sourceraw docstring

platform-idclj

(platform-id spec)

The platform id this spec resolves for — its :platform override, else the host's.

The platform id this spec resolves for — its `:platform` override, else the
host's.
sourceraw docstring

ptrclj

source

resolve-libraryclj

(resolve-library spec-map)

Resolve the native library for spec-map, returning {:path Path :source :configured|:bundled|:downloaded :platform id :resource res}. Throws ex-info (with :platform, :resource, :artifact) when no source has it.

Resolve the native library for `spec-map`, returning
`{:path Path :source :configured|:bundled|:downloaded :platform id :resource res}`.
Throws `ex-info` (with `:platform`, `:resource`, `:artifact`) when no source
has it.
sourceraw docstring

resource-nameclj

(resource-name spec)

The classpath resource a bundled library lives at: prebuilds/<platform>/<file>.

The classpath resource a bundled library lives at:
`prebuilds/<platform>/<file>`.
sourceraw docstring

specclj

(spec {:keys [lib group lib-base lib-files artifact-prefix env-prefix
              prop-prefix version version-resource resource-prefix cache-name
              cache-dir download? resolve-jar]
       :as m})

Normalise a resolver spec. :lib (a short id like "imaging") is the only required key; everything else is derived from it and overridable:

:lib short library id, e.g. "imaging" (required) :group Maven group id "com.blockether" :lib-base cdylib base name "<lib>_c" :lib-files explicit {os file-name} map, wins over :lib-base :artifact-prefix native artifact id prefix "<lib>-native-" :env-prefix env var prefix SCREAMING <lib> :prop-prefix system property prefix "<group>.<lib>" :version version string, skips the resource lookup :version-resource classpath resource holding it "<lib>/VERSION" :resource-prefix classpath dir holding the prebuilds "prebuilds" :cache-name cache directory name under ~/.cache "clj-<lib>" :cache-dir explicit cache root, wins over :cache-name :platform platform id override (default: this host's) :download? allow the tools.deps fallback true :resolve-jar (fn [version platform-id] -> Path) for the native jar

A NAMESPACED version resource (imaging/VERSION) is the default on purpose: a root VERSION collides with every other library that ships one, and whichever lands first on the classpath wins — which makes a library resolve a FOREIGN version and 404 a native artifact that never existed.

Normalise a resolver spec. `:lib` (a short id like "imaging") is the only
required key; everything else is derived from it and overridable:

  :lib               short library id, e.g. "imaging"          (required)
  :group             Maven group id                              "com.blockether"
  :lib-base          cdylib base name                            "<lib>_c"
  :lib-files         explicit {os file-name} map, wins over :lib-base
  :artifact-prefix   native artifact id prefix                   "<lib>-native-"
  :env-prefix        env var prefix                              SCREAMING <lib>
  :prop-prefix       system property prefix                      "<group>.<lib>"
  :version           version string, skips the resource lookup
  :version-resource  classpath resource holding it               "<lib>/VERSION"
  :resource-prefix   classpath dir holding the prebuilds         "prebuilds"
  :cache-name        cache directory name under ~/.cache         "clj-<lib>"
  :cache-dir         explicit cache root, wins over :cache-name
  :platform          platform id override (default: this host's)
  :download?         allow the tools.deps fallback               true
  :resolve-jar       (fn [version platform-id] -> Path) for the native jar

A NAMESPACED version resource (`imaging/VERSION`) is the default on purpose: a
root `VERSION` collides with every other library that ships one, and whichever
lands first on the classpath wins — which makes a library resolve a FOREIGN
version and 404 a native artifact that never existed.
sourceraw docstring

versionclj

(version {:keys [version version-resource] :as spec})

The version used to resolve the native artifact: the spec's :version, else the trimmed :version-resource from the classpath.

The version used to resolve the native artifact: the spec's `:version`, else
the trimmed `:version-resource` from the classpath.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close