The Gradle plugin for GraalVM Native Image adds support for building and testing native images using the Gradle build tool.
Find the differences between the versions in the project changelog.
You can find sample applications in the source repository.
Adding the Plugin
Refer to the Getting Started Gradle Guide which provides step-by-step directions on adding the Gradle plugin to your project, building your first native image, and running it.
The plugin requires that you install a GraalVM JDK. |
The plugin is enabled by adding its declaration to the build.gradle / build.gradle.kts file within the plugins
block:
id 'org.graalvm.buildtools.native' version '0.10.5'
id("org.graalvm.buildtools.native") version "0.10.5"
This plugin supplements and heavily relies on regular Java plugins such as application
, java-library
, java
, and others. Not having them included in your project will most probably cause errors.
You can use development versions of the plugin by adding the snapshot repository instead. Pre-releases are provided for convenience, without any guarantee.
pluginManagement {
repositories {
maven {
url "https://raw.githubusercontent.com/graalvm/native-build-tools/snapshots"
}
gradlePluginPortal()
}
}
pluginManagement {
repositories {
maven {
url = uri("https://raw.githubusercontent.com/graalvm/native-build-tools/snapshots")
}
gradlePluginPortal()
}
}
Using Gradle Toolchains
Check out which Java versions are compatible with the Gradle version you are using.
If the Gradle version you are using is not compatible with the GraalVM version,
you can set up the |
Enabling Toolchain Detection
Toolchain support has many pitfalls. Unless you have a single JDK installed on your machine, which is the GraalVM version that you want to use, we do not recommend enabling them yet. We are working with the Gradle team on improvements in the future. |
Instead of relying on the JDK which is used to run Gradle, you can use the Gradle toolchain support to select a specific GraalVM installation.
However, because of limitations in Gradle, the plugin may not be able to properly detect the toolchain. In particular, this will only work properly if you only have GraalVM JDKs installed on the machine. Otherwise, Gradle will not be able to reliably detect GraalVM JDKs, nor detect GraalVM distributions from different vendors.
Should you still want to enable toolchain support, you do it via the graalvmNative
extension:
graalvmNative {
toolchainDetection = true
}
graalvmNative {
toolchainDetection.set(true)
}
Selecting the GraalVM Toolchain
By default, the plugin will select a Java 11 GraalVM toolchain using the vendor string GraalVM
, which works properly for GraalVM up to version 22.3 included.
More recent versions of GraalVM do not have a specific version and are aligned with the language version they support.
If you want to use a different toolchain, for example, a distribution compatible with Java 20 from Oracle, you can configure the toolchain like this:
graalvmNative {
binaries {
main {
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(20)
vendor = JvmVendorSpec.matching("Oracle Corporation")
}
}
}
}
graalvmNative {
binaries {
named("main") {
javaLauncher.set(javaToolchains.launcherFor {
languageVersion.set(JavaLanguageVersion.of(20))
vendor.set(JvmVendorSpec.matching("Oracle Corporation"))
})
}
}
}
Again, be aware that the toolchain detection cannot distinguish between GraalVM JDKs and standard JDKs without Native Image support. If you have both installed on the machine, Gradle may randomly pick one or the other.
Plugin Configuration
The plugin works with the application
plugin and registers a number of tasks and extensions for you to configure.
Available Tasks
The main tasks that you may want to execute are:
-
nativeCompile
triggers the generation of a native executable of your application -
nativeRun
executes the generated native executable -
nativeTestCompile
builds a native image with tests found in thetest
source set -
nativeTest
executes tests found in thetest
source set in native mode
Those tasks are configured with reasonable defaults using the graalvmNative
extension binaries
container of type NativeImageOptions.
The main executable is configured by the image named main
, while the test executable is configured via the image named test
.
Native Image Configuration
The NativeImageOptions allows you to tweak how the native image is going to be built. The plugin allows configuring the final binary, the tests one, as well as apply options to both.
graalvmNative {
binaries.main {
// options for the main binary
}
binaries.test {
// options for the test binary
}
binaries.all {
// common options for both main and test binaries
}
}
The following configuration options are available for building native images:
graalvmNative {
binaries {
main {
imageName = 'application'
mainClass = 'org.test.Main'
debug = true
verbose = true
fallback = true
sharedLibrary = false
quickBuild = false
richOutput = false
systemProperties = [name1: 'value1', name2: 'value2']
configurationFileDirectories.from(file('src/my-config'))
excludeConfig.put("org.example.test:artifact:version", ["^/META-INF/native-image/.*", "^/config/.*"])
excludeConfig.put(file("path/to/artifact.jar"), listOf("^/META-INF/native-image/.*", "^/config/.*"))
// Advanced options
buildArgs.add('--link-at-build-time')
jvmArgs.add('flag')
// Runtime options
runtimeArgs.add('--help')
useFatJar = true
}
}
agent {
defaultMode = "standard"
enabled = true
modes {
standard {
}
conditional {
userCodeFilterPath = "path-to-filter.json"
extraFilterPath = "path-to-another-filter.json" // Optional
}
direct {
options.add("config-output-dir={output_dir}")
options.add("experimental-configuration-with-origins")
}
}
callerFilterFiles.from("filter.json")
accessFilterFiles.from("filter.json")
builtinCallerFilter = true
builtinHeuristicFilter = true
enableExperimentalPredefinedClasses = false
enableExperimentalUnsafeAllocationTracing = false
trackReflectionMetadata = true
metadataCopy {
inputTaskNames.add("test")
outputDirectories.add("META-INF/native-image/<groupId>/<artifactId>/")
mergeWithExisting = true
}
tasksToInstrumentPredicate = t -> true
}
}
graalvmNative {
binaries {
named("main") {
imageName.set("application")
mainClass.set("org.test.Main")
debug.set(true)
verbose.set(true)
fallback.set(true)
sharedLibrary.set(false)
richOutput.set(false)
quickBuild.set(false)
systemProperties.putAll(mapOf("name1" to "value1", "name2" to "value2"))
configurationFileDirectories.from(file("src/my-config"))
excludeConfig.put("org.example.test:artifact:version", listOf("^/META-INF/native-image/.*", "^/config/.*"))
excludeConfig.put(file("path/to/artifact.jar"), listOf("^/META-INF/native-image/.*", "^/config/.*"))
// Advanced options
buildArgs.add("--link-at-build-time")
jvmArgs.add("flag")
// Runtime options
runtimeArgs.add("--help")
useFatJar.set(true)
}
}
agent {
defaultMode.set("standard")
enabled.set(true)
modes {
standard {
}
conditional {
userCodeFilterPath.set("path-to-filter.json")
extraFilterPath.set("path-to-another-filter.json") // Optional
}
direct {
options.add("config-output-dir={output_dir}")
options.add("experimental-configuration-with-origins")
}
}
callerFilterFiles.from("filter.json")
accessFilterFiles.from("filter.json")
builtinCallerFilter.set(true)
builtinHeuristicFilter.set(true)
enableExperimentalPredefinedClasses.set(false)
enableExperimentalUnsafeAllocationTracing.set(false)
trackReflectionMetadata.set(true)
metadataCopy {
inputTaskNames.add("test")
outputDirectories.add("/META-INF/native-image/<groupId>/<artifactId>/")
mergeWithExisting.set(true)
}
tasksToInstrumentPredicate.set(t -> true)
}
}
Native Image options
-
imageName
- The name of the native executable, defaults to the project name -
mainClass
- The main class to use, defaults to the application.mainClass -
debug
- Determines if debug info should be generated; defaults tofalse
(alternatively, add--debug-native
to the CLI) -
verbose
- Adds verbose output (false
by default) -
fallback
- Sets the fallback mode ofnative-image
(false
by default) -
sharedLibrary
- Determines if the image is a shared library -
quickBuild
- Determines if the image is being built in quick build mode -
richOutput
- Determines whethernative-image
building should produce a rich output -
systemProperties
: Defines the system properties to use for thenative-image
tool -
configurationFileDirectories
: Adds a directory containing configuration files fornative-image
, such as reflection configuration -excludeConfig
: Excludes configuration that matches any of the specified regex patterns from the JAR of a dependency with the given coordinates -
jvmArgs
: Passes the specified arguments directly to the JVM running thenative-image
builder -
useFatJar
: Builds a fat JAR instead of passing each JAR individually
You can also pass build-time and run-time arguments:
-
buildArgs.add('<buildArg>')
: Configures the build by passing options directly tonative-image
. You can pass any Native Image build option listed here. -
runtimeArgs.add('<runtimeArg>')
: Specifies runtime arguments consumed by your application.
For options that can be set using the command line, if both DSL and command-line options are present, command-line options take precedence. |
Native Image Tracing Agent
If your project requires reflection, classpath resources, dynamic proxies, or other features that need explicit configuration, it may be helpful to first run your application or tests using the Native Image Tracing Agent.
The Native Image Gradle plugin simplifies generation of the required metadata files by injecting the agent automatically for you (this includes, but is not limited to the reflection file).
Any task that extends JavaForkOptions
(such as test
, run
, and so on) can be instrumented by passing -Pagent
to Gradle when running those tasks.
Enable the agent by adding the following block inside graalvmNative
:
agent {
enabled = true
}
agent {
enabled.set(true)
}
You can then run it:
./gradlew -Pagent run # Runs on HotSpot with the agent
The generated configuration files can be found in the ${buildDir}/native/agent-output/${taskName}
directory, for example, build/native/agent-output/run.
The plugin also substitutes {output_dir}
in the agent options to point to this directory during the agent run.
The agent can run in multiple modes that dictate how the metadata is collected and merged.
-
defaultMode
- Specifies the default agent mode if one isn’t specified using-Pagent=mode_name
-
enabled
- Enables the agent -
modes
- Configures the following agent modes:-
standard
- Generates metadata without conditions -
conditional
- Generates metadata with conditions-
userCodeFilterPath
- Path to a filter file that specifies which classes to include in metadata conditions -
extraFilterPath
- (Optional) An additional filter file to further refine the collected metadata
-
-
direct
- Allows users to directly pass options to the agent-
{output_dir}
- The output directory to store agent files
-
-
-
tasksToInstrumentPredicate
- By default, when-Pagent
is specified, all tasks extendingJavaForkOptions
are instrumented. This can be limited to only specific tasks that match this predicate.
agent {
enabled = true
defaultMode = "conditional"
modes {
conditional {
userCodeFilterPath = "src/test/native-image/filters/user-code-filter.json"
}
direct {
options.add("config-output-dir=src/test/resources/direct-mode-metadata")
options.add("experimental-configuration-with-origins")
}
}
}
agent {
enabled.set(true)
defaultMode.set("conditional")
modes {
conditional {
userCodeFilterPath.set("src/test/native-image/filters/user-code-filter.json")
}
direct {
options.add("config-output-dir=src/test/resources/direct-mode-metadata")
options.add("experimental-configuration-with-origins")
}
}
}
If you want to enable the agent on the command line, you can specify in which mode you want to run it. For example:
./gradlew -Pagent=standard nativeTest
./gradlew -Pagent=conditional nativeTest
./gradlew -Pagent=direct nativeTest
Resources Autodetecting
You can instruct the plugin to automatically detect resources to be included in a native executable at build time: Add this to your build.gradle file:
graalvmNative {
binaries.all {
resources.autodetect()
}
}
MetadataCopy Task
Once the metadata is collected, it can be copied into the project using the metadataCopy
task.
To do so, add the following block inside your agent
block:
agent {
enabled = true
metadataCopy {
inputTaskNames.add("test")
outputDirectories.add("src/test/resources/META-INF/native-image/org.example")
mergeWithExisting = false
}
}
agent {
enabled.set(true)
metadataCopy {
inputTaskNames.add("test")
outputDirectories.add("resources/META-INF/native-image/org.example")
mergeWithExisting.set(false)
}
}
Inside this block, you configure:
-
inputTaskNames
- specifies tasks previously executed with the agent attached (tasks that generated metadata in the last step). -
outputDirectories
- specifies the location where you want to copy the generated metadata -
mergeWithExisting
- specifies whether the metadata you want to copy, should be merged with the metadata that already exists on the give location, or not. This only makes sense when there is already some existing metadata, created before.
Then you can copy metadata to the location you specified with:
./gradlew metadataCopy
You can configure the
|
Common Agent Options
All the mentioned modes share certain common configuration options like:
-
callerFilterFiles
-
accessFilterFiles
-
builtinCallerFilter
-
builtinHeuristicFilter
-
enableExperimentalPredefinedClasses
-
enableExperimentalUnsafeAllocationTracing
-
trackReflectionMetadata
These options are for advanced usages. You can read more about them here. |
A complete example of the agent block should look like this:
agent {
defaultMode = "standard"
enabled = true
modes {
conditional {
userCodeFilterPath = "path-to-filter.json"
extraFilterPath = "path-to-another-filter.json"
}
direct {
options.add("config-output-dir={output_dir}")
options.add("experimental-configuration-with-origins")
}
}
callerFilterFiles.from("filter.json")
accessFilterFiles.from("filter.json")
builtinCallerFilter = true
builtinHeuristicFilter = true
enableExperimentalPredefinedClasses = false
enableExperimentalUnsafeAllocationTracing = false
trackReflectionMetadata = true
metadataCopy {
inputTaskNames.add("test")
outputDirectories.add("src/main/resources/META-INF/native-image/<groupId>/<artifactId>/")
mergeWithExisting = true
}
}
agent {
defaultMode.set("standard")
enabled.set(true)
modes {
conditional {
userCodeFilterPath.set("path-to-filter.json")
extraFilterPath.set("path-to-another-filter.json")
}
direct {
options.add("config-output-dir={output_dir}")
options.add("experimental-configuration-with-origins")
}
}
callerFilterFiles.from("filter.json")
accessFilterFiles.from("filter.json")
builtinCallerFilter.set(true)
builtinHeuristicFilter.set(true)
enableExperimentalPredefinedClasses.set(false)
enableExperimentalUnsafeAllocationTracing.set(false)
trackReflectionMetadata.set(true)
metadataCopy {
inputTaskNames.add("test")
outputDirectories.add("src/main/resources/META-INF/native-image/<groupId>/<artifactId>/")
mergeWithExisting.set(true)
}
}
Reduce the Amount of Generated Metadata
In some cases the agent may include more metadata than it is actually needed. You can filter metadata using the agent filter files. These filter files that agent consumes have the following structure:
{
"rules": [
{"includeClasses": "some.class.to.include.**"},
{"excludeClasses": "some.class.to.exclude.**"},
],
"regexRules": [
{"includeClasses": "regex\.example\.class.*"},
{"excludeClasses": "regex\.example\.exclude[0-9]+"},
]
}
The process how you can pass the config files to the agent is described in the previous section.
You can see on some simple Java application how different filter files affect generated metadata.
Let’s start with this filter:
{
"rules": [
{"includeClasses": "**"}
]
}
This filter instructs the agent to include everything, which will result in a massive configuration file. For example, this is how reachability-metadata.json looks like:
{
{
"reflection": [
{
"condition": {
"typeReached": "java.io.ObjectInputStream"
},
"type": "[Ljava.lang.Object;"
},
{
"condition": {
"typeReached": "java.io.ObjectInputStream"
},
"type": "java.util.LinkedHashSet"
},
{
"condition": {
"typeReached": "org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor"
},
"type": "org.example.NativeTests"
},
{
"condition": {
"typeReached": "org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor"
},
"type": "org.example.NativeTests",
"allDeclaredFields": true
},
{
"condition": {
"typeReached": "org.junit.jupiter.engine.descriptor.ExtensionUtils"
},
"type": "org.example.NativeTests"
},
...
],
"resources": [
{
"condition": {
"typeReached": "org.junit.platform.launcher.core.LauncherFactory"
},
"glob": "META-INF/services/org.junit.platform.engine.TestEngine"
},
{
"condition": {
"typeReached": "java.lang.ClassLoader"
},
"glob": "TestResource.txt"
},
...
],
"bundles": [],
"jni": [
{
"condition": {
"typeReached": "java.net.InetAddress"
},
"type": "java.lang.Boolean",
"methods": [
{
"name": "getBoolean",
"parameterTypes": [
"java.lang.String"
]
}
]
}
]
}
As you can see, there are lots of entries that you likely do not want. They are present because the metadata was generated using tests, and that the testing library was present at run time during the agent execution pass, but in practice, your application will not use the testing library in production. To reduce the amount of generated metadata, use the following user-code-filter.json:
{
"rules": [
{"includeClasses": "**"},
{"excludeClasses": "org.junit.**"},
{"excludeClasses": "org.gradle.**"},
{"excludeClasses": "worker.org.gradle.**"},
{"excludeClasses": "org.slf4j.**"},
{"excludeClasses": "java.**"}
]
}
Always be careful when removing entries in metadata, as this may result in a broken native image. |
After updating the filter, you can regenerate the metadata, which will result in the following reachability-metadata.json file:
{
"reflection": [
{
"condition": {
"typeReached": "org.example.NativeTests"
},
"type": "org.example.NativeTests$Person",
"allDeclaredFields": true
},
{
"condition": {
"typeReached": "sun.security.jca.GetInstance"
},
"type": "sun.security.provider.SHA",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
}
],
"resources": [
{
"condition": {
"typeReached": "org.example.NativeTests"
},
"glob": "TestResource.txt"
}
],
"bundles": []
}
As you can see there are no more entries that contain classes from org.junit
(as their condition).
Max Parallel Builds
When using Gradle parallel builds, the plugin automatically limits the number of native images which can be built concurrently, in order to limit CPU and memory usage.
By default, it is limited to the number of CPU cores / 16, but you can change this limit either by setting the org.graalvm.buildtools.max.parallel.builds
gradle property (in your gradle.properties file), or by setting the GRAALVM_BUILDTOOLS_MAX_PARALLEL_BUILDS
environment variable.
Long classpath, @argument File, and a Fat JAR Support
The plugin automatically passes arguments to the native-image
tool from the argument file, which should prevent all long classpath issues under Windows.
However, if you are using an older GraalVM release (older than 21.3) which doesn’t support argument files, you will need to rely on creating a "fat JAR", which includes all entries from the classpath automatically, to workaround the problem:
graalvmNative {
useArgFile = false // required for older GraalVM releases
binaries {
main {
useFatJar = true
}
}
}
graalvmNative {
useFatJar.set(false) // required for older GraalVM releases
binaries {
named("main") {
useFatJar.set(true)
}
}
}
Alternatively, it is possible to use your own JAR file (for example, created using the Shadow plugin) by setting the classpathJar
property directly on the task:
tasks.named("nativeCompile") {
classpathJar = myFatJar
}
tasks.named<BuildNativeImageTask>("nativeCompile") {
classpathJar.set(myFatJar.flatMap { it.archiveFile })
}
When the classpathJar
property is set, the classpath
property is ignored.
Testing Support
The plugin supports running tests on the JUnit Platform as native images. This means that tests are compiled and executed as native code.
The minimum supported version is JUnit 5.8.1 (JUnit Platform 1.8.1, JUnit Jupiter 5.8.1, JUnit Vintage 5.8.1). |
In theory, any TestEngine
supported on the JUnit Platform should be supported by the plugin as long as the programming language used by the TestEngine
and the programming language used to write the tests is supported by GraalVM Native Image.
The plugin provides explicit support for the JUnit Jupiter and JUnit Vintage test engines, and support for additional test engines should be possible with custom native configuration.
Currently, this feature requires the execution of the tests on the JVM prior to the execution of tests as native code. To execute the tests, run:
./gradlew nativeTest
Configuring Test Image Options
You can fine-tune the test binary using the test
binary configuration.
The following example prints additional data for troubleshooting and sets the minimal optimizations.
graalvmNative {
binaries {
test {
buildArgs.addAll('--verbose', '-O0')
}
}
}
graalvmNative {
binaries {
named("main") {
mainClass.set("org.test.Main")
}
named("test") {
buildArgs.addAll("--verbose", "-O0")
}
}
}
Disabling Testing Support
There are cases where you might want to disable running native tests:
-
You don’t actually want to run your tests as native code.
-
Your library or application uses a testing framework that is not supported on the JUnit Platform.
-
You need to use the agent when running tests on the JVM, but do not wish to run the same tests as native code.
In this case, you can disable native testing support by configuring the graalvmNative
option as follows:
graalvmNative {
testSupport = false
}
graalvmNative {
testSupport.set(false)
}
Configuring Additional Test Suites
It is common to have multiple test source sets in a Gradle build. Typically, you may have an integration test suite, or a functional test suite, in addition to the unit test suite. The plugin supports running those tests as native binaries too.
For example, imagine that you have a source set named integTest
and that its corresponding test task is named integTest
.
In this case you can register a new native test binary with graalvmNative
:
graalvmNative {
registerTestBinary("integTest") {
usingSourceSet(sourceSets.integTest)
forTestTask(integTest)
}
}
graalvmNative {
registerTestBinary("integTest") {
usingSourceSet(sourceSets.getByName("integTest"))
forTestTask(tasks.named<Test>("integTest"))
}
}
The plugin then automatically creates the following tasks:
-
nativeIntegTestCompile
to compile a native image using theintegTest
source set -
nativeIntegTest
to execute the tests as native code
The same mechanism can be used if you have multiple test tasks for a single test source set, which is often the case with manual test sharding.
GraalVM Reachability Metadata Support
The plugin adds support for the GraalVM Reachability Metadata Repository. This repository provides the configuration for libraries that do not support GraalVM Native Image.
The GraalVM Reachability Metadata Repository is also published on Maven Central at the following coordinates: |
Configuring the Metadata Repository
The plugin automatically downloads the configuration metadata from the official repository if you supply the version of the repository you want to use.
The support is enabled by default, but can be disabled explicitly:
graalvmNative {
metadataRepository {
enabled = false
}
}
graalvmNative {
metadataRepository {
enabled.set(false)
}
}
To override the repository version, use:
graalvmNative {
metadataRepository {
version = "0.1.0"
}
}
graalvmNative {
metadataRepository {
version.set("0.1.0")
}
}
Alternatively, it is possible to use a local repository, in which case you can specify the path to the repository:
graalvmNative {
metadataRepository {
uri(file("metadata-repository"))
}
}
graalvmNative {
metadataRepository {
uri(file("metadata-repository"))
}
}
For each library dependency, the plugin automatically searches for the configuration metadata in the repository. In some cases, you may need to exclude a particular module from the search. This can be done by adding it to the exclude list:
graalvmNative {
metadataRepository {
// Exclude this library from automatic metadata
// repository search
excludes.add("com.company:some-library")
}
}
graalvmNative {
metadataRepository {
// Exclude this library from automatic metadata
// repository search
excludes.add("com.company:some-library")
}
}
Lastly, it is possible for you to override the metadata version of a particular module. This may be interesting if there is no specific metadata available for the particular version of the library that you use, but that you know that a version works:
graalvmNative {
metadataRepository {
// Force the version of the metadata for a particular library
moduleToConfigVersion.put("com.company:some-library", "3")
}
}
graalvmNative {
metadataRepository {
// Force the version of the metadata for a particular library
moduleToConfigVersion.put("com.company:some-library", "3")
}
}
Including Metadata Repository Files
By default, reachability metadata is used only when your native image is being generated. In some situations, you may want a copy of the reachability metadata to use it directly.
Copying the reachability metadata into the application JAR can be useful when some other process is responsible for converting your JAR into a native image. For example, you might be generating a shaded JAR file and using a Paketo buildpack to convert it to a native image.
To download a copy of the metadata into the build/native-reachability-metadata directory, you can the collectReachabilityMetadata
task.
Files will be downloaded into META-INF/native-image/<groupId>/<versionId>
subdirectories.
To include metadata repository inside the JAR file, you can link to the task using the jar
DSL from
directive:
tasks.named("jar") {
from collectReachabilityMetadata
}
tasks.named("jar", Jar) {
from(collectReachabilityMetadata)
}
For more advanced configurations you can declare the org.graalvm.buildtools.gradle.tasks.CollectReachabilityMetadata
task and set the appropriate properties.
Profile-Guided Optimization
The plugin supports building optimized images with Profile-Guided Optimization (PGO) to improve performance and throughput.
PGO is available in Oracle GraalVM. |
The PGO workflow includes three steps:
-
First, generate a native image with instrumentation enabled.
-
Next, run the image to gather profiling information.
-
Then, create an optimized native image based on the profiles.
To generate a binary with instrumentation enabled, you should run the nativeCompile
command with the --pgo-instrument
command line option:
./gradlew nativeCompile --pgo-instrument
This generates a native image under build/native/nativeCompile with the -instrumented
suffix.
You can run this image to gather profiling data:
./myApp-instrumented
A default.iprof file will be generated once the application is stopped. Alternatively, you can have Gradle both generate and run the instrumented binary in a single command by running:
./gradlew nativeCompile --pgo-instrument nativeRun
In this case, the profile will automatically be stored under build/native/nativeCompile.
The last phase consists in copying the generated profile, so that it is automatically used when building an optimized native image. The conventional location for profiles is src/pgo-profiles/<name of the binary>. By default, the location will be src/pgo-profiles/main. Copy default.iprof into that directory, and then run:
./gradlew nativeCompile
The profile will automatically be used. If everything was done properly, you will see "PGO: user-provided" in the native image build output.
It is possible to include more than one profile, in which case you should rename the .iprof files in the src/pgo-profiles/main directory. Learn more about PGO on the website.
Configurations Defined by the Plugin
For each binary (main
and test
), the plugin declares 2 configurations that users or plugin authors can use to tweak the native image compilation classpath:
-
nativeImageCompileOnly
(for themain
binary) andnativeImageTestCompileOnly
(for thetest
binary) can be used to declare dependencies which are only needed during the ahead-of-time compilation. -
nativeImageClasspath
(for themain
binary) andnativeImageTestClasspath
(for thetest
binary) are the configurations which are resolved to determine the image classpath.
The native image "compile only" configurations can typically be used to declare dependencies which are only required when building a native binary, and therefore should not leak when running on the JVM.
For example, you can declare a source set which uses the GraalVM SDK to implement native features. This source set would contain code which is only relevant to native images building:
sourceSets {
graal
}
dependencies {
graalCompileOnly 'org.graalvm.nativeimage:svm:21.2.0'
graalCompileOnly 'org.graalvm.sdk:graal-sdk:21.2.0'
nativeImageCompileOnly sourceSets.graal.output.classesDirs
}
configurations {
nativeImageClasspath.extendsFrom(graalImplementation)
}
val graal by sourceSets.creating
dependencies {
"graalCompileOnly"("org.graalvm.nativeimage:svm:21.2.0")
"graalCompileOnly"("org.graalvm.sdk:graal-sdk:21.2.0")
nativeImageCompileOnly(graal.output.classesDirs)
}
configurations {
nativeImageClasspath.extendsFrom(getByName("graalImplementation"))
}
Javadocs
For further reference, you can review the Javadocs of the plugin.