Skip to content

Uplift the Native Image layer configuration model - #1011

Merged
vjovanov merged 30 commits into
masterfrom
rhei/issue-977
Aug 10, 2026
Merged

vjovanov merged 30 commits into
masterfrom
rhei/issue-977

Conversation

@jvukicev

@jvukicev jvukicev commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

What changed

This PR turns Native Image layers from an experimental, binary-scoped Gradle feature into a shared model with first-class Gradle and Maven support.

A layer produces a build-time .nil artifact plus runtime shared libraries. Consumers pass the .nil to Native Image and must ship the runtime libraries with the final executable.

Layer consumption is supported on GraalVM 25.1 and later. GraalVM 25.0.x is allowed for compatibility but can fail internally after loading a valid layer.

Gradle DSL: complete surface

A named layer is an entry in graalvmNative.layers. Its task is named native<LayerName>Layer and writes build/native/layers/<name>/<name>.nil.

Declare layer contents

Use any combination of these selectors in a layer's contents block:

graalvmNative {
    layers {
        selected {
            contents {
                // JDK modules; include java.base in a module-selecting base layer.
                modules('java.base', 'java.logging')

                // Packages found on the layer classpath.
                packages('org.slf4j')

                // Explicit files, FileCollections, directories, or providers.
                from(files('libs/local.jar'))

                // Every resolved artifact from a Configuration or Provider<Configuration>.
                fromConfiguration(configurations.runtimeClasspath)

                // An individually resolved module; transitive is true by default.
                dependencies('org.slf4j:slf4j-api:2.0.17')

                // An individually resolved module without its transitives.
                dependencies('com.acme:standalone:1.0') {
                    transitive = false
                }

                // A version-catalog accessor is also accepted.
                // dependencies(libs.slf4j.api)
            }

            // Producer-specific Native Image settings.
            buildArgs('-Ob')
            verbose = true
        }

        everything {
            contents {
                // The full runtime dependency graph, including project artifacts.
                all = true

                // all may also be combined with module/package selectors.
                modules('java.base')
            }
        }
    }
}

modules(...), packages(...), from(...), fromConfiguration(...), and dependencies(...) append selections. all = true selects the runtime graph. Empty, invalid, and duplicate selections fail before Native Image runs.

Consume named layers

The preferred consumer form is lazy, name-based usesLayer('<name>'); it works regardless of declaration order. Repeat it to select multiple layers.

graalvmNative {
    binaries {
        main {
            usesLayer('selected')
            usesLayer('everything')
        }

        test {
            usesLayer('selected')
        }

        create('api') {
            sharedLibrary = true
            imageName = 'api'
            usesLayer('selected')
        }
    }
}

Typed alternatives are available when passing the layer object or a provider:

graalvmNative {
    binaries.main {
        useLayer(graalvmNative.layers.selected)
        useLayer(graalvmNative.layers.named('everything'))
    }

    // The singular assignment selects one layer and replaces a prior assignment.
    binaries.test {
        layer = graalvmNative.layers.selected
    }
}

Inside a Groovy binary closure, use the qualified graalvmNative.layers.<name> form for typed references: the deprecated binary-level layers property shadows the extension container.

A layer can itself consume a named layer to construct a stack:

graalvmNative {
    layers {
        base {
            contents.modules('java.base')
        }
        framework {
            usesLayer('base')
            contents.packages('org.example.framework')
        }
    }
    binaries.main {
        usesLayer('framework')
    }
}

The older binary-scoped forms remain deprecated compatibility adapters:

graalvmNative {
    binaries {
        libdependencies {
            createLayer {
                modules = ['java.base']
                classpath.from(externalDependenciesOf(configurations.runtimeClasspath))
            }
        }
        main {
            useLayer('libdependencies')
        }
    }
}

nativeRun supplies selected layer directories to the platform loader. With the Gradle application plugin, installDist, distZip, and distTar include the executable, staged runtime libraries, and a loader-aware launcher. Named-layer reuse is first-class within a Gradle project; cross-project use currently requires manual file/runtime wiring.

Maven DSL: complete surface

Maven creates a layer with the layer-create goal. Configure the plugin as an extension so Maven consistently registers the nil artifact type in reactor and repository builds:

<plugin>
  <groupId>org.graalvm.buildtools</groupId>
  <artifactId>native-maven-plugin</artifactId>
  <version>${native.maven.plugin.version}</version>
  <extensions>true</extensions>
</plugin>

Declare layer contents

Bind layer-create in a producer execution. A <layer> supports module, package, path, dependency-coordinate, and full-runtime selectors:

<execution>
  <id>create-selected-layer</id>
  <phase>package</phase>
  <goals><goal>layer-create</goal></goals>
  <configuration>
    <layer>
      <name>selected</name>

      <modules>
        <module>java.base</module>
        <module>java.logging</module>
      </modules>

      <packages>
        <package>org.slf4j</package>
      </packages>

      <paths>
        <path>${project.basedir}/libs/local.jar</path>
      </paths>

      <dependencies>
        <dependency>
          <artifact>org.slf4j:slf4j-api:2.0.17</artifact>
          <transitive>true</transitive>
        </dependency>
        <dependency>
          <artifact>com.acme:standalone:1.0</artifact>
          <transitive>false</transitive>
        </dependency>
      </dependencies>
    </layer>
  </configuration>
</execution>

<execution>
  <id>create-everything-layer</id>
  <phase>package</phase>
  <goals><goal>layer-create</goal></goals>
  <configuration>
    <layer>
      <name>everything</name>
      <all>true</all>
      <!-- Legacy equivalent: <includeDependencies>all</includeDependencies> -->
    </layer>
  </configuration>
</execution>

Dependency coordinates use groupId:artifactId or groupId:artifactId:version; <transitive> defaults to true. The goal attaches both <name>.nil and a platform-classified runtime ZIP. A POM-packaging module may be a layer-only producer.

Consume one or more layers

Declare each producer as a normal dependency of type nil, then select it by coordinate. <useLayers> can be configured at plugin level or goal-execution level.

<dependencies>
  <dependency>
    <groupId>org.example</groupId>
    <artifactId>selected-layer</artifactId>
    <version>${project.version}</version>
    <type>nil</type>
    <scope>runtime</scope>
  </dependency>
  <dependency>
    <groupId>org.example</groupId>
    <artifactId>everything-layer</artifactId>
    <version>${project.version}</version>
    <type>nil</type>
    <scope>runtime</scope>
  </dependency>
</dependencies>

<plugin>
  <groupId>org.graalvm.buildtools</groupId>
  <artifactId>native-maven-plugin</artifactId>
  <extensions>true</extensions>
  <configuration>
    <useLayers>
      <useLayer>
        <artifact>org.example:selected-layer</artifact>
      </useLayer>
      <useLayer>
        <artifact>org.example:everything-layer:${project.version}</artifact>
      </useLayer>
    </useLayers>
  </configuration>
</plugin>

<useLayers> applies to every goal receiving that configuration: application compilation, native:test, shared-library builds, and layer-create. Use an execution-specific configuration when those goals need different stacks. Maven consumption is coordinate-only; a local .nil file path is not a supported <useLayers> form.

Repository consumers resolve the matching runtime ZIP and stage it under target/native/layer-runtime. Package that directory with the final executable and expose it through LD_LIBRARY_PATH (Linux), DYLD_LIBRARY_PATH (macOS), or PATH (Windows).

Shared behavior and deployment

  • Shared common/utils renders -H:LayerCreate and -H:LayerUse consistently for both plugins.
  • Layer producers keep their selected classpath/module-path inputs; consumers receive the producer layer automatically in the supported Gradle/Maven flows.
  • .nil files are build-time artifacts. Runtime shared libraries are deployment artifacts and must accompany the final executable.
  • .nil files can be large; plan Maven repository retention and cache capacity before broadly publishing layers.

Testing

Validated locally with grounding and formatting checks; shared utility tests; Gradle inspections, unit tests, and layered functional tests; and Maven inspections, unit tests, and layered functional tests.

Functional coverage includes module, package, path, dependency, and all-dependency selection where supported by the Native Image version; main executable, native-test, shared-library, and additional native-test-suite consumers; Maven reactor/nil resolution; and layered executable launch with the producer directory on LD_LIBRARY_PATH.

Closes #977

AI workflow

Generated by Rhei’s github-issue-fix workflow using 23 completed AI-assisted steps, 3 resolved models, and 3 focused review cycles.

View AI workflow details

Approved proposal ID: 193a8259f600cc11

The proposal was approved by an authorized repository member with /rhei approve 193a8259f600cc11 before implementation.

State path:

issue-intake → approval-check → approval-apply → implement-fix → validate/review cycle 1 → address-review → validate/review cycle 2 → address-review → validate/review cycle 3 → record-blocked-publication → completed

Deterministic approval, dispatch, review-routing, and completion transitions were performed by Rhei and did not consume model tokens.

  1. Issue intake and repository grounding — openai:gpt-5.6-sol via Codex, reasoning effort medium. Inspected the issue, repository rules, worktree, and specification fit. Tokens: 1,247,127 total; 1,231,844 input including 1,133,568 cached-read; 15,283 output.
  2. Implement the approved proposal — openai:gpt-5.6-sol via Codex, reasoning effort medium. Implemented the shared, Gradle, Maven, specification, sample, and test changes. Tokens: 23,727,061 total; 23,664,080 input including 22,914,688 cached-read; 62,981 output.
  3. Validation cycle 1 — openai:gpt-5.6-sol via Codex, reasoning effort medium. Ran grounding, formatting, focused unit, inspection, functional, and Native Image checks. Tokens: 2,849,937 total; 2,836,313 input including 2,691,712 cached-read; 13,624 output.
  4. Requirements review cycle 1 — openai:gpt-5.6-terra via Codex, reasoning effort medium. Reviewed issue scope and accepted behavior. Tokens: 523,722 total; 518,684 input including 450,048 cached-read; 5,038 output.
  5. Specification review cycle 1 — openai:gpt-5.6-terra via Codex, reasoning effort medium. Reviewed grund alignment, repository rules, and specification coverage. Tokens: 559,568 total; 553,019 input including 482,432 cached-read; 6,549 output.
  6. Implementation review cycle 1 — openai:gpt-5.6-terra via Codex, reasoning effort medium. Reviewed code quality, API boundaries, maintainability, and coverage. Tokens: 635,412 total; 627,370 input including 540,160 cached-read; 8,042 output.
  7. Validation review cycle 1 — openai:gpt-5.6-terra via Codex, reasoning effort medium. Assessed test evidence, failures, and remaining CI risk. Tokens: 202,333 total; 198,525 input including 161,792 cached-read; 3,808 output.
  8. Aggregate review cycle 1 — openai:gpt-5.6-sol via Codex, reasoning effort medium. Reconciled focused findings into the first repair list. Tokens: 137,439 total; 134,751 input including 108,800 cached-read; 2,688 output.
  9. Review repair cycle 1 — openai:gpt-5.6-sol via Codex, reasoning effort medium. Addressed the first aggregate review findings. Tokens: 10,152,091 total; 10,136,921 input including 9,917,312 cached-read; 15,170 output.
  10. Validation cycle 2 — openai:gpt-5.6-sol via Codex, reasoning effort medium. Re-ran focused checks after the first repair. Tokens: 3,897,354 total; 3,884,800 input including 3,730,176 cached-read; 12,554 output.
  11. Requirements review cycle 2 — openai:gpt-5.6-terra via Codex, reasoning effort medium. Rechecked issue requirements after repair. Tokens: 239,926 total; 236,685 input including 191,104 cached-read; 3,241 output.
  12. Specification review cycle 2 — openai:gpt-5.6-terra via Codex, reasoning effort medium. Rechecked specifications and grounding after repair. Tokens: 558,130 total; 549,894 input including 473,600 cached-read; 8,236 output.
  13. Implementation review cycle 2 — openai:gpt-5.6-terra via Codex, reasoning effort medium. Rechecked implementation quality and edge cases after repair. Tokens: 951,066 total; 943,220 input including 846,848 cached-read; 7,846 output.
  14. Validation review cycle 2 — openai:gpt-5.6-terra via Codex, reasoning effort medium. Reassessed validation evidence and disclosure. Tokens: 201,903 total; 197,665 input including 158,336 cached-read; 4,238 output.
  15. Aggregate review cycle 2 — openai:gpt-5.6-sol via Codex, reasoning effort medium. Reconciled the second focused review cycle. Tokens: 160,616 total; 158,379 input including 112,256 cached-read; 2,237 output.
  16. Review repair cycle 2 — openai:gpt-5.6-sol via Codex, reasoning effort medium. Addressed the second aggregate review findings. Tokens: 604,631 total; 598,878 input including 542,336 cached-read; 5,753 output.
  17. Validation cycle 3 — openai:gpt-5.6-sol via Codex, reasoning effort medium. Re-ran focused validation after the second repair. Tokens: 2,177,923 total; 2,165,805 input including 2,049,536 cached-read; 12,118 output.
  18. Requirements review cycle 3 — openai:gpt-5.6-terra via Codex, reasoning effort medium. Completed the final requirements-focused review. Tokens: 488,847 total; 483,569 input including 425,088 cached-read; 5,278 output.
  19. Specification review cycle 3 — openai:gpt-5.6-terra via Codex, reasoning effort medium. Completed the final specification and grounding review. Tokens: 490,430 total; 485,942 input including 428,544 cached-read; 4,488 output.
  20. Implementation review cycle 3 — openai:gpt-5.6-terra via Codex, reasoning effort medium. Identified the two remaining legacy compatibility blockers. Tokens: 890,896 total; 881,455 input including 800,896 cached-read; 9,441 output.
  21. Validation review cycle 3 — openai:gpt-5.6-terra via Codex, reasoning effort medium. Confirmed focused validation passed and recorded broader gaps. Tokens: 239,300 total; 234,312 input including 201,088 cached-read; 4,988 output.
  22. Aggregate review cycle 3 — openai:gpt-5.6-sol via Codex, reasoning effort medium. Marked publication blocked because the bounded repair visits were exhausted with two fixable compatibility findings. Tokens: 228,588 total; 225,929 input including 199,424 cached-read; 2,659 output.
  23. Blocked-publication record — openai:gpt-5.6-luna via Codex, reasoning effort medium. Recorded the local-only result without pushing or opening a PR. Tokens: 135,286 total; 133,502 input including 110,208 cached-read; 1,784 output.

Rhei accounting covers all 23 completed model invocations: 51,299,586 total tokens; 51,081,542 input tokens including 48,669,952 cached-read tokens; and 218,044 output tokens. Cached-read tokens are included in input and total counts. Cache-write dimensions were unsupported, and the recorded models were unpriced.

After the bounded Rhei review-repair loop ended, a human explicitly directed a manual continuation through Codex. That continuation added API-level @Deprecated migration documentation, added focused legacy producer/consumer compatibility coverage, reran the relevant grounding, inspection, unit, and functional checks, committed the result, and published this PR. This post-Rhei session is disclosed separately because its token usage was not captured by Rhei accounting.

@oracle-contributor-agreement oracle-contributor-agreement Bot added the OCA Verified All contributors have signed the Oracle Contributor Agreement. label Jul 30, 2026
@jvukicev jvukicev added the rhei label Jul 30, 2026

@vjovanov vjovanov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed this with a focus on usability gaps. The architecture is right — shared selection/rendering in common, typed lazy Gradle wiring, Maven-native nil artifact resolution — and the error paths that do exist fail before Native Image is invoked. But there are several gaps worth addressing before merge. File-specific issues are inline; the cross-cutting ones are below.

1. No end-user documentation. The PR updates the internal spec docs and samples, but docs/src/docs/asciidoc/gradle-plugin.adoc, maven-plugin.adoc, and the end-to-end guides contain no mention of layers at all, and changelog.adoc gets no entry. Users cannot discover the layers { } DSL, the layer-create goal, <useLayers>, or the nil dependency type from the published documentation. For a PR whose point is uplifting usability, this is the biggest gap.

2. The happy path was never verified on a supported Native Image release. Per the PR's own testing notes, the consumer image was only run on 25.0.3 — the exact release this PR blocks for consumption. The full produce → consume → run flow should be validated on a newer Native Image before merge.

3. Maven setup silently depends on <extensions>true</extensions> for the nil artifact handler registration, but that is only visible in the sample. Forgetting it degrades confusingly (the nil dependency falls back to the default artifact handler and can leak onto compile classpaths). It should be documented as a requirement with a recognizable failure mode.

4. Empty layers aren't validated. A named Gradle layer with no contents renders an unqualified -H:LayerCreate=<name>.nil with an empty classpath and defers to an obscure Native Image error. A configuration-time "layer 'x' has no contents" would be friendlier.

5. The new model still warns Experimental support for layered images enabled. DSL may change at any time. on every build. If the first-class DSL is the stable uplift, consider scoping or softening that message — it reads oddly against the PR's framing.

Summary of the inline comments: the documented useLayer(layers.base) example doesn't resolve (layers is shadowed inside binary closures), layer = assignment accumulates rather than replaces, fromConfiguration/all silently drop project dependencies (and Gradle/Maven all semantics diverge in multi-module builds), the 25.0.3 guard exists only in Maven, unreferenced nil dependencies are silently ignored, and malformed coordinates surface as raw IllegalArgumentExceptions.

@olpaw

olpaw commented Aug 3, 2026 •

Copy link
Copy Markdown
Member

In the example from the description:

    layers {
        dependencies {
            contents {
                modules("java.base")
                fromConfiguration(configurations.runtimeClasspath)
            }
        }
    }

is dependencies a user chosen name or part of the DSL used for layers?

@jvukicev

jvukicev commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

In the example from the description:

    layers {
        dependencies {
            contents {
                modules("java.base")
                fromConfiguration(configurations.runtimeClasspath)
            }
        }
    }

is dependencies a user chosen name or part of the DSL used for layers?

dependencies is a user-chosen layer name in the example.

@vjovanov

vjovanov commented Aug 3, 2026

Copy link
Copy Markdown
Member

PM test report

I reviewed the full diff, published the PR's plugins (1.1.8-SNAPSHOT) to Maven local, and drove them with scratch Gradle/Maven projects on Oracle GraalVM 25.0.1, deliberately trying to break the new surface. Overall: the new model is a real improvement and the happy paths work end-to-end — but I found 3 hard bugs (one breaks the advertised backwards compatibility), 1 latent CI failure, and a set of first-contact UX traps that will hit almost every adopter.

What works (verified by running it)

  • Gradle named layer end-to-end: layer builds to build/native/layers/deps/deps.nil, nativeCompile consumes via -H:LayerUse, nativeRun executes correctly (LD_LIBRARY_PATH wiring works).
  • The Layered image functional test is not configuration-cache compatible #957 fix is real: changing app source leaves the layer task UP-TO-DATE; only the app image rebuilds. Configuration cache stores and reuses with zero problems.
  • Maven reactor flow: mvn package orders base-layer before app via the nil dependency, builds base.nil, consumes it, produces a working executable. Out-of-reactor consumption from ~/.m2 also works (mvn install, then building the consumer alone).
  • Validation messages are mostly good: empty layer, typo'd useLayers coordinate ("Declare the corresponding <dependency> with <type>nil</type>"), duplicate selection ([deps, deps]), all+modules conflict, and the unreferenced-nil warning all fire with actionable text.
  • Module-only and all = true Gradle layers both build successfully.

Bugs (should block merge)

1. The legacy Gradle compatibility path is broken end-to-end. Using exactly the DSL from master's shipped sample (libdependencies { createLayer { … } } + useLayer("libdependencies")): the producer now emits <project-name>.so instead of libdependencies.so, because NativeImageCommandLineProvider removed the legacy targetOutputPath = layerCreateName override and only the new path sets imageName = "lib" + name. The producer "succeeds", then the consumer dies with:

VMError$HostedError: Expecting that shared layer library file starts with lib and ends with .so. Found: layer-lab.so

This matches the PR's own disclosure that review cycle 3 flagged "two remaining legacy compatibility blockers" and marked publication blocked. The retained unit tests check argument rendering, not the produced artifact name. Fix: have the legacy createLayer path set the image name to the binary name (it's already forced to start with lib).

2. externalDependenciesOf was renamed without a deprecated alias. Any build script written against 1.1.7 — including anyone who copied the current shipped sample — fails on upgrade with Could not find method externalDependenciesOf(). That directly contradicts "keeps the previous APIs operational" and §REQ-backwards-compatibility. Keep a deprecated delegating externalDependenciesOf for at least one release. (Note the rename also silently changed semantics: the ModuleComponentIdentifier filter was dropped, so it now includes project artifacts.)

3. Maven modules-only layer leaks the whole project classpath into the layer build. LayerCreateMojo sets classpath = selectedPaths; when the selection is modules-only that list is empty, and populateClasspath() treats empty as "use defaults", so the layer build got -cp base-layer.jar:commons-lang3.jar alongside -H:LayerCreate=base.nil,module=java.base. Gradle passes no -cp for the same declaration — a parity break and exactly the "leaky inputs" this uplift was meant to kill. The mojo should distinguish "explicitly empty" from "unset".

4. Latent CI failure in the Maven functional test. Its success branch (GraalVM ≥ 25.0.5) executes the layered app without setting LD_LIBRARY_PATH, and the executable has no rpath — readelf shows NEEDED libbase.so with no RUNPATH; running it fails with cannot open shared object file. On 25.0.3/25.0.4 the guard makes the test take the failure branch, so the success branch has never actually run. It will start failing the moment CI gets a newer GraalVM.

UX traps (the first hour of every adopter)

  • The layers shadowing footgun is worse than the docs admit — and the PR description's own example is broken. layer = layers.deps (and the PR body's useLayer(layers.dependencies)) fails with Could not get unknown property 'deps' for LayerOptions collection of type DefaultDomainObjectSet — the deprecated binary-level layers swallows the reference, and nothing in the error hints at graalvmNative.layers. Suggestions: fix the PR body; add a typed name-based lookup that resolves from the extension container lazily (e.g. usesLayer('deps')), or catch this case and error with "did you mean graalvmNative.layers.deps?".
  • Declaration order is load-bearing with no lazy escape. If binaries {} precedes layers {} in the build file, both graalvmNative.layers.deps and graalvmNative.layers.named('deps') fail (NativeImageLayer with name 'deps' not found). There is currently no order-independent way to reference a layer. Either document "layers first" prominently or accept a name-based provider that defers lookup to task-graph time.
  • No layer-name validation. create('my,base layer') happily registers task nativeMy,base layerLayer, then fails minutes later inside native-image with Unknown option 'my'. The shared validateLayerName only checks blank — both plugins should reject names not matching something like [A-Za-z0-9._-]+ at declaration time (commas, spaces, =, path separators all corrupt the flag).
  • Layer builds don't inherit binaries.all { buildArgs } — the PR's own test fixture had to duplicate -Ob into layers.all "because layer-sensitive options must match between producers and consumers", but the user docs never mention this. A user with -O3/GC flags on binaries will produce mismatched layers and get a confusing native-image failure. At minimum add a docs callout; consider warning on known-sensitive flag mismatches.
  • Metadata-repository asymmetry: Gradle layer tasks get no reachability-metadata wiring at all (a layer built fine while nativeCompile was failing the metadata schema check; Maven's layer-create does attach metadata dirs). Decide which is right — currently the two producers behave differently.
  • Maven runtime/deployment story is undocumented. The built app hard-depends on libbase.so from the producer's target directory; there's no rpath and no doc telling users to set LD_LIBRARY_PATH or ship the .so. Consider rpath support via -H:AdditionalLinkerOptions or at least a docs section; the e2e guide ends at "passes it to the consumer image".
  • The <extensions>true</extensions> claim is overstated: I built the whole layered reactor and consumed from the local repo without it. Keep the recommendation, but the IMPORTANT box asserting failure modes should be verified/softened.
  • Smaller items:
    • all = true can't be combined with modules(…) — the restriction feels artificial since the docs' own recommended pattern (modules + fromConfiguration) is equivalent.
    • Undocumented includeDependencies accepts only "all" and silently ignores anything else.
    • The 25.0.3/25.0.4 guard has no override property and won't match vendor version formats with suffixes (e.g. Mandrel-style x.y.z.n).
    • Duplicate detection with provider forms only fires at execution, after the layer has already spent minutes building.
    • No Kotlin DSL sample (the Groovy container syntax doesn't translate 1:1).
    • Gradle has no cross-project layer sharing story (Maven does) — worth a "future work" note for parity.

Suggested priority

Items 1–4 are merge blockers (1 and 2 break the PR's explicit compatibility promise; 3 breaks the model's core claim; 4 is a time bomb in CI). The shadowing/ordering/name-validation trio is what I'd fix next — they're cheap, and they're the first three errors any real adopter will hit.


Report produced by a hands-on review: static review of all 55 files plus scripted break-it scenarios against locally published 1.1.8-SNAPSHOT plugins (Gradle 9.6.1, Maven, Oracle GraalVM 25.0.1).

@vjovanov

vjovanov commented Aug 3, 2026

Copy link
Copy Markdown
Member

Follow-up: layer test-coverage audit

Two questions worth answering explicitly for this PR: (1) are all selector types — module, package, path, all — tested, and (2) are layers consumed from all binary kinds — executables, test binaries, shared libraries? The answer to both is no — coverage is thinner than it looks.

Selector coverage

Level module package path all
Shared renderer unit test (NativeImageLayerArgumentsTest) ✔ ✔ ✔ ✔
Gradle unit (NativeImageCommandLineProviderTest) ✔ ✔ ✔ (jars) –
Gradle functional (real native-image build) ✔ ✗ ✔ (fromConfiguration) ✗
Maven unit (LayerConfigurationTest) – ✗ ✔ (dependency matching only) ✔ (mapping only)
Maven functional (real build) ✔ ✗ ✔ (<dependencies>) ✗

Every selector is covered at the string-rendering level, but against a real native-image only module= + path= ever run:

  • package= is never exercised in any real build, in either plugin. Given Experimental support for layered images #719 itself flagged package selection as the unreliable one (the logback case), it is the selector most in need of a functional test — and the Gradle unit coverage for it goes through the legacy CreateLayerOptions path, not the new layers { contents { packages(...) } } DSL.
  • all is never built for real anywhere. (I built one manually against 25.0.1 — it works — but the repo has no test proving the bare -H:LayerCreate=name.nil + full-classpath form produces a usable layer.)
  • Maven's explicit <paths> selector (LayerConfiguration.getPaths() → resolveSelectedPaths) has zero tests at any level.
  • A modules-only Maven layer is untested — the sample and functional test always add a dependency. That is exactly the untested combination where the classpath-leak bug from my earlier report hides (empty classpath list falls back to the full project classpath).

Consumption contexts

Only main executable binaries consume layers in any test, in both plugins:

  • Gradle test binary: nothing links a layer into binaries.test / nativeTest. The plumbing exists (DelegatingCompileOptions explicitly forwards useLayer / getLayerCreate / getLayerFiles, which only matters for delegated test-style options), so the PR added the forwarding but never tests the scenario it enables.
  • Shared library consumer: no test or sample builds a sharedLibrary = true binary that consumes a layer. Since layer linkage is literally dynamic-library linkage (NEEDED libbase.so), a shared-lib-consuming-a-layer case has real failure potential and is unverified.
  • Layer chaining (layer consumes layer) — a gap beyond testing: the new Gradle NativeImageLayer DSL has no consumption API at all (only contents, buildArgs, verbose), so a multi-level layer stack — which Native Image supports (LayerUse + LayerCreate in one invocation) and the legacy binary-scoped DSL could express — is now inexpressible in the new model. Maven can express it (layer-create inherits useLayers from AbstractNativeImageMojo) but never tests it.
  • Maven native:test with layers: NativeTestMojo extends AbstractNativeImageMojo, so a plugin-level <useLayers> configuration silently applies to the test image too — untested, and arguably surprising for users who set useLayers in the shared plugin <configuration>.

Suggested additions

  1. One functional test per plugin using packages selection.
  2. A modules-only Maven layer functional test (it will catch the classpath-leak bug).
  3. A nativeTest-consumes-layer test (Gradle) and a native:test-with-useLayers test (Maven) — or an explicit decision that test images do not consume layers.
  4. A shared-library-consumes-layer test.
  5. A decision on whether the new Gradle DSL should support layer chaining before the legacy path (which could express it) is removed.

@vjovanov

vjovanov commented Aug 6, 2026

Copy link
Copy Markdown
Member

Product review: driving the layer feature end-to-end

I published the plugins from this branch and used them from real Gradle and Maven projects (not the functional test harness) to answer a few adoption questions: does it work with non-Maven dependencies, can it be used for tests / shared libraries / executables, can several consumers share one layer, and can the result actually be shipped.

Short version: the DSL and goal surface are in good shape, and the common cases work. The blocker is deployment — a layer consumed from a Maven repository builds successfully and produces a binary that cannot start.

Environment

Layer consumption is broken on GraalVM 25.0.x, so everything below was run on GraalVM CE 25.1.3 unless stated otherwise.

# publish the branch
git fetch origin pull/1011/head:pr-1011 && git checkout pr-1011
./gradlew publishAllPublicationsToCommonRepository --no-parallel   # -> build/common-repo

# GraalVM CE 25.1.3
curl -sLO https://github.com/graalvm/graalvm-ce-builds/releases/download/graal-25.1.3/graalvm-community-jdk-25i1-25.0.3_linux-x64_bin.tar.gz
tar xzf graalvm-community-jdk-25i1-25.0.3_linux-x64_bin.tar.gz
export GRAALVM_HOME=$PWD/graalvm-community-25.1.3+9.1
export JAVA_HOME=$GRAALVM_HOME

1. Blocker: a repository-resolved layer produces an unrunnable binary

layer-create attaches only the .nil. The .so that the executable actually links against is never attached, so it never reaches the local or remote repository.

Repro

cd samples/layered-maven-application
mvn -Dnative.maven.plugin.version=1.1.8-SNAPSHOT install     # reactor build, succeeds

ls ~/.m2/repository/org/graalvm/buildtools/samples/base-layer/1.0-SNAPSHOT/
# base-layer-1.0-SNAPSHOT.jar
# base-layer-1.0-SNAPSHOT.nil   (916 MB)
# base-layer-1.0-SNAPSHOT.pom
find ~/.m2/repository -name '*.so'            # nothing

# now build a consumer OUTSIDE the reactor: copy application/ out, drop the <parent>,
# give it its own groupId/version, and keep the <type>nil</type> dependency + <useLayers>
cd /tmp/standalone-consumer && mvn package    # succeeds
./target/standalone-consumer
./target/standalone-consumer: error while loading shared libraries:
libbase.so: cannot open shared object file: No such file or directory

The same happens for native:test. The test image builds, getUsedLayerLibraryDirectories() points LD_LIBRARY_PATH at the .m2 directory holding the .nil, and the run dies:

target/native-tests: error while loading shared libraries: libbase.so: cannot open shared object file
[ERROR] Failed to execute goal ...:test ... returned non-zero result

It only works inside a reactor because base.nil and libbase.so happen to sit together in target/native/layers/base/.

So the PR's claim that "attached nil artifacts work in reactor, local-repository, and remote-repository builds" holds for the build but not for the result. The .nil is a build-time input and the .so is the runtime artifact; they need to travel together — attaching the .so as a second artifact (classifier or so/dylib/dll type) would fix both the standalone build and native:test.

Related sizing concern: the java.base layer installs a 916 MB .nil into ~/.m2, and Gradle produces 249–321 MB .nil files. That is not something teams will push to a shared repository, which makes the .nil-as-Maven-artifact model worth a second look.

On the Gradle side the same gap exists but is hidden: nativeRun sets LD_LIBRARY_PATH for you, so it looks fine in development, but nothing wires the .so into installDist/distZip, there is no -rpath $ORIGIN support, and gradle-plugin.adoc has no deployment section at all.

build/native/nativeCompile/app
# error while loading shared libraries: liblegacy.so: cannot open shared object file

2. No version gate on layer consumption

On GraalVM 25.0.1 — the LTS most users have installed — consuming a layer fails with a raw Native Image internal error:

Error: Trying to create a new ImageHeapConstant for
com.oracle.svm.core.reflect.serialize.SerializationSupport@... after the ImageHeapScanner is sealed.

The test suite already knows about this (hasLayerConsumptionBug() = !isGraalVMVersionAtLeast(version, 25, 1)), but there is no equivalent check in the plugins, so users get the crash with no hint. A pre-flight error like "Native Image layers require GraalVM 25.1 or later" would save a lot of confusion.

Related: the PR description says validation ran on Oracle GraalVM 25.0.2, where hasLayerConsumptionBug() skips the layer-consumption tests. Worth confirming which layer tests CI actually executed before merging.


3. Native tests fail unless the layer has the right JDK modules

The sample ships modules('java.base'), which cannot work for a test image. With it:

Error: com.oracle.svm.shared.util.VMError$HostedError: Newly seen platform package package java.sql

Adding java.sql and java.management fixes it — which is exactly what the functional test in this PR had to do:

layers.dependencies.contents.modules('java.sql', 'java.management')
binaries.test { usesLayer('dependencies') }
[         1 tests successful      ]

This requirement is not documented anywhere, and Newly seen boot package package ... is not a message anyone can act on. Application code that touches XML additionally needs java.xml; adding too many modules then trips a different failure (The core type AnalysisType<...> was not seen as reachable in the initial layer), so the working window is narrow and needs guidance.


4. Custom shared-library binary + layer crashes without mainClass

binaries {
  create('api') { usesLayer('dependencies'); sharedLibrary = true }   // fails
}
java.lang.Error: ImageSingletons do not contain key com.oracle.svm.core.JavaMainWrapper$JavaMainSupport

Isolated it to the combination:

configuration result
create('api') { sharedLibrary = true } (no layer) builds
binaries.main.sharedLibrary = true + layer builds
create('api') { usesLayer(...); sharedLibrary = true } crashes
create('api') { usesLayer(...); sharedLibrary = true; mainClass = '...' } builds

Worth validating in the plugin rather than letting Native Image abort.


5. Non-Maven dependencies

Gradle: works well. I built a jar by hand with javac/jar (no build tool, no coordinates) plus a project(':liblocal') dependency:

dependencies {
    implementation files('libs/legacy.jar')
    implementation project(':liblocal')
    implementation 'org.apache.commons:commons-lang3:3.17.0'
}
graalvmNative {
    layers {
        legacy     { contents { from(files('libs/legacy.jar')) } }
        everything { contents { fromConfiguration(configurations.runtimeClasspath) } }
    }
    binaries { main { usesLayer('legacy') } }
}

Both layers build, fromConfiguration correctly wires :liblocal:jar as a producer task dependency, and ./gradlew :app:nativeRun prints legacy-jar | project-dependency | Maven-dependency. No complaints here.

Maven: producing is fine, consuming is coordinate-only. UseLayerConfiguration has a single artifact field parsed as groupId:artifactId[:version], so there is no way to point <useLayers> at a .nil file. A layer produced by Gradle, downloaded, or vendored cannot be consumed without mvn install:install-file. A <useLayer><file>...</file></useLayer> form would close this.


6. Sharing one layer across consumers and across projects

Within one Gradle project: works. Four consumers on one layer configure cleanly, and main + a second executable + test all build against a single nativeDependenciesLayer:

> Task :nativeDependenciesLayer UP-TO-DATE
> Task :nativeCompile
> Task :nativeOtherCompile
> Task :nativeTestCompile

Across Gradle projects: only via an undocumented escape hatch. NativeImageCompileOptions.getLayerFiles() is public, and this does work:

binaries.create('external') {
  mainClass = 'org.graalvm.demo.Application'
  layerFiles.from(someProject.layout.buildDirectory.file('native/layers/deps/deps.nil'))
}
- '-H:LayerUse' (origin(s): command line)
Finished generating 'layered-java-application' in 29.1s.

But there is no task-dependency wiring (a clean build fails), no name validation, and no compatibility-classpath propagation. A supported usesLayer(file) / usesLayerFrom(configuration) — ideally an outgoing variant so project(':base-layer') just works — would make this the answer to "how do several projects share one layer" instead of a trick.


7. pom-packaging producer works, and is probably the pattern to recommend

A module whose only job is declaring dependencies:

<packaging>pom</packaging>
<dependencies><dependency>...commons-lang3...</dependency></dependencies>
...
<execution>
  <phase>package</phase>
  <goals><goal>layer-create</goal></goals>
  <configuration><layer><name>deps</name><all>true</all></layer></configuration>
</execution>

mvn install produces a clean pom-only-layer-1.0.0.nil + .pom with no jar. This is the tidiest producer shape and is not mentioned anywhere in the docs.


8. Smaller items

  • Configuration cache fails: 15 problems were found storing the configuration cache (DefaultProject, TaskContainer, SourceSetContainer not serializable). Pre-existing — BaseNativeImageOptions on master already holds a TaskContainer — but NativeImageLayer and LayerContents add a Project field, so "configuration-cache-safe" in the description overstates the current state.
  • Custom binaries all default to the same image name (layered-java-application appears in both nativeCompile/ and nativeOtherCompile/). Pre-existing, but layers make multi-binary builds much more common.
  • changelog.adoc gains a "Release 1.1.6" section holding an unrelated Surefire entry, inserted above "Release 1.0.1".

Documentation gaps

The new adoc sections cover syntax well. What is missing is everything needed to actually ship:

  1. Requirements — "layers require GraalVM 25.1 or later", at the top of both pages.
  2. Deployment, with a worked example — currently one paragraph in maven-plugin.adoc and nothing in gradle-plugin.adoc. Needs: which files ship (.so, not .nil), how to place them next to the application, a container/tarball layout, and the LD_LIBRARY_PATH / DYLD_LIBRARY_PATH / PATH setup. As written, the docs describe a build whose output will not start.
  3. .nil vs .so — build-time input vs runtime artifact, and their real sizes, so nobody is surprised by a 900 MB repository artifact.
  4. Module selection for native tests — that java.base alone is not enough, that java.sql / java.management / java.xml are commonly required, and what Newly seen boot package ... means. This is the most likely first failure a user hits.
  5. Sharing a layer across modules — an end-to-end Maven example (producer, consumers, reactor vs repository) and, for Gradle, an honest statement of the current limitation.
  6. pom-packaging producer module as the recommended producer shape.
  7. Shared libraries — that a layered shared-library binary needs a mainClass.

Happy to open follow-up issues for any of these, or send a docs patch for items 1–4.

@vjovanov

vjovanov commented Aug 6, 2026

Copy link
Copy Markdown
Member

Correction: the "core type ... not seen as reachable the initial layer" failure is a missing java.base

In my earlier review I described this failure:

com.oracle.svm.shared.util.VMError$HostedError: The core type
AnalysisType<RuntimeMetadataDecoderImpl$$Lambda/0x... -> LambdaSubstitutionType@...,
instantiated: true, reachable: true> was not seen as reachable the initial layer.

and attributed it to "adding too many modules" to the layer. That was wrong. I've since bisected it, and the actual cause is much simpler and much more actionable:

A module-selecting layer that omits java.base produces a broken layer.

The reason I misread it is that the passing case in my notes was the sample, whose build.gradle already contains modules("java.base") — my appended modules('java.sql', 'java.management', 'java.xml') added to it rather than replacing it. My own projects specified only the extra modules, so they silently lost java.base.

Evidence

One variable changed, everything else identical (GraalVM CE 25.1.3, nativeTest):

layer modules app result
java.sql, java.management, java.xml trivial ❌ core type ... not seen as reachable the initial layer
java.base, java.sql, java.management, java.xml trivial ✅ 1 tests successful
java.sql, java.management, java.xml commons-lang3 + file jar + project dep ❌ core type ... not seen as reachable the initial layer
java.base, java.sql, java.management, java.xml commons-lang3 + file jar + project dep ✅ 1 tests successful

Repro

graalvmNative {
    metadataRepository.enabled = false
    layers { dependencies { contents { modules('java.sql', 'java.management', 'java.xml') } } }
    binaries { main { usesLayer('dependencies') }; test { usesLayer('dependencies') } }
}
./gradlew :nativeTest      # -> core type ... was not seen as reachable the initial layer

Add 'java.base' to the modules(...) list, change nothing else:

./gradlew :nativeTest      # -> [ 1 tests successful ]

Corrected model of layer module selection

Two distinct failure modes, and they're easy to confuse:

  • Missing java.base → The core type AnalysisType<...> was not seen as reachable the initial layer. Reads like an internal Native Image bug; gives no hint that a module is missing.
  • Missing some other needed module → Newly seen boot package <pkg> / Newly seen platform package <pkg>. Nameable: add the module that owns the package.

So java.base is necessary but not sufficient. My original point stands that the sample's modules('java.base') alone is not enough for a native test — that still fails with Newly seen platform package package java.sql, and needs java.sql and java.management on top. The correct guidance is java.base plus whatever the final image touches.

Suggested fix

This is cheap to catch in the plugin. When a layer has a non-empty module selection that doesn't include java.base, fail (or at minimum warn) before invoking Native Image, with a message naming java.base. That converts the least actionable error in the whole feature into a one-line fix for the user. It fits naturally next to the existing empty/duplicate/malformed validation in ArtifactSelection / NativeImageLayerArguments.

Failing that, the docs must state plainly that java.base is effectively mandatory in every module-selecting layer, and that modules(...) appends rather than replaces — the sample's modules("java.base") masking my override is exactly how a user will trip over this.

What this does and doesn't change

It resolves the one failure in my review I couldn't characterize, and it's good news: layered native tests do work for non-trivial applications on GraalVM 25.1, once java.base is present. The other findings are unaffected — in particular the .so publishing gap remains the blocker, since a repository-resolved layer still yields a binary that cannot start.

@vjovanov

vjovanov commented Aug 7, 2026

Copy link
Copy Markdown
Member

The PR description still doesn't show me all the possibilities of the DSL.

contents {
modules('java.base', 'java.logging')
packages('org.slf4j')
fromConfiguration(configurations.runtimeClasspath)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this capture the org.slf4j?

@jvukicev jvukicev Aug 7, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Yes, the configuration selector captures the resolved artifact, so combining it with the package selector was misleading. I removed it from this example. Our package-selector functional test already uses the intended form, packages("org.slf4j"), without fromConfiguration.


binaries {
test {
usesLayer('sharedRuntime')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does this come from?

@jvukicev jvukicev Aug 7, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was assuming a project-defined integTest source set and task, which is too project-specific for this guide. I removed that additional-test-suite example.


[source,groovy]
----
graalvmNative {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these live in our tests?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you clarify what you mean here? We already test the documented layer scenarios in our functional tests. I had intended this as a separate user-facing guide for layered-image workflows, rather than moving the documentation into test fixtures.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like the doc snippets. I would like to test all of these verbatim.

layer and rebuild it.

[[layer-chaining]]
== Prepare Multi-Level Layer Stacks

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this possible?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DSL can express it, but current Native Image releases do not support the resulting chained shared-layer topology. I removed the public documentation and support claims, while leaving the API in place for a future Native Image release that supports it. We also retain an ignored functional test for this capability.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I would not mention it as it doesn't work.

@vjovanov

Copy link
Copy Markdown
Member

We can do followup fixes for this later if needed. It has passed enough reviews that we can merge it and use it in the next release.

@vjovanov
vjovanov merged commit f9d090c into master Aug 10, 2026
70 of 71 checks passed
@vjovanov
vjovanov deleted the rhei/issue-977 branch August 10, 2026 09:28
mergify Bot added a commit to ArcadeData/arcadedb that referenced this pull request Aug 16, 2026
….8 to 1.1.9 [skip ci]

Bumps [org.graalvm.buildtools:native-maven-plugin](https://github.com/graalvm/native-build-tools) from 1.1.8 to 1.1.9.
Release notes

*Sourced from [org.graalvm.buildtools:native-maven-plugin's releases](https://github.com/graalvm/native-build-tools/releases).*

> 1.1.9
> -----
>
> What's Changed
> --------------
>
> * Make repository test output concise by default by [`@​jormundur00`](https://github.com/jormundur00) in [graalvm/native-build-tools#1004](https://redirect.github.com/graalvm/native-build-tools/pull/1004)
> * Decouple Maven plugin unit tests from remote bootstrap tasks by [`@​jormundur00`](https://github.com/jormundur00) in [graalvm/native-build-tools#1007](https://redirect.github.com/graalvm/native-build-tools/pull/1007)
> * Clarify build prerequisites and add a core build mode by [`@​jormundur00`](https://github.com/jormundur00) in [graalvm/native-build-tools#1012](https://redirect.github.com/graalvm/native-build-tools/pull/1012)
> * Release 1.1.8 by [`@​graalvmbot`](https://github.com/graalvmbot) in [graalvm/native-build-tools#1015](https://redirect.github.com/graalvm/native-build-tools/pull/1015)
> * Bump version to 1.1.9-SNAPSHOT by [`@​graalvmbot`](https://github.com/graalvmbot) in [graalvm/native-build-tools#1016](https://redirect.github.com/graalvm/native-build-tools/pull/1016)
> * Uplift the Native Image layer configuration model by [`@​jormundur00`](https://github.com/jormundur00) in [graalvm/native-build-tools#1011](https://redirect.github.com/graalvm/native-build-tools/pull/1011)
> * Update reachability metadata to 1.0.10 by [`@​graalvmbot`](https://github.com/graalvmbot) in [graalvm/native-build-tools#1021](https://redirect.github.com/graalvm/native-build-tools/pull/1021)
>
> **Full Changelog**: <graalvm/native-build-tools@1.1.8...1.1.9>


Commits

* [`70a22ad`](graalvm/native-build-tools@70a22ad) Release 1.1.9
* [`226c2f2`](graalvm/native-build-tools@226c2f2) Merge pull request [#1021](https://redirect.github.com/graalvm/native-build-tools/issues/1021) from graalvm/update-metadata-to-1.0.10
* [`750e24d`](graalvm/native-build-tools@750e24d) Update reachability metadata to 1.0.10
* [`f9d090c`](graalvm/native-build-tools@f9d090c) Clarify layer selector and test suite support
* [`8a62507`](graalvm/native-build-tools@8a62507) Skip later Gradle layer consumers on GraalVM 25.0
* [`fa9cb64`](graalvm/native-build-tools@fa9cb64) Narrow GraalVM 25.0 layer test skips
* [`a7f0ca4`](graalvm/native-build-tools@a7f0ca4) Skip Gradle layer consumers on GraalVM 25.0
* [`cf67f05`](graalvm/native-build-tools@cf67f05) Skip Maven shared layer test on GraalVM 25.0
* [`60da37c`](graalvm/native-build-tools@60da37c) Skip Maven all-selector layer test on GraalVM 25.0
* [`0498739`](graalvm/native-build-tools@0498739) Fix layered image runtime distribution
* Additional commits viewable in [compare view](graalvm/native-build-tools@1.1.8...1.1.9)
  
[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility\_score?dependency-name=org.graalvm.buildtools:native-maven-plugin&package-manager=maven&previous-version=1.1.8&new-version=1.1.9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
  
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot show  ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
mergify Bot added a commit to robfrank/linklift that referenced this pull request Aug 20, 2026
….6 to 1.1.9 [skip ci]

Bumps [org.graalvm.buildtools:native-maven-plugin](https://github.com/graalvm/native-build-tools) from 1.1.6 to 1.1.9.
Release notes

*Sourced from [org.graalvm.buildtools:native-maven-plugin's releases](https://github.com/graalvm/native-build-tools/releases).*

> 1.1.9
> -----
>
> What's Changed
> --------------
>
> * Make repository test output concise by default by [`@​jormundur00`](https://github.com/jormundur00) in [graalvm/native-build-tools#1004](https://redirect.github.com/graalvm/native-build-tools/pull/1004)
> * Decouple Maven plugin unit tests from remote bootstrap tasks by [`@​jormundur00`](https://github.com/jormundur00) in [graalvm/native-build-tools#1007](https://redirect.github.com/graalvm/native-build-tools/pull/1007)
> * Clarify build prerequisites and add a core build mode by [`@​jormundur00`](https://github.com/jormundur00) in [graalvm/native-build-tools#1012](https://redirect.github.com/graalvm/native-build-tools/pull/1012)
> * Release 1.1.8 by [`@​graalvmbot`](https://github.com/graalvmbot) in [graalvm/native-build-tools#1015](https://redirect.github.com/graalvm/native-build-tools/pull/1015)
> * Bump version to 1.1.9-SNAPSHOT by [`@​graalvmbot`](https://github.com/graalvmbot) in [graalvm/native-build-tools#1016](https://redirect.github.com/graalvm/native-build-tools/pull/1016)
> * Uplift the Native Image layer configuration model by [`@​jormundur00`](https://github.com/jormundur00) in [graalvm/native-build-tools#1011](https://redirect.github.com/graalvm/native-build-tools/pull/1011)
> * Update reachability metadata to 1.0.10 by [`@​graalvmbot`](https://github.com/graalvmbot) in [graalvm/native-build-tools#1021](https://redirect.github.com/graalvm/native-build-tools/pull/1021)
>
> **Full Changelog**: <graalvm/native-build-tools@1.1.8...1.1.9>
>
> 1.1.8
> -----
>
> What's Changed
> --------------
>
> * Release 1.1.7 by [`@​graalvmbot`](https://github.com/graalvmbot) in [graalvm/native-build-tools#1009](https://redirect.github.com/graalvm/native-build-tools/pull/1009)
> * Bump version to 1.1.8-SNAPSHOT by [`@​graalvmbot`](https://github.com/graalvmbot) in [graalvm/native-build-tools#1010](https://redirect.github.com/graalvm/native-build-tools/pull/1010)
> * Deprecate fallback plugin options by [`@​jormundur00`](https://github.com/jormundur00) in [graalvm/native-build-tools#997](https://redirect.github.com/graalvm/native-build-tools/pull/997)
> * Update reachability metadata to 1.0.9 by [`@​graalvmbot`](https://github.com/graalvmbot) in [graalvm/native-build-tools#1014](https://redirect.github.com/graalvm/native-build-tools/pull/1014)
>
> **Full Changelog**: <graalvm/native-build-tools@1.1.7...1.1.8>
>
> 1.1.7
> -----
>
> What's Changed
> --------------
>
> * Release 1.1.6 by [`@​graalvmbot`](https://github.com/graalvmbot) in [graalvm/native-build-tools#996](https://redirect.github.com/graalvm/native-build-tools/pull/996)
> * Suppress missing Javadoc diagnostics across convention tasks by [`@​jormundur00`](https://github.com/jormundur00) in [graalvm/native-build-tools#994](https://redirect.github.com/graalvm/native-build-tools/pull/994)
> * Replace deprecated Kotlin DSL delegated accessors by [`@​jormundur00`](https://github.com/jormundur00) in [graalvm/native-build-tools#998](https://redirect.github.com/graalvm/native-build-tools/pull/998)
> * Fix Maven plugin unit-test mock maker and SLF4J setup by [`@​jormundur00`](https://github.com/jormundur00) in [graalvm/native-build-tools#999](https://redirect.github.com/graalvm/native-build-tools/pull/999)
> * Bump version to 1.1.7-SNAPSHOT by [`@​graalvmbot`](https://github.com/graalvmbot) in [graalvm/native-build-tools#1001](https://redirect.github.com/graalvm/native-build-tools/pull/1001)
> * Bump io.netty:netty-codec-http from 4.1.135.Final to 4.1.136.Final in /samples/metadata-repo-integration by [`@​dependabot`](https://github.com/dependabot)[bot] in [graalvm/native-build-tools#1002](https://redirect.github.com/graalvm/native-build-tools/pull/1002)
> * Avoid Maven internal API for console colors by [`@​jormundur00`](https://github.com/jormundur00) in [graalvm/native-build-tools#1003](https://redirect.github.com/graalvm/native-build-tools/pull/1003)
> * Update reachability metadata to 1.0.8 by [`@​graalvmbot`](https://github.com/graalvmbot) in [graalvm/native-build-tools#1008](https://redirect.github.com/graalvm/native-build-tools/pull/1008)
>
> **Full Changelog**: <graalvm/native-build-tools@1.1.6...1.1.7>


Commits

* [`70a22ad`](graalvm/native-build-tools@70a22ad) Release 1.1.9
* [`226c2f2`](graalvm/native-build-tools@226c2f2) Merge pull request [#1021](https://redirect.github.com/graalvm/native-build-tools/issues/1021) from graalvm/update-metadata-to-1.0.10
* [`750e24d`](graalvm/native-build-tools@750e24d) Update reachability metadata to 1.0.10
* [`f9d090c`](graalvm/native-build-tools@f9d090c) Clarify layer selector and test suite support
* [`8a62507`](graalvm/native-build-tools@8a62507) Skip later Gradle layer consumers on GraalVM 25.0
* [`fa9cb64`](graalvm/native-build-tools@fa9cb64) Narrow GraalVM 25.0 layer test skips
* [`a7f0ca4`](graalvm/native-build-tools@a7f0ca4) Skip Gradle layer consumers on GraalVM 25.0
* [`cf67f05`](graalvm/native-build-tools@cf67f05) Skip Maven shared layer test on GraalVM 25.0
* [`60da37c`](graalvm/native-build-tools@60da37c) Skip Maven all-selector layer test on GraalVM 25.0
* [`0498739`](graalvm/native-build-tools@0498739) Fix layered image runtime distribution
* Additional commits viewable in [compare view](graalvm/native-build-tools@1.1.6...1.1.9)
  
[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility\_score?dependency-name=org.graalvm.buildtools:native-maven-plugin&package-manager=maven&previous-version=1.1.6&new-version=1.1.9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
  
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot show  ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

OCA Verified All contributors have signed the Oracle Contributor Agreement. rhei

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Proposal: Uplift the layers configuration model (typed model, shared selection, Gradle+Maven parity)

3 participants