Skip to content

Verify native-image behaviour with an end-to-end native build #16176

Description

@codeconsole

Background

Grails 8 now has a native-image build path: #16094 added processAot wiring, cooperation with org.graalvm.buildtools.native, and the GenerateNativeMetadataTask / TraceNativeMetadataTask machinery, and #16102 moved i18n onto Spring Boot's MessageSource with an I18nRuntimeHintsProcessor that registers resource hints for the effective spring.messages.basename.

What neither PR established is a build that actually compiles a native binary and runs it. Today:

  • nothing in the repository invokes nativeCompile, nativeRun or nativeTest;
  • no CI workflow provisions GraalVM or mentions native-image;
  • grails-test-examples/aot verifies processAot output and a JVM startup check, both of which run on a normal JDK.

So every native-image claim in the framework currently rests on JVM-level AOT processing plus reasoning about the metadata, rather than on a binary that was built and exercised. That is a reasonable place to have landed the feature work, but it leaves a gap worth closing deliberately.

Proposal

Add a native-image verification target — most likely under end-to-end/, since it already consumes published artifacts — that builds a real GraalVM binary from a Grails application and asserts runtime behaviour that only a native image can falsify.

Suggested first assertions, ordered by how likely they are to catch something:

  1. Plugin message bundles resolve. A namespaced plugin bundle (for example spring-security-core_fr.properties) resolving in the binary. This is the case Spring Boot's own MessageSourceRuntimeHints does not cover — it registers two hardcoded messages* patterns and derives nothing from the configured base names — so if I18nRuntimeHintsProcessor is wrong, plugin messages resolve on the JVM and silently vanish in native.
  2. A base name the application configured itself resolves, e.g. spring.messages.basename: config/i18n/custom, covering the dot-to-slash path conversion.
  3. The i18n descriptor is readable, since base-name discovery reads META-INF/grails/i18n.properties through an exact-name ClassLoader.getResources lookup at runtime.
  4. Whatever equivalent assertions the AOT cache and native metadata work from Make Grails applications processable by Spring AOT (Leyden AOT Cache + GraalVM Native Image Support) #16094 would want.

Open question worth settling with the test

I18nRuntimeHintsProcessor uses the same hint API Spring Boot uses. Boot's MessageSourceRuntimeHints calls registerPattern (verified in spring-boot-autoconfigure 4.1.0: it references registerPattern, messages.properties and messages_*.properties, and never registerResourceBundle), and this processor does the same. The difference is coverage, not mechanism: Boot registers two hardcoded messages* patterns and derives nothing from the configured base names, whereas this one generates patterns from the effective spring.messages.basename, which is what brings namespaced plugin bundles along.

The open question is only whether the more precise hints.resources().registerResourceBundle(...) would also work. It exists and is the more obvious API for message bundles, yet Boot does not use it. The likely reason is that it serialises to a {"bundle": ...} entry, which GraalVM resolves against the image's included locale set, so non-default locales could drop out — but that is inferred from reading the serialisation code, not documented by Boot or confirmed against a binary. A native test would settle it. If registerResourceBundle turns out to be safe, the hints could become more precise.

Notes

  • This is framework-wide infrastructure, not specific to i18n: a native job would serve the AOT cache work, GORM, GSP and anything else that ships hints.
  • Cost is the main design question. A full nativeCompile is minutes of CPU and needs a GraalVM toolchain, so it probably belongs on a scheduled or opt-in workflow rather than every PR.

Raised from review discussion on #16102.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions