Skip to content

feat(core-runtime): configurable native library cache directory (#303) - #686

Merged
kdroidFilter merged 1 commit into
nucleus-2.6from
feat/native-cache-dir
Sep 18, 2026
Merged

kdroidFilter merged 1 commit into
nucleus-2.6from
feat/native-cache-dir

Conversation

@kdroidFilter

Copy link
Copy Markdown
Collaborator

Closes #303.

Summary

The native library extraction path was hard-wired (%LOCALAPPDATA%\nucleus\native, ~/Library/Caches/nucleus/native, $XDG_CACHE_HOME/nucleus/native), so an application could not keep its config, cache, logs and native libraries under one user-defined directory.

Two levers, tried in order, each falling through to the next when the directory cannot be created or written to:

  1. -Dnucleus.native.cacheDir=<dir> (NativeLibraryLoader.CACHE_DIR_PROPERTY). Works for libraries loaded before any application code runs, and bakes into the launcher .cfg through the existing jvmArgs with no new plugin DSL.
  2. NativeLibraryLoader.cacheDirectory, for a path computed in main().
  3. The platform default, unchanged.

Notes:

  • The root is resolved once, at the first extraction, so every library of a run shares it. A later assignment cannot move libraries already loaded, so it is ignored with a warning naming the root in use.
  • A configured directory is validated with a real write probe (create + delete a temp file), not Files.isWritable, which is advisory on Windows. The probe leaves nothing behind.
  • The content-addressed layout of [BUG] Native library cache can load a mismatched version #304 (<root>/<platform>/<fingerprint>/<library>) is kept under the chosen root, so two versions still never share a file.
  • Applications packaged by the Gradle plugin ship their natives on java.library.path and extract nothing; this setting matters for fat JARs, IDE runs and distributions that bypass the plugin. Documented in the KDoc.

Also fixes a pre-existing defect in the default path: a set-but-empty or relative XDG_CACHE_HOME / LOCALAPPDATA made the cache root relative, putting native libraries under the process working directory. Such values are now ignored, as the XDG spec requires. buildSrc applies the same filter so its cache eviction keeps targeting the directory actually in use.

Compatibility

No change for anyone who configures nothing. Verified by compiling the pre-change loader in a separate worktree and comparing both side by side against the real Rust fs-watcher library: with XDG_CACHE_HOME unset or absolute, the resulting path, layout and timing are identical, with no extra directory creation and no probe. The only behaviour change is for the broken environments described above.

Test plan

  • 15 unit tests in NativeLibraryLoaderTest, covering the candidate chain, malformed and relative values, the platform defaults with and without the environment variables, and cacheRoot itself (configured root used and latched, unusable root falling through, default when nothing is configured)
  • 21-case end-to-end matrix, one JVM per case, against the real 500 KB Rust fs-watcher library: resolution order, fall-through, path handling, latch-once, content-addressed layout, no probe or temp files left behind, re-run reuses the extraction without rewriting it, and java.library.path still short-circuits extraction entirely
  • Real application: examples:fs-watcher-smoke run through Gradle with the property set — the library extracts to the chosen directory and the watcher reports real inotify create/delete events; re-run without the property falls back to the default
  • All 20 Linux native libraries of the repo, including the tao window backend, loaded into one relocated root in a single JVM
  • buildNativeLinux cache eviction still removes a stale library from the default cache
  • :core-runtime:check (test, detekt, ktlint, apiCheck) and :buildSrc:build
  • macOS and Windows: the platform branches are unchanged apart from the shared environment-variable filter, but neither was exercised here

Applications that keep config, cache and logs under a single directory can
now relocate the native library extraction path, which was hard-wired to
`%LOCALAPPDATA%\nucleus\native` / `~/.cache/nucleus/native`.

Two levers, tried in order, each falling through to the next when the
directory cannot be created or written to:

  1. `-Dnucleus.native.cacheDir=<dir>`, which also works for libraries
     loaded before any application code runs and bakes into the launcher
     `.cfg` through the existing `jvmArgs`;
  2. `NativeLibraryLoader.cacheDirectory`, for a path computed in `main()`;
  3. the platform default.

The root is resolved once, at the first extraction, so every library of a
run shares it; a later assignment is ignored with a warning. The
content-addressed layout of #304 is kept under the chosen root.

Also fixes a pre-existing defect in the default: a set-but-empty or
relative `XDG_CACHE_HOME` / `LOCALAPPDATA` made the root relative, putting
native libraries under the process working directory.
@kdroidFilter
kdroidFilter merged commit 617dc63 into nucleus-2.6 Sep 18, 2026
17 of 23 checks passed
@kdroidFilter
kdroidFilter deleted the feat/native-cache-dir branch September 18, 2026 05:58
@kdroidFilter kdroidFilter mentioned this pull request Sep 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant