Automatic metadata resolution
How Nucleus resolves and merges GraalVM reachability metadata at build time so native images compile without hand-written configuration.
Nucleus resolves and merges GraalVM reachability metadata at build time, so
packageGraalvmNative produces a working native binary without you writing reflection,
resource, or JNI configuration by hand. The metadata comes from five sources. Each is
generated into its own directory and passed to native-image, and the module's own metadata
is discovered from the classpath.
Level 1 — per-library metadata
The plugin ships 28 metadata files curated for the Kotlin desktop ecosystem, covering Compose,
Skiko, ktor, kotlinx.serialization, SQLite JDBC, Coil, JNA, FileKit, SLF4J, Sentry, and others.
Each file may declare a matchPackages condition. The filterGraalvmLibraryMetadata task reads
these files from the plugin JAR and includes a file only when the corresponding packages are on
the runtime classpath; files without a condition are always included. Libraries in the curated
set are covered without extra configuration.
Level 2 — GraalVM Reachability Metadata Repository
The resolveGraalvmReachabilityMetadata task resolves entries from the
Oracle GraalVM Reachability Metadata Repository
for each dependency on your runtime classpath. Resolution is enabled by default and uses
repository version 0.10.6.
Configure this source with the metadataRepository { } block: enabled, version,
excludedModules, and moduleToConfigVersion. See
Configuration.
Level 3 — platform-specific metadata
The generateGraalvmPlatformMetadata task writes reflection metadata for the current operating
system: sun.awt.windows.* on Windows, sun.lwawt.macosx.* on macOS, and sun.awt.X11.* on
Linux, along with Java2D pipelines, font managers, and security providers. It also injects a
reflection entry for your main class. No per-platform configuration is required in your build
script.
Level 4 — static bytecode analysis
The analyzeGraalvmStaticMetadata task scans every compiled class on the runtime classpath,
including your own module's classes, and detects:
- Native methods and their parameter and return types (JNI metadata).
Class.forName()andMethodHandles.Lookup.findClass()calls with literal class names (reflection metadata).getResource()andgetResourceAsStream()calls with literal paths (resource metadata).- JNI callback parameters — classes passed to native code that call back into Java.
- JNI superclass chains — parent classes needed for field access from native code.
@Serializableclasses — emits theCompanion.serializer()reflection entry.
Level 5 — bundled runtime metadata
The nucleus.graalvm-runtime module bundles GraalVM metadata inside its JAR:
- A
reachability-metadata.jsonwith serialization entries. - A
native-image.propertiesthat registers resource patterns (.svg,.ttf,.otf,nucleus/.*,META-INF/services/.*, andcomposeResources/.*). - SVM substitutions for the AWT font managers, the splash screen, and the X11 window class.
native-image discovers all of it from the classpath, so no configuration is needed.
How the sources merge
packageGraalvmNative runs the generation tasks and then passes each resulting directory to
native-image through repeated -H:ConfigurationFileDirectories= arguments — the per-library
output (Level 1), the resolved repository directories (Level 2), the platform metadata
(Level 3), and the static-analysis output (Level 4). Any manual entries in your project's own
config directory are included as well. Level 5 is read from the classpath by native-image
itself. Most apps compile to a native binary without any hand-written metadata.
Fill gaps with the tracing agent
The static sources can miss reflection driven by runtime values, dynamically loaded classes, or unusual library patterns. Run the agent once before a release to catch them:
./gradlew runWithNativeAgentExercise every screen and feature. The agent records reflection, JNI, resource, and proxy accesses into a temporary directory and merges them into your config without overwriting existing entries. In many cases it finds nothing new.
Clean up manual entries
If you accumulated manual entries that the automatic sources now cover, remove the redundant ones:
./gradlew cleanupGraalvmMetadataThe task compares your manual reachability-metadata.json against the combined baseline of
Levels 1–4 and removes anything already managed, reporting which entries were removed and which
remain.
What's next
- Native access — resource patterns and font substitutions on top of this metadata system.
- Tasks & CI — the full GraalVM task graph.
Configuration
Reference for the graalvm { } DSL block that configures GraalVM native-image builds, including toolchain, image name, build arguments, metadata repository, and per-OS settings.
Tasks & CI
The Gradle tasks the Nucleus plugin adds for GraalVM native image, where their output lands, and how to run them on each OS in CI.