Files
Elie GambacheandClaude Opus 4.8 215b31b85d feat(graalvm): run AWT/Compose native images on Oracle GraalVM (macOS) (#347)
* feat(graalvm): run AWT/Compose native images on Oracle GraalVM (macOS)

Oracle GraalVM's mainline SubstrateVM — unlike BellSoft Liberica NIK — lacks
the AWT startup wiring needed to run Compose Desktop apps that touch AWT
(fonts, Java2D). This adds the macOS pieces so images built with Oracle
GraalVM behave like Liberica ones. All changes are additive and vendor-
agnostic (harmless under Liberica).

- Platform (JNU) encoding: Oracle's SVM never calls the JDK's
  InitializeEncoding, so the bundled libjava.dylib's fastEncoding stays
  uninitialized and the first AWT JNI_OnLoad aborts with "platform encoding
  not initialized" / "Could not allocate library name". PlatformEncodingInitializer
  calls it via a C shim (nucleus_init_platform_encoding, compiled into the
  image with the cursor stub) that dlopens the SAME bundled libjava.dylib dyld
  shares by path with libawt/libfontmanager. macOS-only, guarded accordingly.

- fontconfig: SunFontManager reads <java.home>/lib/fontconfig.bfc at startup;
  bundle it so FontConfiguration.getVersion() no longer throws "Fontconfig head
  is null" on Font.createFont / BufferedImage.createGraphics.

- L3 macOS reachability metadata: register the freetype font scaler
  (sun.font.FreetypeFontScaler + the createFont path) and the CGL/OpenGL
  Java2D pipeline (OGLSurfaceData, CGLSurfaceData, CGraphicsEnvironment, ...)
  that the macOS native graphics code resolves via JNI FindClass — the agent
  cannot capture these native FindClass calls.

Validated on macOS aarch64 (Oracle GraalVM 25.1.3) with cmp-demo, compose-demo,
nucleus-demo and jewel-demo. Opt in per app via jvmVendor = JvmVendorSpec.ORACLE.
Windows/Linux Oracle support (same encoding wiring) is not yet ported.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(graalvm): add allCharsets toggle for embedding all JDK charsets (#348)

native-image ships only a minimal charset set (US-ASCII, ISO-8859-1, UTF-8,
UTF-16 + platform default) by default; any other charset requested via
Charset.forName(...) throws UnsupportedCharsetException at runtime. Apps that
decode bytes in a legacy encoding (e.g. windows-1255, ISO-8859-8, Shift_JIS)
previously had to pass the raw -H:+AddAllCharsets in buildArgs.

Expose it as a named DSL toggle mirroring optimizeForSize:

    graalvm { allCharsets = true }

Off by default to match GraalVM's own default (no size regression). Note this
is unrelated to displaying/typing non-Latin text, which is Unicode-internal.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(graalvm): keep nucleus_init_platform_encoding symbol out of non-macOS images

The @CFunction("nucleus_init_platform_encoding") binding was reachable on all
platforms, so SVM emitted its external symbol into every image. The C shim that
defines it is compiled only on macOS, breaking the Linux/Windows link with
"undefined reference to nucleus_init_platform_encoding".

Move the @CFunction into a @Platforms(DARWIN) class so it is excluded from
non-macOS images, and guard the call with the build-time Platform.includedIn
fold. A runtime platform check does not help: SVM registers the @CFunction
symbol per reachable declaring class, not per call site.

Also switch nucleus-demo/jewel-demo to Oracle GraalVM and the optimizeForSize DSL.

* feat(examples): migrate cmp-demo and compose-demo to Oracle GraalVM

- Wrap cmp-demo App composable in MaterialTheme + Surface
- Switch compose-demo JVM vendor from BellSoft to Oracle
- Replace manual nativeDistributions/nativeImage config with simplified DSL using TargetFormat and CompressionLevel

* fix(examples): make cmp-demo title text white for readability

* feat(plugin-build): copy fontconfig.bfc for proper AWT font initialization in GraalVM native apps

Adds a task to ensure `fontconfig.bfc` is copied to the `lib/` directory under the native image output. Prevents "Fontconfig head is null" errors during AWT font operations. Updates the `graalvmNative` task dependencies accordingly.

* refactor(build.bat): improve JDK and MSVC validation, streamline build logic

- Added validation for JNI headers in JAVA_HOME.
- Enhanced MSVC toolchain location via `vswhere` with fallback paths, reducing manual setup.
- Consolidated source and library dependencies into variables for better maintainability.
- Improved isolation of `vcvarsall` environments to avoid PATH overflow in CI systems.
- Simplified handling of ARM64 builds with clear comments.

* use consistent GraalVM settings across demos: enable `optimizeForSize`, switch `jvmVendor` to Oracle

* update(setup-nucleus): switch from Liberica NIK to Oracle GraalVM, default Java version to 25.1

- Updated action.yml to use Oracle GraalVM for AWT and `native-image` support.
- Adjusted graalvm-related inputs and descriptions, aligning them with Oracle GraalVM.
- Updated CI configuration to install Oracle GraalVM via `graalvm/setup-graalvm`.

* update(setup-nucleus): refine GraalVM version

* feat(examples): add reachability metadata for Compose UI Skia layer in system-info-demo

* ci(graalvm): install Oracle GraalVM 25.1 via setup-graalvm version input

Builds request jvmVendor = ORACLE, but CI installed Liberica NIK, so Gradle
auto-provisioned a plain Oracle JDK without native-image. Use setup-graalvm
with distribution 'graalvm' and version '25.1'; fall back to 25.0 LTS on Intel
macOS, which has no 25.1 build.

* feat(plugin-build): add `mlProfileInference` to GraalVM settings

Introduced `mlProfileInference` configuration to align with Oracle GraalVM's machine-learning-inferred PGO profiling. Added the ability to disable it by appending `-H:-MLProfileInference` to build arguments when opted out.

* feat(build): replace `optimizeForSize` with `optimization` for fine-grained native-image control

Replaced the `optimizeForSize` flag with the more flexible `optimization` setting across examples and plugin configuration. Added support for advanced GraalVM native-image optimization levels, such as `SIZE` and `LEVEL_3`. Expanded target formats to include `.rpm` in the `cmp-demo`.

* fix(window-tao): keep VSync on during resize to stop native-image leak

Dropping VSync (eglSwapInterval(0)) for the OS modal resize/move loop let
WM_SIZE render at ~1 kHz: every new window size minted a fresh
BackendRenderTarget + Surface + Skia stencil/scratch, reclaimed only lazily
by the GrResourceCache purge and the skiko Cleaner after a GC. Under
native-image's Serial GC that reclamation never caught up for lean apps
(cmp-demo, system-info-demo), so RSS climbed past 1 GB and stayed there
on Windows only — macOS (CVDisplayLink) and Linux (EGL swap thread) never
leaked because their resize paints are display-rate paced, and HotSpot
collects old gen automatically.

Keep VSync on throughout so the per-WM_SIZE present paces off the display
refresh, matching the non-leaking platforms. Logs confirmed a border drag
now renders at <=90 Hz (WM_SIZE-arrival-bound) instead of ~1 kHz. Also
bound the DirectContext GPU resource cache (256 MB) and purge it on drag
end, and defer the size push into the next paint so surface resize +
present stay atomic.

* feat(plugin-build, window-tao): add advanced GraalVM optimizations and cross-platform view factories

- Introduced `NativeImageOptimization` enum for fine-grained GraalVM `native-image` optimization control.
- Added `NucleusPlatformViewFactory` with cross-platform support for macOS, Windows, and Linux platform views.

* update(system-info-demo): bump Lets-Plot dependencies and adjust native image optimization to SIZE

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 01:14:11 +03:00
..