Verify native-image behaviour with an end-to-end native build
- Dominant language
- Groovy
- Stars
- 2.9k
- Forks
- 975
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 92
Description
### 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 #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.
Contributor guide
Research direction
Start with end-to-end/ and grails-test-examples/aot, then inspect the existing processAot coverage and nativeCompile/nativeRun/nativeTest entry points. Add a GraalVM-backed verification target and an opt-in or scheduled workflow; done means a real native binary builds and runs the listed plugin/custom i18n and descriptor assertions.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- spring-boot
- Domain
- build-system, ci-cd, testing
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100