FS-native-builds: Both plugins build native images from build-tool project state

August 10, 2026 · View on GitHub

Both product plugins must translate Gradle or Maven project state into a single native-image invocation. The user's durable configuration lives in the build file (Gradle DSL or Maven XML); one-off command-line overrides feed the same command-line assembly path so behavior does not diverge by configuration source. This contract realizes §GOAL-plugin-parity together with §FS-plugin-common, and is adapted by §gradle/FS-native-tasks and §gradle/FS-native-invocation for Gradle and §maven/FS-goal-surface.1 and §maven/FS-native-builds for Maven.

1. Required inputs

A native image build must derive the following from project state and configuration:

InputContract
Classpath and module pathDerived from selected build-tool project state.
Entry point or shared-library modeDerived from user configuration, build-tool conventions, or build-tool-specific discovery.
Image name and output locationDerived from user configuration or stable build-tool conventions.
Build argumentsCombined from durable configuration and documented command-line overrides.
JVM arguments, system properties, environment variablesPassed to the native-image driver process when configured.
Configuration file directoriesInclude generated resource config (§FS-resources-and-metadata.1), resolved repository metadata (§FS-resources-and-metadata.2), and dynamic access metadata (§FS-resources-and-metadata.4).
Optional inputsInclude classpath JAR (§GLOSS-fat-jar), argument file (§GLOSS-argument-file), layer options (§GLOSS-layered-image), and PGO options (§GLOSS-pgo) when supported by the plugin.

Gradle-specific task inputs are specified by §gradle/FS-native-tasks and §gradle/FS-native-invocation. Maven-specific goal inputs are specified by §maven/FS-goal-surface, §maven/FS-native-builds, and §maven/FS-config-model.

2. Command-line construction

Both plugins must construct the native-image command line through shared utilities from §common/FS-common-libraries.1 so escaping, quoting, and argument-file conversion stay identical. Plugin-specific string handling must not bypass those utilities.

When a build tool disables colored console output, its adapter must explicitly disable Native Image colors with the flag supported by the discovered Native Image version. When console colors are enabled, plugin-specific rich-output configuration may enable them explicitly. User-supplied build arguments retain precedence over the adapter's detected console mode.

When a user-configured option set exceeds platform argument limits, or when configuration requests it explicitly, the command line must be written as a Native Image argument file (@<path>).

3. Executable lookup

native-image must be located from the configured Java toolchain when toolchain detection is enabled, then from GRAALVM_HOME/JAVA_HOME/PATH fallbacks. Failure messages must name which lookup paths were attempted and what was found.

4. Version and schema gates

When the user configures a required Native Image version, the build must fail before invoking native-image if the discovered version is older. When repository metadata is consumed, the build must validate that metadata against the schema expected by the discovered Native Image major version before passing it to native-image (§FS-resources-and-metadata.5). These gates surface declared compatibility boundaries; they must not mask Native Image constraints or bugs that belong upstream in GraalVM (§NGOAL-graalvm-is-graalvm).

5. Shared library mode

Both plugins must support shared-library output where the build tool's packaging model allows it. Shared-library mode disables entry-point requirements and may change the output file extension. The plugin-specific configuration surface and defaults are specified by §gradle/FS-plugin-model and §maven/FS-config-model.

6. Layered images

Both plugins must support named layer creation from resolved build-tool dependencies and layer consumption through declared task or artifact relationships. Selection rendering belongs to common utilities; Gradle owns provider/task wiring and Maven owns goal/artifact/repository wiring. The two surfaces may use build-tool-native syntax but must preserve equivalent selector and multi-layer behavior. The all selector includes the complete runtime dependency graph, including artifacts produced by other projects in the same multi-project or reactor build. Empty layer selections fail before Native Image is invoked. Layer consumers retain the classpath or modulepath inputs used to create their producer layers; Gradle propagates those build-local inputs through its task graph, while Maven consumers declare equivalent dependencies. Layered executable deployment must make each layer's runtime native files discoverable by the platform loader: LD_LIBRARY_PATH on Linux, DYLD_LIBRARY_PATH on macOS, or PATH on Windows.

The .nil file is a build-time input consumed through -H:LayerUse; it is not the layer's runtime payload. Native Image also produces platform-specific runtime libraries beside the .nil, and the final image loads those files when it runs. Build-tool run and native-test tasks must configure the loader for execution from the build tree. Repository and distribution flows must carry or stage the runtime files in a consumer-owned location and must not depend on a producer build directory. Publishing or attaching only a .nil is therefore not a complete deployable repository flow.

Layer consumption is supported on GraalVM 25.1 and later. GraalVM 25.0.x consumption remains permitted but unsupported and must warn that it proceeds at the user's own risk; layer creation alone does not warn. Native Image layers remain experimental upstream (§DEC-layer-model.4). §GOAL-plugin-parity.