diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index aaa63565b..e3224d917 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,11 +7,21 @@ assignees: '' --- +**Before reporting** + +- This repository should be used to report **issues on the Maven or Gradle plugins for GraalVM**. +- Please report issues which are specific to [the Spring Framework](https://spring.io/) or [the Micronaut framework](https://micronaut.io/) to their specific repositories. +- Do not report issues with building your specific application, e.g errors which happen at image build time like classes initialized at build time, or missing classes as run time: those are not related to the plugins but problems with configuration. You can refer to the [GraalVM native image documentation](https://www.graalvm.org/latest/reference-manual/native-image/) for available options and the [plugins documentation](https://graalvm.github.io/native-build-tools) for how to use them with the plugin. + **Describe the bug** A clear and concise description of what the bug is. **Make sure that you have read [the documentation](https://graalvm.github.io/native-build-tools) and that you are using the latest plugin version.** **To Reproduce** + +When possible, provide a link to a repository which reproduces the issue, with instructions on how to use. +The reproducer **must** make use of either the Maven or Gradle plugin. + Steps to reproduce the behavior: ```xml diff --git a/build-logic/aggregator/src/main/kotlin/org.graalvm.build.aggregator.gradle.kts b/build-logic/aggregator/src/main/kotlin/org.graalvm.build.aggregator.gradle.kts index c28db8655..d15ac6360 100644 --- a/build-logic/aggregator/src/main/kotlin/org.graalvm.build.aggregator.gradle.kts +++ b/build-logic/aggregator/src/main/kotlin/org.graalvm.build.aggregator.gradle.kts @@ -74,11 +74,19 @@ tasks.named("check") { } } +tasks.register("showPublications") { + gradle.includedBuilds.forEach { + if (it.name != "docs" && !it.projectDir.path.contains("build-logic")) { + dependsOn(it.task(":showPublications")) + } + } +} + listOf( - "publishTo" to "MavenLocal", - "publishAllPublicationsTo" to "CommonRepository", - "publishAllPublicationsTo" to "SnapshotsRepository", - "publishAllPublicationsTo" to "NexusRepository", + "publishTo" to "MavenLocal", + "publishAllPublicationsTo" to "CommonRepository", + "publishAllPublicationsTo" to "SnapshotsRepository", + "publishAllPublicationsTo" to "NexusRepository", ).forEach { entry -> val (taskPrefix, repo) = entry tasks.register("$taskPrefix$repo") { @@ -123,8 +131,8 @@ tasks.register("releaseZip") { val updateSamples by tasks.registering mapOf( - "updateSamplesDir" to "samples", - "updateMavenReprosDir" to "native-maven-plugin/reproducers" + "updateSamplesDir" to "samples", + "updateMavenReprosDir" to "native-maven-plugin/reproducers" ).forEach { (taskName, dir) -> val t = tasks.register(taskName) { inputDirectory.set(layout.projectDirectory.dir(dir)) diff --git a/build-logic/common-plugins/src/main/kotlin/org.graalvm.build.publishing.gradle.kts b/build-logic/common-plugins/src/main/kotlin/org.graalvm.build.publishing.gradle.kts index 182d16891..ef318324f 100644 --- a/build-logic/common-plugins/src/main/kotlin/org.graalvm.build.publishing.gradle.kts +++ b/build-logic/common-plugins/src/main/kotlin/org.graalvm.build.publishing.gradle.kts @@ -151,8 +151,33 @@ plugins.withId("java-test-fixtures") { } } +val publicationCoordinatesCollector = gradle.sharedServices.registerIfAbsent("publicationCoordinatesCollector", PublicationCoordinatesCollector::class.java) { +} + +val showPublications by tasks.registering { + usesService(publicationCoordinatesCollector) + doLast { + publishing.publications.all { + val pub = this as MavenPublication + publicationCoordinatesCollector.get().addPublication(pub) + } + } +} + // Get a handle on the software component factory interface Services { @Inject fun getSoftwareComponentFactory(): SoftwareComponentFactory } + +abstract class PublicationCoordinatesCollector: BuildService, AutoCloseable { + val publications = mutableSetOf() + + fun addPublication(publication: MavenPublication) { + publications.add("${publication.groupId}:${publication.artifactId}:${publication.version}") + } + + override fun close() { + publications.sorted().forEach { println(it) } + } +} diff --git a/common/utils/src/main/java/org/graalvm/buildtools/agent/AgentConfiguration.java b/common/utils/src/main/java/org/graalvm/buildtools/agent/AgentConfiguration.java index 7e3dfe404..105311a43 100644 --- a/common/utils/src/main/java/org/graalvm/buildtools/agent/AgentConfiguration.java +++ b/common/utils/src/main/java/org/graalvm/buildtools/agent/AgentConfiguration.java @@ -49,21 +49,33 @@ public class AgentConfiguration implements Serializable { private final Collection callerFilterFiles; private final Collection accessFilterFiles; - private final boolean builtinCallerFilter; - private final boolean builtinHeuristicFilter; - private final boolean experimentalPredefinedClasses; - private final boolean experimentalUnsafeAllocationTracing; - private final boolean trackReflectionMetadata; + private final Boolean builtinCallerFilter; + private final Boolean builtinHeuristicFilter; + private final Boolean experimentalPredefinedClasses; + private final Boolean experimentalUnsafeAllocationTracing; + private final Boolean trackReflectionMetadata; private final AgentMode agentMode; + // This constructor should be used only to specify that we have instance of agent that is disabled (to avoid using null for agent enable check) + public AgentConfiguration(AgentMode ...modes) { + this.callerFilterFiles = null; + this.accessFilterFiles = null; + this.builtinCallerFilter = null; + this.builtinHeuristicFilter = null; + this.experimentalPredefinedClasses = null; + this.experimentalUnsafeAllocationTracing = null; + this.trackReflectionMetadata = null; + this.agentMode = modes.length == 1 ? modes[0] : new DisabledAgentMode(); + } + public AgentConfiguration(Collection callerFilterFiles, Collection accessFilterFiles, - boolean builtinCallerFilter, - boolean builtinHeuristicFilter, - boolean experimentalPredefinedClasses, - boolean experimentalUnsafeAllocationTracing, - boolean trackReflectionMetadata, + Boolean builtinCallerFilter, + Boolean builtinHeuristicFilter, + Boolean experimentalPredefinedClasses, + Boolean experimentalUnsafeAllocationTracing, + Boolean trackReflectionMetadata, AgentMode agentMode) { this.callerFilterFiles = callerFilterFiles; this.accessFilterFiles = accessFilterFiles; @@ -79,11 +91,11 @@ public List getAgentCommandLine() { List cmdLine = new ArrayList<>(agentMode.getAgentCommandLine()); appendOptionToValues("caller-filter-file=", callerFilterFiles, cmdLine); appendOptionToValues("access-filter-file=", accessFilterFiles, cmdLine); - cmdLine.add("builtin-caller-filter=" + builtinCallerFilter); - cmdLine.add("builtin-heuristic-filter=" + builtinHeuristicFilter); - cmdLine.add("experimental-class-define-support=" + experimentalPredefinedClasses); - cmdLine.add("experimental-unsafe-allocation-support=" + experimentalUnsafeAllocationTracing); - cmdLine.add("track-reflection-metadata=" + trackReflectionMetadata); + addToCmd("builtin-caller-filter=", builtinCallerFilter, cmdLine); + addToCmd("builtin-heuristic-filter=", builtinHeuristicFilter, cmdLine); + addToCmd("experimental-class-define-support=", experimentalPredefinedClasses, cmdLine); + addToCmd("experimental-unsafe-allocation-support=", experimentalUnsafeAllocationTracing, cmdLine); + addToCmd("track-reflection-metadata=", trackReflectionMetadata, cmdLine); return cmdLine; } @@ -100,10 +112,19 @@ public boolean isEnabled() { } public static void appendOptionToValues(String option, Collection values, Collection target) { - values.stream().map(value -> option + value).forEach(target::add); + if (values != null) { + values.stream().map(value -> option + value).forEach(target::add); + } } public AgentMode getAgentMode() { return agentMode; } + + private void addToCmd(String option, Boolean value, List cmdLine) { + if (value != null) { + cmdLine.add(option + value); + } + } + } diff --git a/common/utils/src/main/java/org/graalvm/buildtools/utils/NativeImageUtils.java b/common/utils/src/main/java/org/graalvm/buildtools/utils/NativeImageUtils.java index 4980e7211..ebc09de0f 100644 --- a/common/utils/src/main/java/org/graalvm/buildtools/utils/NativeImageUtils.java +++ b/common/utils/src/main/java/org/graalvm/buildtools/utils/NativeImageUtils.java @@ -59,7 +59,7 @@ public class NativeImageUtils { private static final Pattern requiredVersionPattern = Pattern.compile("^([0-9]+)(?:\\.([0-9]+)?)?(?:\\.([0-9]+)?)?$"); - private static final Pattern graalvmVersionPattern = Pattern.compile("^GraalVM ([0-9]+)\\.([0-9]+)\\.([0-9]+).*"); + private static final Pattern graalvmVersionPattern = Pattern.compile("^(GraalVM|native-image) ([0-9]+)\\.([0-9]+)\\.([0-9]+).*"); public static void maybeCreateConfigureUtilSymlink(File configureUtilFile, Path nativeImageExecutablePath) { if (!configureUtilFile.exists()) { @@ -119,6 +119,9 @@ public static String escapeArg(String arg) { * @throws IllegalStateException when the version is not correct */ public static void checkVersion(String requiredVersion, String versionToCheck) { + if (versionToCheck.startsWith("GraalVM dev") || versionToCheck.startsWith("native-image dev")) { + return; + } Matcher requiredMatcher = requiredVersionPattern.matcher(requiredVersion); if (!requiredMatcher.matches()) { throw new IllegalArgumentException("Invalid version " + requiredVersion + ", should be for example \"22\", \"22.3\" or \"22.3.0\"."); @@ -128,21 +131,27 @@ public static void checkVersion(String requiredVersion, String versionToCheck) { throw new IllegalArgumentException("Version to check '" + versionToCheck + "' can't be parsed."); } int requiredMajor = Integer.parseInt(requiredMatcher.group(1)); - int checkedMajor = Integer.parseInt(checkedMatcher.group(1)); + int checkedMajor = Integer.parseInt(checkedMatcher.group(2)); if (checkedMajor < requiredMajor) { throw new IllegalStateException("GraalVM version " + requiredMajor + " is required but " + checkedMajor + " has been detected, please upgrade."); } + if (checkedMajor > requiredMajor) { + return; + } if (requiredMatcher.group(2) != null) { int requiredMinor = Integer.parseInt(requiredMatcher.group(2)); - int checkedMinor = Integer.parseInt(checkedMatcher.group(2)); + int checkedMinor = Integer.parseInt(checkedMatcher.group(3)); if (checkedMinor < requiredMinor) { throw new IllegalStateException("GraalVM version " + requiredMajor + "." + requiredMinor + " is required but " + checkedMajor + "." + checkedMinor + " has been detected, please upgrade."); } + if (checkedMinor > requiredMinor) { + return; + } if (requiredMatcher.group(3) != null) { int requiredPatch = Integer.parseInt(requiredMatcher.group(3)); - int checkedPatch = Integer.parseInt(checkedMatcher.group(3)); + int checkedPatch = Integer.parseInt(checkedMatcher.group(4)); if (checkedPatch < requiredPatch) { throw new IllegalStateException("GraalVM version " + requiredMajor + "." + requiredMinor + "." + requiredPatch + " is required but " + checkedMajor + "." + checkedMinor + "." + checkedPatch + diff --git a/common/utils/src/test/java/org/graalvm/buildtools/utils/NativeImageUtilsTest.java b/common/utils/src/test/java/org/graalvm/buildtools/utils/NativeImageUtilsTest.java index 17eb8cceb..1292269d7 100644 --- a/common/utils/src/test/java/org/graalvm/buildtools/utils/NativeImageUtilsTest.java +++ b/common/utils/src/test/java/org/graalvm/buildtools/utils/NativeImageUtilsTest.java @@ -75,6 +75,9 @@ void checkGraalVMCEDevVersion() { NativeImageUtils.checkVersion("22", "GraalVM 22.3.0-dev Java 17 CE (Java Version 17.0.5+8-LTS)"); NativeImageUtils.checkVersion("22.3", "GraalVM 22.3.0-dev Java 17 CE (Java Version 17.0.5+8-LTS)"); NativeImageUtils.checkVersion("22.3.0", "GraalVM 22.3.0-dev Java 17 CE (Java Version 17.0.5+8-LTS)"); + NativeImageUtils.checkVersion("22.3.0", "GraalVM 23.0.0-dev Java 17.0.6+2-jvmci-23.0-b04 CE (Java Version 17.0.6+2-jvmci-23.0-b04)"); + NativeImageUtils.checkVersion("22.3.0", "GraalVM dev CE (Java Version 19+36-jvmci-23.0-b01)"); + NativeImageUtils.checkVersion("22.3.0", "native-image dev CE (Java Version 19+36-jvmci-23.0-b01)"); } @Test @@ -89,6 +92,9 @@ void checkGreaterVersion() { NativeImageUtils.checkVersion("22", "GraalVM 23.2.1"); NativeImageUtils.checkVersion("23.1", "GraalVM 23.2.1"); NativeImageUtils.checkVersion("23.2.0", "GraalVM 23.2.1"); + NativeImageUtils.checkVersion("22.3.0", "GraalVM 23.0.0"); + NativeImageUtils.checkVersion("22.2.1", "GraalVM 22.3.0"); + NativeImageUtils.checkVersion("22.2.1", "native-image 22.3.0"); } @Test diff --git a/docs/src/docs/asciidoc/graalvm-setup.adoc b/docs/src/docs/asciidoc/graalvm-setup.adoc index b08a13f91..9c218ff6a 100644 --- a/docs/src/docs/asciidoc/graalvm-setup.adoc +++ b/docs/src/docs/asciidoc/graalvm-setup.adoc @@ -1,12 +1,12 @@ -= Setting up GraalVM with native-image support += Setting up GraalVM with Native Image Support image:https://www.graalvm.org/resources/img/logo-colored.svg[GraalVM] -Working GraalVM distribution with `native-image` installable and `GRAALVM_HOME` and/or `JAVA_HOME` environment variables set, is prequisite for successful *native-image* building. +As a prerequisite for building with GraalVM Native Image, a GraalVM JDK is required and the `GRAALVM_HOME` and/or `JAVA_HOME` environment variables need to be set. -Following are the steps needed to obtain and setup GraalVM environment. +Following are the steps needed to obtain and setup a GraalVM environment. -NOTE: This is just a quick overview, and that user should consult https://www.graalvm.org/docs/getting-started/[Getting Started section] in official documentation before proceeding. +NOTE: This is just a quick overview, and users can consult the https://www.graalvm.org/docs/getting-started/[GraalVM Getting Started section] before proceeding. == 1. Obtaining distribution @@ -36,7 +36,7 @@ setx /M GRAALVM_HOME "C:\path\to\graalvm" NOTE: Preferably user would also set `JAVA_HOME` variable in the same manner (by replacing `GRAALVM_HOME` with `JAVA_HOME` in previous commands). -== 3. `native-image` tool instalation +== 3. `native-image` tool installation .Linux / macOS ```bash diff --git a/docs/src/docs/asciidoc/gradle-plugin-quickstart.adoc b/docs/src/docs/asciidoc/gradle-plugin-quickstart.adoc new file mode 100644 index 000000000..0e991e445 --- /dev/null +++ b/docs/src/docs/asciidoc/gradle-plugin-quickstart.adoc @@ -0,0 +1,469 @@ += Getting Started with Gradle Plugin for GraalVM Native Image +The GraalVM team +:highlighjsdir: {gradle-relative-srcdir}/highlight + +This guide shows how to get started with the <> and build a native executable for a Java application. + +You will create a sample application, enable the plugin, add support for dynamic features, run JUnit tests, and build a native executable. + +Two ways to build a native executable using the plugin are demonstrated: + +- <<#build-a-native-executable-with-resources-autodetection,Build a Native Executable with Resources Autodetection>> +- <<#build-a-native-executable-detecting-resources-with-the-agent,Build a Native Executable Detecting Resources with the Agent>> + +[NOTE] +==== +.Sample Application + +You start by creating a **Fortune Teller** sample application that simulates the traditional +https://en.wikipedia.org/wiki/Fortune_(Unix)[fortune Unix program]. +The data for the fortune phrases is provided by https://github.com/your-fortune[YourFortune]. +==== + +==== +To use the Native Build Tools, install GraalVM with Native Image. +The easiest way to install GraalVM with Native Image is to use the https://github.com/graalvm/graalvm-jdk-downloader[GraalVM JDK Downloader]: + +[source,bash] +---- +bash <(curl -sL https://get.graalvm.org/jdk) +---- +==== + +== Prepare a Demo Application + +. Create a new Java project with *Gradle* using the following command (alternatively, you can use your IDE to generate a project): ++ +[source,shell] +---- +gradle init --project-name fortune-parent --type java-application --package demo --test-framework junit-jupiter --dsl groovy +---- +. Rename the default `app` directory to `fortune`, edit the `settings.gradle` file to replace `app` with `fortune`, then rename the default filename `App.java` to `Fortune.java`, and replace its contents with the following: ++ +[source,java] +---- +package demo; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Random; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class Fortune { + + private static final Random RANDOM = new Random(); + private final ArrayList fortunes = new ArrayList<>(); + + public Fortune() throws JsonProcessingException { + // Scan the file into the array of fortunes + String json = readInputStream(ClassLoader.getSystemResourceAsStream("fortunes.json")); + ObjectMapper omap = new ObjectMapper(); + JsonNode root = omap.readTree(json); + JsonNode data = root.get("data"); + Iterator elements = data.elements(); + while (elements.hasNext()) { + JsonNode quote = elements.next().get("quote"); + fortunes.add(quote.asText()); + } + } + + private String readInputStream(InputStream is) { + StringBuilder out = new StringBuilder(); + try (InputStreamReader streamReader = new InputStreamReader(is, StandardCharsets.UTF_8); + BufferedReader reader = new BufferedReader(streamReader)) { + String line; + while ((line = reader.readLine()) != null) { + out.append(line); + } + + } catch (IOException e) { + Logger.getLogger(Fortune.class.getName()).log(Level.SEVERE, null, e); + } + return out.toString(); + } + + public String randomFortune() { + // Pick a random number + int r = RANDOM.nextInt(fortunes.size()); + // Use the random number to pick a random fortune + return fortunes.get(r); + } + + private void printRandomFortune() throws InterruptedException { + String f = randomFortune(); + // Print out the fortune s.l.o.w.l.y + for (char c : f.toCharArray()) { + System.out.print(c); + Thread.sleep(100); + } + System.out.println(); + } + + /** + * @param args the command line arguments + */ + public static void main(String[] args) throws InterruptedException, JsonProcessingException { + Fortune fortune = new Fortune(); + fortune.printRandomFortune(); + } +} +---- +. Delete the `fortune/src/test/java` directory, you will add tests in a later stage. +. Copy and paste the following file, +https://raw.githubusercontent.com/graalvm/graalvm-demos/master/fortune-demo/fortune/src/main/resources/fortunes.json[fortunes.json] under `fortune/src/main/resources/`. Your project tree should be: ++ +[source,shell] +---- +. +├── fortune +│ ├── build.gradle +│ └── src +│ ├── main +│ │ ├── java +│ │ │ └── demo +│ │ │ └── Fortune.java +│ │ └── resources +│ │ └── fortunes.json +│ └── test +│ └── resources +├── gradle +│ └── wrapper +│ ├── gradle-wrapper.jar +│ └── gradle-wrapper.properties +├── gradlew +├── gradlew.bat +└── settings.gradle +---- +. Open the Gradle configuration file _build.gradle_, and update the main class in the `application` section: ++ +[source,xml] +---- +application { + mainClass = 'demo.Fortune' +} +---- +. Add explicit FasterXML Jackson dependencies that provide functionality to read and write JSON, data bindings (used in the demo application). Insert the following three lines in the `dependencies` section of _build.gradle_: ++ +[source,xml] +---- +implementation 'com.fasterxml.jackson.core:jackson-core:2.13.2' +implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.2.2' +implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.2' +---- ++ +Also, remove the dependency on `guava` that will not be used. ++ +The next steps demonstrate what you should do to enable the +https://graalvm.github.io/native-build-tools/latest/gradle-plugin.html[Gradle Plugin for GraalVM Native Image]. +. Register the plugin. Add the following to +`plugins` section of your project’s _build.gradle_ file: ++ +[source,groovy,subs="verbatim,attributes", role="multi-language-sample"] +---- +plugins { + // ... + + // Apply GraalVM Native Image plugin + id 'org.graalvm.buildtools.native' version '{gradle-plugin-version}' +} +---- ++ +[source,kotlin,subs="verbatim,attributes",role="multi-language-sample"] +---- +plugins { + // ... + + // Apply GraalVM Native Image plugin + id("org.graalvm.buildtools.native") version "{gradle-plugin-version}" +} +---- ++ +The `{gradle-plugin-version}` block pulls the latest plugin version. Replace it with a specific version if you prefer. +The plugin discovers which JAR files it needs to pass to the +`native-image` builder and what the executable main class should be. +. The plugin is not yet available on the Gradle Plugin Portal, so declare an additional plugin repository. Open the _settings.gradle_ file and replace the default content with this: ++ +[source,groovy,subs="verbatim,attributes", role="multi-language-sample"] +---- +pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + } +} + +rootProject.name = 'fortune-parent' +include('fortune') +---- ++ +[source,kotlin,subs="verbatim,attributes", role="multi-language-sample"] +---- +pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + } +} + +rootProject.name = "fortune-parent" +include("fortune") +---- ++ +Note that the `pluginManagement {}` block must appear before any other statements in the file. + +[[build-a-native-executable-with-resources-autodetection]] +== Build a Native Executable with Resources Autodetection + +You can already build a native executable by running +`./gradlew nativeCompile` or run it directly by invoking +`./gradlew nativeRun`. However, at this stage, running the native executable will fail because this application requires additional metadata: you need to provide it with a list of resources to load. + +. Instruct the plugin to automatically detect resources to be included in the native executable. Add this to your `build.gradle` file: ++ +[source,groovy,subs="verbatim,attributes", role="multi-language-sample"] +---- +graalvmNative { + binaries.all { + resources.autodetect() + } + toolchainDetection = false +} +---- ++ +[source,kotlin,subs="verbatim,attributes", role="multi-language-sample"] +---- +graalvmNative { + binaries.all { + resources.autodetect() + } + toolchainDetection.set(false) +} +---- ++ +Another thing to note here: the plugin may not be able to properly detect the GraalVM installation, because of limitations in Gradle. By default, the plugin selects a Java 11 GraalVM Community Edition. If you want to use GraalVM Enterprise, or a particular version of GraalVM and Java, you need to explicitly tell this in plugin's configuration. For example: ++ +[source,groovy,subs="verbatim,attributes", role="multi-language-sample"] +---- +graalvmNative { + binaries { + main { + javaLauncher = javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(11) + vendor = JvmVendorSpec.matching("GraalVM Community") + } + } + } +} +---- ++ +[source,kotlin,subs="verbatim,attributes", role="multi-language-sample"] +---- +graalvmNative { + binaries { + main { + javaLauncher = javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(11) + vendor = JvmVendorSpec.matching("GraalVM Community") + } + } + } +} +---- ++ +The workaround to this is to disable toolchain detection with this command `toolchainDetection = false`. + +[start=2] +. Compile the project and build a native executable at one step: ++ +[source,shell] +---- +./gradlew nativeRun +---- ++ +The native executable, named _fortune_, is created in the +_/fortune/build/native/nativeCompile_ directory. +[start=3] +. Run the native executable: ++ +[source,shell] +---- +./fortune/build/native/nativeCompile/fortune +---- + +The application starts and prints a random quote. + +Configuring the `graalvmNative` plugin to automatically detect resources (`resources.autodetect()`) to be included in a binary is one way to make this example work. Using `resources.autodetect()` works because the application uses resources (_fortunes.json_) which are directly available in the `src/main/resources` location. + +In the next section, the guide shows that you can use the tracing agent to do the same. + +[[build-a-native-executable-detecting-resources-with-the-agent]] +== Build a Native Executable by Detecting Resources with the Agent + +The Native Image Gradle plugin simplifies generation of the required metadata by injecting the +https://graalvm.github.io/native-build-tools/latest/gradle-plugin.html#agent-support[ +tracing agent] automatically for you at compile time. To enable the agent, just pass the `-Pagent` option to any Gradle tasks that extends `JavaForkOptions` (for example, `test` or `run`). + +The following steps illustrate how to collect metadata using the agent, and then build a native executable using that metadata. + +. To demonstrate this approach, remove the `resources.autodetect()` block from your `build.gradle` file: ++ +[source,shell] +---- +binaries.all { + resources.autodetect() +} +---- +. Run your application with the agent enabled: ++ +[source,shell] +---- +./gradlew -Pagent run +---- +It runs your application on the JVM with the agent, collects the metadata, and generates configuration files in the _$\{buildDir}/native/agent-output/$\{taskName}_ directory. +. Copy the configuration files into the project's +`/META-INF/native-image` directory using the `metadataCopy` task: ++ +[source,shell] +---- +./gradlew metadataCopy --task run --dir src/main/resources/META-INF/native-image +---- +. Build a native executable using metadata acquired by the agent: ++ +[source,shell] +---- +./gradlew nativeCompile +---- ++ +The native executable, named _fortune_, is created in the +_build/native/nativeCompile_ directory. +. Run the native executable: ++ +[source,shell] +---- +./fortune/build/native/nativeCompile/fortune +---- ++ +The application starts and prints a random quote. + +To see the benefits of running your application as a native executable, `time` how long it takes and compare the results with running as a Java application. + +=== Plugin Customization + +You can customize the plugin. For example, change the name of the native executable and pass additional parameters to the plugin in the _build.gradle_ file, as follows: + +[source,groovy,subs="verbatim,attributes", role="multi-language-sample"] +---- +graalvmNative { + binaries { + main { + imageName.set('fortuneteller') + buildArgs.add('--verbose') + } + } +} +---- + +[source,kotlin,subs="verbatim,attributes", role="multi-language-sample"] +---- +graalvmNative { + binaries { + main { + imageName.set("fortuneteller") + buildArgs.add("--verbose") + } + } +} +---- + +The native executable then will be called `fortuneteller`. Notice how you can pass additional arguments to the `native-image` tool using the `buildArgs.add` syntax. + +== Add JUnit Testing + +The Gradle plugin for GraalVM Native Image can run +https://junit.org/junit5/docs/current/user-guide/[JUnit Platform] tests on your native executable. This means that the tests will be compiled and run as native code. + +. Create the following test in the +`fortune/src/test/java/demo/FortuneTest.java` file: ++ +.fortune/src/test/java/demo/FortuneTest.java +[source,java] +---- +package demo; + +import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class FortuneTest { + @Test + @DisplayName("Returns a fortune") + void testItWorks() throws JsonProcessingException { + Fortune fortune = new Fortune(); + assertTrue(fortune.randomFortune().length()>0); + } +} +---- + +. Run JUnit tests: +[source,shell] +---- +./gradlew nativeTest +---- + +The plugin runs tests on the JVM prior to running tests from the native executable. To disable testing support (which comes by default), add the following configuration to the _build.gradle_ file: + +[source,groovy,subs="verbatim,attributes", role="multi-language-sample"] +---- +graalvmNative { + testSupport = false +} +---- + +[source,kotlin,subs="verbatim,attributes", role="multi-language-sample"] +---- +graalvmNative { + testSupport.set(false) +} +---- + +== Run Tests with the Agent + +If you need to test collecting metadata with the agent, add the +`-Pagent` option to the `test` and `nativeTest` task invocations: + +. Run the tests on the JVM with the agent: ++ +[source,shell] +---- +./gradlew -Pagent test +---- ++ +It runs your application on the JVM with the agent, collects the metadata and uses it for testing on `native-image`. The generated configuration files (containing the metadata) can be found in the +_$\{buildDir}/native/agent-output/$\{taskName}_ directory. +In this case, the plugin also substitutes `{output_dir}` in the agent options to point to this directory. +. Build a native executable using the metadata collected by the agent: ++ +[source,shell] +---- +./gradlew -Pagent nativeTest +---- + +=== Summary + +The Gradle plugin for GraalVM Native Image adds support for building and testing native executables using the https://gradle.org[Gradle]. The plugin has many features, described in the +https://graalvm.github.io/native-build-tools/latest/gradle-plugin.html[plugin +reference documentation]. + +Note that if your application does not call any classes dynamically at run time, the execution with the agent is needless. Your workflow, in that case, is just `./gradlew nativeRun`. \ No newline at end of file diff --git a/docs/src/docs/asciidoc/gradle-plugin.adoc b/docs/src/docs/asciidoc/gradle-plugin.adoc index 089049a8f..919b64620 100644 --- a/docs/src/docs/asciidoc/gradle-plugin.adoc +++ b/docs/src/docs/asciidoc/gradle-plugin.adoc @@ -10,13 +10,27 @@ The {doctitle} adds support for building and testing native images using the htt For upgrading please take a look at the <>. +[[quickstart]] == Quickstart -[NOTE] +==== +<> +==== + ==== You can find full samples in https://github.com/graalvm/native-build-tools/tree/master/samples[the source repository]. ==== +==== +The plugin requires that you <>. +The easiest way to install GraalVM with Native Image is to use the https://github.com/graalvm/graalvm-jdk-downloader[GraalVM JDK Downloader]: +``` +bash <(curl -sL https://get.graalvm.org/jdk) +``` +==== + +== Reference documentation + === Adding the plugin Add following to `plugins` section of your project's `build.gradle` / `build.gradle.kts`: @@ -182,6 +196,14 @@ include::../snippets/gradle/kotlin/build.gradle.kts[tags=all-config-options] NOTE: For options that can be set using command-line, if both DSL and command-line options are present, command-line options take precedence. +[[max_parallel_builds]] +==== Max parallel builds + +When using Gradle parallel builds, the plugin will automatically limit the number of native images which can be built concurrently, in order to limit CPU and memory usage. +By default, it's 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 (e.g in your `gradle.properties` file) or by setting the `GRAALVM_BUILDTOOLS_MAX_PARALLEL_BUILDS` environment variable. + +[[configuration-advanced]] + [[long_classpath_and_fat_jar_support]] ==== Long classpath, @argument file and fat jar support diff --git a/docs/src/docs/asciidoc/index.adoc b/docs/src/docs/asciidoc/index.adoc index 3b3af6b64..b5c6bc095 100644 --- a/docs/src/docs/asciidoc/index.adoc +++ b/docs/src/docs/asciidoc/index.adoc @@ -19,6 +19,18 @@ If you are using alternative build systems, see <> and build a native executable for a Java application. + +You will create a sample application, enable the plugin, add support for dynamic features, run JUnit tests, and build a native executable. + +[NOTE] +==== +.Sample Application + +You start by creating a **Fortune Teller** sample application that simulates the traditional +https://en.wikipedia.org/wiki/Fortune_(Unix)[fortune Unix program]. +The data for the fortune phrases is provided by https://github.com/your-fortune[YourFortune]. +==== + +==== +The plugin requires that you <>. +The easiest way to install GraalVM with Native Image is to use the https://github.com/graalvm/graalvm-jdk-downloader[GraalVM JDK Downloader]: +``` +bash <(curl -sL https://get.graalvm.org/jdk) +``` +It will then make use of Maven profiles to enable building and testing of native executables. +==== + +== Prepare a Demo Application + +. Create a new Java project with *Maven* in your favorite IDE, called "Fortune", in the `demo` package. +Make sure to choose JUnit Jupiter as the test engine. +The application should contain a sole Java file with the following content: ++ +[source,java] +---- +package demo; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Random; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class Fortune { + + private static final Random RANDOM = new Random(); + private final ArrayList fortunes = new ArrayList<>(); + + public Fortune() throws JsonProcessingException { + // Scan the file into the array of fortunes + String json = readInputStream(ClassLoader.getSystemResourceAsStream("fortunes.json")); + ObjectMapper omap = new ObjectMapper(); + JsonNode root = omap.readTree(json); + JsonNode data = root.get("data"); + Iterator elements = data.elements(); + while (elements.hasNext()) { + JsonNode quote = elements.next().get("quote"); + fortunes.add(quote.asText()); + } + } + + private String readInputStream(InputStream is) { + StringBuilder out = new StringBuilder(); + try (InputStreamReader streamReader = new InputStreamReader(is, StandardCharsets.UTF_8); + BufferedReader reader = new BufferedReader(streamReader)) { + String line; + while ((line = reader.readLine()) != null) { + out.append(line); + } + + } catch (IOException e) { + Logger.getLogger(Fortune.class.getName()).log(Level.SEVERE, null, e); + } + return out.toString(); + } + + private void printRandomFortune() throws InterruptedException { + //Pick a random number + int r = RANDOM.nextInt(fortunes.size()); + //Use the random number to pick a random fortune + String f = fortunes.get(r); + // Print out the fortune s.l.o.w.l.y + for (char c: f.toCharArray()) { + System.out.print(c); + Thread.sleep(100); + } + System.out.println(); + } + + /** + * @param args the command line arguments + * @throws java.lang.InterruptedException + * @throws com.fasterxml.jackson.core.JsonProcessingException + */ + public static void main(String[] args) throws InterruptedException, JsonProcessingException { + Fortune fortune = new Fortune(); + fortune.printRandomFortune(); + } +} +---- +. Copy and paste the following file, +https://raw.githubusercontent.com/graalvm/graalvm-demos/master/fortune-demo/fortune/src/main/resources/fortunes.json[fortunes.json] under `resources/`. Your project tree should be: ++ +[source,shell] +---- +. +├── pom.xml +└── src + └── main + ├── java + │   └── demo + │   └── Fortune.java + └── resources + └── fortunes.json +---- +. Add explicit FasterXML Jackson dependencies that provide functionality to read and write JSON, data bindings (used in the demo application). Open the _pom.xml_ file (a Maven configuration file), and insert the following in the `` section: ++ +[source,xml] +---- + + + com.fasterxml.jackson.core + jackson-databind + 2.12.6.1 + + +---- +. Add regular Maven plugins for building and assembling a Maven project into an executable JAR. Insert the following into the `build` section in the _pom.xml_ file: ++ +[source,xml] +---- + + + + org.codehaus.mojo + exec-maven-plugin + 3.0.0 + + + java + + java + + + ${mainClass} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${maven.compiler.source} + ${maven.compiler.source} + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.2.2 + + + + true + ${mainClass} + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + + + + true + ${mainClass} + + + + jar-with-dependencies + + + + + + +---- +. Replace the default `` section in the _pom.xml_ file with this content: ++ +[source,xml,subs="verbatim,attributes"] +---- + + {maven-plugin-version} + 5.8.1 + ${java.specification.version} + ${java.specification.version} + fortune + demo.Fortune + +---- ++ +The statements "hardcoded" plugin versions and the entry point class to your application. The next steps demonstrate what you should do to enable the +https://graalvm.github.io/native-build-tools/latest/maven-plugin.html[Maven plugin for GraalVM Native Image]. +. Register the Maven plugin for GraalVM Native Image, +`native-maven-plugin`, in the profile called `native` by adding the following to the _pom.xml_ file: ++ +[source,xml] +---- + + + native + + + + org.graalvm.buildtools + native-maven-plugin + ${native.maven.plugin.version} + true + + + build-native + + build + + package + + + test-native + + test + + test + + + + false + + + + + + +---- ++ +It pulls the latest plugin version. Replace `${native.maven.plugin.version}` with a specific version if you prefer. +The plugin discovers which JAR files it needs to pass to the +`native-image` builder and what the executable main class should be. With this plugin you can already build a native executable directly with Maven by running `mvn -Pnative package` (if your application does not call any methods reflectively at run time). ++ +This demo application is a little more complicated than `HelloWorld`, and requires metadata before building a native executable. You do not have to configure anything manually: the plugin can generate the required metadata for you by +injecting the https://graalvm.github.io/native-build-tools/latest/maven-plugin.html#agent-support[tracing +agent] at package time. The agent is disabled by default, and can be enabled in project's _pom.xml_ file or via the command line. + +- To enable the agent via the _pom.xml_ file, specify +`true` in the `native-maven-plugin` plugin +configuration: ++ +[source,xml] +---- + + + true + + +---- +- To enable the agent via the command line, pass the `-Dagent=true` option when running Maven. ++ +So your next step is to run with the agent. +. Before running with the agent, register a separate Mojo execution in the `native` profile which allows forking the Java process. It is required to run your application with the agent. ++ +[source,xml] +---- + + org.codehaus.mojo + exec-maven-plugin + 3.0.0 + + + java-agent + + exec + + + java + ${project.build.directory} + + -classpath + + ${mainClass} + + + + + native + + exec + + + ${project.build.directory}/${imageName} + ${project.build.directory} + + + + +---- ++ +Now you are all set to to build a native executable from a Java +application the plugin. + +== Build a Native Executable + +. Compile the project on the JVM to create a runnable JAR with all dependencies. Open a terminal window and, from the root application directory, run: ++ +[source,shell] +---- +mvn clean package +---- +. Run your application with the agent enabled: ++ +[source,shell] +---- +mvn -Pnative -Dagent exec:exec@java-agent +---- ++ +The agent collects the metadata and generates the configuration files in a subdirectory of `target/native/agent-output`. Those files will be automatically used by the `native-image` tool if you pass the appropriate options. +. Now build a native executable with the Maven profile: ++ +[source,shell] +---- +mvn -DskipTests=true -Pnative -Dagent package +---- ++ +When the command completes a native executable, _fortune_, is created in the _/target_ directory of the project and ready for use. ++ +The executable's name is derived from the artifact ID, but you can specify any custom name in `native-maven-plugin` within a +`` node: ++ +[source,xml] +---- + + fortuneteller + +---- +. Run the demo directly or with the Maven profile: ++ +[source,shell] +---- +./target/fortune +---- ++ +[source,shell] +---- +mvn -Pnative exec:exec@native +---- + +To see the benefits of running your application as a native executable, +`time` how long it takes and compare the results with running on the +JVM. + +== Add JUnit Testing + +The Maven plugin for GraalVM Native Image can run +https://junit.org/junit5/docs/current/user-guide/[JUnit Platform] tests on a native executable. This means that tests will be compiled and executed as native code. + +This plugin requires JUnit Platform 1.8 or higher and Maven Surefire 2.22.0 or higher to run tests on a native executable. + +. Enable extensions in the plugin's configuration, +`true`: ++ +[source,xml] +---- + + org.graalvm.buildtools + native-maven-plugin + ${native.maven.plugin.version} + true +---- +. Add an explicit dependency on the `junit-platform-launcher` artifact +to the dependencies section of your native profile configuration as in +the following example: ++ +[source,xml] +---- + + + org.junit.platform + junit-platform-launcher + 1.8.2 + test + + +---- +. Create the following test in the +`src/test/java/demo/FortuneTest.java` file: ++ +.src/test/java/demo/FortuneTest.java +[source,java] +---- +package demo; + +import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class FortuneTest { + @Test + @DisplayName("Returns a fortune") + void testItWorks() throws JsonProcessingException { + Fortune fortune = new Fortune(); + assertTrue(fortune.randomFortune().length()>0); + } +} +---- ++ +. Run native tests: ++ +[source,shell] +---- +mvn -Pnative test +---- ++ +Run `-Pnative` profile will then build and run native tests. + +=== Summary + +The Maven plugin for GraalVM Native Image adds support for building and testing native executables using https://maven.apache.org/[Apache Maven™]. The plugin has many features, described in the <>. + +Note that if your application does not call any classes dynamically at run time, the execution with the agent is needless. Your workflow, in that case, is just `mvn clean -Pnative package`. diff --git a/docs/src/docs/asciidoc/maven-plugin.adoc b/docs/src/docs/asciidoc/maven-plugin.adoc index 26d19dc9d..f8ab0af31 100644 --- a/docs/src/docs/asciidoc/maven-plugin.adoc +++ b/docs/src/docs/asciidoc/maven-plugin.adoc @@ -14,16 +14,25 @@ For upgrading please take a look at the <>. [[quickstart]] == Quickstart -[NOTE] +==== +<> +==== + ==== You can find full samples in https://github.com/graalvm/native-build-tools/tree/master/samples[the source repository]. ==== -This plugin first requires that you <>. -It will then make use of Maven profiles to enable building and testing of native images. +==== +The plugin requires that you <>. +The easiest way to install GraalVM with Native Image is to use the https://github.com/graalvm/graalvm-jdk-downloader[GraalVM JDK Downloader]: +``` +bash <(curl -sL https://get.graalvm.org/jdk) +``` +It will then make use of Maven profiles to enable building and testing of native executables. +==== [[configuration]] -== Configuration +== Reference documentation [[configuration-registering-plugin]] === Registering the plugin @@ -469,7 +478,7 @@ The Native Image Maven plugin simplifies generation of the required configuratio injecting the agent automatically for you (this includes, but is not limited to the reflection file). -The agent generates the native configuration files in a subdirectory of +The agent generates the native configuration files in subdirectories of `target/native/agent-output`. Although those files will be automatically used if you run your build with the agent enabled, you should consider reviewing the generated files and adding them to your sources instead. @@ -505,47 +514,56 @@ line by supplying the `-Dagent=false` flag. === Configuring agent options If you would like to configure the options for the agent -- for example, to configure -experimental features such as `experimental-class-loader-support` or advanced features +agent mode or advanced features such as -https://www.graalvm.org/reference-manual/native-image/Agent/#caller-based-filters[Caller-based Filters] -and https://www.graalvm.org/reference-manual/native-image/Agent/#access-filters[Access Filters] --- you can include `` within the `` block of the configuration of the -`native-maven-plugin` in your POM. - -* You can supply multiple sets of ``. -* You can declare an unnamed `` element which will always be used whenever the - agent is enabled. This should be used to declare common options that will be used for - all executions with the agent. -* Additional `` elements must declare a unique `name` attribute. - - To configure options for your application, use the name `main`. - - To configure options for your tests, use the name `test`. - - To configure additional sets of options, declare each with a unique name other than - `main` or `test`. -* The `main` options are enabled automatically whenever your application is run with the - agent. -* The `test` options are enabled automatically whenever your tests are run with the agent. -* To enable any other set of named ``, supply `-DagentOptions=` as a - command-line argument for Maven, where `` corresponds to the `name` attribute of - the `` element. - -[WARNING] -==== -The Native Image Maven plugin automatically configures the `config-output-dir` for the -agent. An attempt to configure a custom value for the `config-output-dir` option will -therefore result in a build failure. -==== +https://www.graalvm.org/latest/reference-manual/native-image/metadata/AutomaticMetadataCollection/#caller-based-filters[Caller-based Filters] +and https://www.graalvm.org/latest/reference-manual/native-image/metadata/AutomaticMetadataCollection/#access-filters[Access Filters] +- you can specify them in your POM as described bellow. + +[source,xml,indent=0] +include::../snippets/maven/pom.xml[tag=native-plugin-agent-configuration] + +This example shows all possibilities you can use for native agent configuration. If you don't need some of the options, +just remove them. + +Agent can be run in one of the following modes: -The following example is likely more complex than anything you would do in your own -projects, but it demonstrates how to configure four sets of ``. +* `standard` - in this mode you run agent only with options provided in `options` section. +* `direct` - in this mode you can provide command line that you want to be executed as an agent configuration. In this + mode, user is fully responsible for agent configuration, and the rest of the agent configuration, provided in pom.xml file, will be ignored. +* `conditional` - in this mode you can provide additional files that can be used as a filter for the agent output. You +can read more about conditional mode https://www.graalvm.org/latest/reference-manual/native-image/metadata/ExperimentalAgentOptions/[here]. -* The unnamed set is always active. -* The `main` set is automatically active for application execution. -* The `test` set is automatically active for test execution. -* The `periodic-config` set is never active by default, but it can be enabled via - `-DagentOptions=periodic-config` on the command line. +Each option and how you should use it is described bellow: + +* `enabled` - a simple flag that specifies whether the agent is enabled or not. Can be set to true or false. +* `defaultMode` - agent mode switcher. Can be set to: standard, direct or conditional. +* `modes` - list of additional mode options, specific for certain mode type. Inside this tag, you can specify options for + direct or conditional modes. Standard mode doesn't have any specific options. + ** in case of `direct` mode you can specify `` tag, with the agent command line as its value + ** in case of `conditional` mode you can specify `` tag, and set additional https://github.com/oracle/graalvm-reachability-metadata/blob/master/docs/CollectingMetadata.md[filter files] + inside `` and ``. Also, you can set value for `` tag. If the value is true, agent will create + partial-config file, and merge it with conditional merge, otherwise agent will generate same kind of output as other modes. +* `options` - list of options that can be specify independent from agent mode. More about common options can be found +https://www.graalvm.org/latest/reference-manual/native-image/metadata/AutomaticMetadataCollection/[here]. + + +[[metadata-copy]] +==== Metadata copy +`metadataCopy` provides additional options for manipulating the agent output after agent finishes its job. [source,xml,indent=0] -include::../../../../samples/java-application-with-reflection/pom.xml[tag=native-plugin-agent-options] +include::../snippets/maven/metadataCopy.xml[tag=native-plugin-agent-metadata-copy] + +You can set values for the following tags: + +* `` - where you want to copy agent output. +* `` - in case you already have some other config files inside `output directory`, you can choose whether you want to override those files or + merge new files with the existing once (set merge value to true). +* `` - in case you don't want to copy output of the certain stage (main or test) you can disable them and metadataCopy will not look at + the agent output for that stage. For example, if you want to copy only config files generated in tests, you can disable main stage. Also, if you want + to copy only files generated in main phase, you can disable test stage. Therefore, if you skip both stages, metadataCopy will not be executed. + [[agent-support-running-tests]] === Running tests with the agent @@ -563,13 +581,14 @@ When the `agent` system property is set to `true` (or when the agent is to your Maven Surefire test execution, and the generated files can be found in the `target/native/agent-output/test` directory. -To run your tests with custom agent options, supply the `-DagentOptions=` -command-line argument to Maven as follows. See the documentation for -<> for details. - +[TIP] +==== +If you want to run metadataCopy as well, first define its configuration as described <> and add `native:metadata-copy` +at the end of the agent invocation command. Example: ```bash -mvn -Pnative -Dagent=true -DagentOptions=periodic-config test +mvn -Pnative -Dagent=true test native:metadata-copy ``` +==== [[agent-support-running-application]] === Running your application with the agent @@ -588,14 +607,6 @@ Then you can execute your application with the agent by running: mvn -Pnative -Dagent=true -DskipTests -DskipNativeBuild=true package exec:exec@java-agent ``` -To execute your application with custom agent options, supply the `-DagentOptions=` -command-line argument to Maven as follows. See the documentation for -<> for details. - -```bash -mvn -Pnative -Dagent=true -DagentOptions=periodic-config -DskipTests -DskipNativeBuild=true package exec:exec@java-agent -``` - Both of the above commands will generate configuration files in the `target/native/agent-output/main` directory. If you want to run your native application with those configuration files, you then need to execute the following command: diff --git a/docs/src/docs/snippets/maven/metadataCopy.xml b/docs/src/docs/snippets/maven/metadataCopy.xml new file mode 100644 index 000000000..fbd30ccf5 --- /dev/null +++ b/docs/src/docs/snippets/maven/metadataCopy.xml @@ -0,0 +1,9 @@ + + + + main + + true + /tmp/test-output-dir + + \ No newline at end of file diff --git a/docs/src/docs/snippets/maven/pom.xml b/docs/src/docs/snippets/maven/pom.xml new file mode 100644 index 000000000..18a398ec6 --- /dev/null +++ b/docs/src/docs/snippets/maven/pom.xml @@ -0,0 +1,36 @@ + + + + true + Standard + + config-output-dir=${project.build.directory}/native/agent-output + + user-code-filter.json + extra-filter.json + true + + + + + caller-filter-file1.json + caller-filter-file2.json + + + access-filter-file1.json + access-filter-file2.json + + true + true + true + + true + + true + + + + + + + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2b48e25a5..b223a3471 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] # Project versions -nativeBuildTools = "0.9.19" -metadataRepository = "0.2.5" +nativeBuildTools = "0.9.20" +metadataRepository = "0.2.6" # External dependencies spock = "2.1-groovy-3.0" diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java index f8286435b..6f8428acf 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java @@ -387,6 +387,7 @@ private Provider graalVMReachabilityMetadata .getSharedServices() .registerIfAbsent("nativeConfigurationService", GraalVMReachabilityMetadataService.class, spec -> { LogLevel logLevel = determineLogLevel(); + spec.getMaxParallelUsages().set(1); spec.getParameters().getLogLevel().set(logLevel); spec.getParameters().getUri().set(repositoryExtension.getUri().map(configuredUri -> computeMetadataRepositoryUri(project, repositoryExtension, configuredUri, GraalVMLogger.of(project.getLogger())))); spec.getParameters().getCacheDir().set( diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImageService.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImageService.java index 4f6d95b05..ace3cb43c 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImageService.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImageService.java @@ -44,18 +44,31 @@ import org.graalvm.buildtools.gradle.internal.GraalVMLogger; import org.gradle.api.Project; import org.gradle.api.provider.Provider; +import org.gradle.api.provider.ProviderFactory; import org.gradle.api.services.BuildService; import org.gradle.api.services.BuildServiceParameters; @SuppressWarnings({"UnstableApiUsage", "unused"}) public abstract class NativeImageService implements BuildService { + + public static final String MAX_PARALLEL_SYSTEM_PROPERTY = "org.graalvm.buildtools.max.parallel.builds"; + public static final String MAX_PARALLEL_ENV_VAR = "GRAALVM_BUILDTOOLS_MAX_PARALLEL_BUILDS"; + public static Provider registerOn(Project project) { return project.getGradle() .getSharedServices() .registerIfAbsent("nativeImage", NativeImageService.class, spec -> { GraalVMLogger.newBuild(); - spec.getMaxParallelUsages().set(1 + Runtime.getRuntime().availableProcessors() / 16); + spec.getMaxParallelUsages().set(maxParallelUsagesOf(project.getProviders())); }); } + + private static Provider maxParallelUsagesOf(ProviderFactory providers) { + return providers.gradleProperty(MAX_PARALLEL_SYSTEM_PROPERTY) + .forUseAtConfigurationTime() + .orElse(providers.environmentVariable(MAX_PARALLEL_ENV_VAR).forUseAtConfigurationTime()) + .map(Integer::parseInt) + .orElse(1 + Runtime.getRuntime().availableProcessors() / 16); + } } diff --git a/native-maven-plugin/reproducers/issue-144/pom.xml b/native-maven-plugin/reproducers/issue-144/pom.xml index 11d21de41..c811ac09c 100644 --- a/native-maven-plugin/reproducers/issue-144/pom.xml +++ b/native-maven-plugin/reproducers/issue-144/pom.xml @@ -56,8 +56,8 @@ 1.8 UTF-8 - 0.9.19 - 0.9.19 + 0.9.20 + 0.9.20 example-app org.graalvm.demo.Application diff --git a/native-maven-plugin/src/functionalTest/groovy/org/graalvm/buildtools/maven/JavaApplicationWithAgentFunctionalTest.groovy b/native-maven-plugin/src/functionalTest/groovy/org/graalvm/buildtools/maven/JavaApplicationWithAgentFunctionalTest.groovy index a0db0dcf8..26c9d08dc 100644 --- a/native-maven-plugin/src/functionalTest/groovy/org/graalvm/buildtools/maven/JavaApplicationWithAgentFunctionalTest.groovy +++ b/native-maven-plugin/src/functionalTest/groovy/org/graalvm/buildtools/maven/JavaApplicationWithAgentFunctionalTest.groovy @@ -78,218 +78,107 @@ class JavaApplicationWithAgentFunctionalTest extends AbstractGraalVMMavenFunctio outputDoesNotContain "containers found" } - def "agent is used for tests when enabled in POM without custom options"() { + def "test agent with metadata copy task"() { given: withSample("java-application-with-reflection") + mvn'-Pnative', '-DskipNativeBuild=true', 'package', 'exec:exec@java-agent' when: - // Run Maven in debug mode (-X) in order to capture the command line arguments - // used to launch Surefire with the agent. - mvnDebug '-Pnative', 'test' + mvn'-Pnative', '-DskipNativeTests', 'native:metadata-copy' then: - outputContains """ -[ 4 containers found ] -[ 0 containers skipped ] -[ 4 containers started ] -[ 0 containers aborted ] -[ 4 containers successful ] -[ 0 containers failed ] -[ 7 tests found ] -[ 0 tests skipped ] -[ 7 tests started ] -[ 0 tests aborted ] -[ 7 tests successful ] -[ 0 tests failed ] -""".trim() - - and: - ['jni', 'proxy', 'reflect', 'resource', 'serialization'].each { name -> - assert file("target/native/agent-output/test/${name}-config.json").exists() - } - - and: - outputContains '-agentlib:native-image-agent' - // From shared, unnamed config: - outputContains '=experimental-class-loader-support' - // From test config: - outputContains ',access-filter-file=' - outputContains '/src/test/resources/access-filter.json'.replace('/', java.io.File.separator) - // Always configured: - outputContains ',config-output-dir=' - outputContains '/target/native/agent-output/test'.replace("/", java.io.File.separator) - - and: - // If the custom access-filter.json is applied, we should not see any warnings about Surefire types. - // The actual warning would be something like: - // Warning: Could not resolve org.apache.maven.surefire.junitplatform.JUnitPlatformProvider for reflection configuration. Reason: java.lang.ClassNotFoundException: org.apache.maven.surefire.junitplatform.JUnitPlatformProvider. - outputDoesNotContain 'Warning: Could not resolve org.apache.maven.surefire' - // From periodic-config: - outputDoesNotContain ',config-write-period-secs=30,config-write-initial-delay-secs=5' + buildSucceeded + outputDoesNotContain "Cannot merge agent files because native-image-configure is not installed. Please upgrade to a newer version of GraalVM." + outputDoesNotContain "returned non-zero result" + outputDoesNotContain "Agent files cannot be copied." + outputDoesNotContain "Cannot collect agent configuration." + outputContains "Metadata copy process finished." } - def "agent is not used for tests when enabled in POM but disabled via the command line"() { + def "test agent with metadata copy task and disabled stages"() { given: withSample("java-application-with-reflection") + mvn'-PagentConfigurationWithDisabledStages', '-DskipNativeBuild=true', 'package', 'exec:exec@java-agent' when: - mvn '-Pnative', '-Dagent=false', 'test' + mvn '-PagentConfigurationWithDisabledStages', '-DskipNativeTests', 'native:metadata-copy' then: - outputContains """ -[ 4 containers found ] -[ 0 containers skipped ] -[ 4 containers started ] -[ 0 containers aborted ] -[ 4 containers successful ] -[ 0 containers failed ] -[ 7 tests found ] -[ 0 tests skipped ] -[ 7 tests started ] -[ 0 tests aborted ] -[ 6 tests successful ] -[ 1 tests failed ] -""".trim() - - and: - outputContains 'expected: but was: ' + buildSucceeded + outputDoesNotContain "Cannot collect agent configuration." + outputDoesNotContain "Cannot merge agent files because native-image-configure is not installed. Please upgrade to a newer version of GraalVM." + outputDoesNotContain "returned non-zero result" + outputDoesNotContain "Agent files cannot be copied." + outputContains "Copying files from: test" + outputContains "Metadata copy process finished." } - def "agent is used for tests when enabled in POM with custom options"() { + def "test agent in direct mode with metadata copy task"() { given: withSample("java-application-with-reflection") + mvn'-PagentConfigurationDirectMode', '-DskipNativeBuild=true', 'package', 'exec:exec@java-agent' when: - // Run Maven in debug mode (-X) in order to capture the command line arguments - // used to launch Surefire with the agent. - mvnDebug '-Pnative', '-DagentOptions=periodic-config', 'test' + mvn '-PagentConfigurationDirectMode', '-DskipNativeTests', 'native:metadata-copy' then: - outputContains """ -[ 4 containers found ] -[ 0 containers skipped ] -[ 4 containers started ] -[ 0 containers aborted ] -[ 4 containers successful ] -[ 0 containers failed ] -[ 7 tests found ] -[ 0 tests skipped ] -[ 7 tests started ] -[ 0 tests aborted ] -[ 7 tests successful ] -[ 0 tests failed ] -""".trim() - - and: - ['jni', 'proxy', 'reflect', 'resource', 'serialization'].each { name -> - assert file("target/native/agent-output/test/${name}-config.json").exists() - } - - and: - // If custom agent options are processed, the debug output for Surefire - // should include the following segments of the agent command line argument. - outputContains '-agentlib:native-image-agent' - // From shared, unnamed config: - outputContains '=experimental-class-loader-support' - // From test config: - outputContains ',access-filter-file=' - outputContains '/src/test/resources/access-filter.json'.replace('/', java.io.File.separator) - // From periodic-config: - outputContains ',config-write-period-secs=30,config-write-initial-delay-secs=5' - // Always configured: - outputContains ',config-output-dir=' - outputContains '/target/native/agent-output/test'.replace("/", java.io.File.separator) - - and: - // If the custom access-filter.json is applied, we should not see any warnings about Surefire types. - // The actual warning would be something like: - // Warning: Could not resolve org.apache.maven.surefire.junitplatform.JUnitPlatformProvider for reflection configuration. Reason: java.lang.ClassNotFoundException: org.apache.maven.surefire.junitplatform.JUnitPlatformProvider. - outputDoesNotContain 'Warning: Could not resolve org.apache.maven.surefire' + buildSucceeded + outputDoesNotContain "Cannot collect agent configuration." + outputDoesNotContain "Cannot merge agent files because native-image-configure is not installed. Please upgrade to a newer version of GraalVM." + outputDoesNotContain "returned non-zero result" + outputContains "You are running agent in direct mode. Skipping both merge and metadata copy tasks." } - @Issue("https://github.com/graalvm/native-build-tools/issues/134") - @Unroll("generated agent files are added when building native image on Maven #version with JUnit Platform #junitVersion") - def "generated agent files are used when building native image"() { + def "test agent in conditional mode with metadata copy task"() { given: withSample("java-application-with-reflection") + mvn '-PagentConfigurationConditionalMode', '-DskipNativeBuild=true', 'package', 'exec:exec@java-agent' when: - mvnDebug '-Pnative', '-DskipTests=true', '-DskipNativeBuild=true', 'package', 'exec:exec@java-agent' + mvn '-PagentConfigurationConditionalMode', '-DskipNativeTests', 'native:metadata-copy' then: - ['jni', 'proxy', 'reflect', 'resource', 'serialization'].each { name -> - assert file("target/native/agent-output/main/${name}-config.json").exists() - } - - and: - // If custom agent options are not used, the Maven debug output should include - // the following segments of the agent command line argument. - outputContains '-agentlib:native-image-agent' - // From shared, unnamed config: - outputContains '=experimental-class-loader-support' - // From main config: - outputContains ',access-filter-file=' - outputContains '/src/main/resources/access-filter.json'.replace('/', java.io.File.separator) - // Always configured: - outputContains ',config-output-dir=' - outputContains '/target/native/agent-output/main'.replace("/", java.io.File.separator) - - when: - mvn '-Pnative', '-DskipTests=true', 'package', 'exec:exec@native' - - then: - outputContains "Application message: Hello, native!" + buildSucceeded + outputDoesNotContain "Cannot collect agent configuration." + outputDoesNotContain "Cannot merge agent files because native-image-configure is not installed. Please upgrade to a newer version of GraalVM." + outputDoesNotContain "returned non-zero result" } - def "generated agent files are not used when building native image when agent is enabled in POM but disabled via the command line"() { + def "test without agent configuration"() { given: withSample("java-application-with-reflection") when: - mvnDebug '-Pnative', '-Dagent=false', '-DskipTests=true', '-DskipNativeBuild=true', 'package', 'exec:exec@java-agent' + mvn'-PnoAgentConfiguration', 'package' then: - outputDoesNotContain '-agentlib:native-image-agent' - - when: - mvn '-Pnative', '-DskipTests=true', 'package', 'exec:exec@native' - - then: - outputContains "Application message: null" + buildSucceeded } - def "custom options and generated agent files are used when building native image"() { + def "agent is not used for tests when enabled in POM but disabled via the command line"() { given: withSample("java-application-with-reflection") when: - mvnDebug '-Pnative', '-DagentOptions=periodic-config', '-DskipTests=true', '-DskipNativeBuild=true', 'package', 'exec:exec@java-agent' + mvn '-Pnative', '-Dagent=false', 'test' then: - ['jni', 'proxy', 'reflect', 'resource', 'serialization'].each { name -> - assert file("target/native/agent-output/main/${name}-config.json").exists() - } + outputContains """ +[ 4 containers found ] +[ 0 containers skipped ] +[ 4 containers started ] +[ 0 containers aborted ] +[ 4 containers successful ] +[ 0 containers failed ] +[ 7 tests found ] +[ 0 tests skipped ] +[ 7 tests started ] +[ 0 tests aborted ] +[ 6 tests successful ] +[ 1 tests failed ] +""".trim() and: - // If custom agent options are used, the Maven debug output should include - // the following segments of the agent command line argument. - outputContains '-agentlib:native-image-agent' - // From shared, unnamed config: - outputContains '=experimental-class-loader-support' - // From main config: - outputContains ',access-filter-file=' - outputContains '/src/main/resources/access-filter.json'.replace('/', java.io.File.separator) - // From periodic-config: - outputContains ',config-write-period-secs=30,config-write-initial-delay-secs=5' - // Always configured: - outputContains ',config-output-dir=' - outputContains '/target/native/agent-output/main'.replace("/", java.io.File.separator) - - when: - mvn '-Pnative', '-DskipTests=true', 'package', 'exec:exec@native' - - then: - outputContains "Application message: Hello, native!" + outputContains 'expected: but was: ' } - } diff --git a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/AbstractNativeImageMojo.java b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/AbstractNativeImageMojo.java index ed71a1564..cdd58e386 100644 --- a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/AbstractNativeImageMojo.java +++ b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/AbstractNativeImageMojo.java @@ -41,43 +41,42 @@ package org.graalvm.buildtools.maven; -import java.io.BufferedReader; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.toolchain.ToolchainManager; +import org.graalvm.buildtools.maven.config.ExcludeConfigConfiguration; +import org.graalvm.buildtools.utils.NativeImageConfigurationUtils; +import org.graalvm.buildtools.utils.NativeImageUtils; +import org.graalvm.buildtools.utils.SharedConstants; + +import javax.inject.Inject; import java.io.File; -import java.io.IOException; import java.io.InputStream; +import java.io.BufferedReader; import java.io.InputStreamReader; +import java.io.IOException; import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.FileSystems; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; +import java.util.Collections; import java.util.Map; import java.util.Optional; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.inject.Inject; - -import org.apache.maven.artifact.Artifact; -import org.apache.maven.execution.MavenSession; -import org.apache.maven.plugin.MojoExecution; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.descriptor.PluginDescriptor; -import org.apache.maven.plugins.annotations.Component; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.toolchain.ToolchainManager; -import org.graalvm.buildtools.Utils; -import org.graalvm.buildtools.maven.config.ExcludeConfigConfiguration; -import org.graalvm.buildtools.utils.NativeImageUtils; -import org.graalvm.buildtools.utils.SharedConstants; - /** * @author Sebastien Deleuze */ @@ -383,7 +382,7 @@ protected String getClasspath() throws MojoExecutionException { protected void buildImage() throws MojoExecutionException { checkRequiredVersionIfNeeded(); - Path nativeImageExecutable = Utils.getNativeImage(logger); + Path nativeImageExecutable = NativeImageConfigurationUtils.getNativeImage(logger); try { ProcessBuilder processBuilder = new ProcessBuilder(nativeImageExecutable.toString()); @@ -419,7 +418,7 @@ protected void checkRequiredVersionIfNeeded() throws MojoExecutionException { if (requiredVersion == null) { return; } - Path nativeImageExecutable = Utils.getNativeImage(logger); + Path nativeImageExecutable = NativeImageConfigurationUtils.getNativeImage(logger); try { ProcessBuilder processBuilder = new ProcessBuilder(nativeImageExecutable.toString()); processBuilder.command().add("--version"); diff --git a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/MergeAgentFilesMojo.java b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/MergeAgentFilesMojo.java index ad4707015..d5b5a19f4 100644 --- a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/MergeAgentFilesMojo.java +++ b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/MergeAgentFilesMojo.java @@ -40,32 +40,29 @@ */ package org.graalvm.buildtools.maven; -import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.logging.Logger; import org.codehaus.plexus.util.FileUtils; -import org.graalvm.buildtools.Utils; -import org.graalvm.buildtools.utils.NativeImageUtils; +import org.graalvm.buildtools.maven.config.AbstractMergeAgentFilesMojo; +import org.graalvm.buildtools.maven.config.agent.AgentConfiguration; +import org.graalvm.buildtools.utils.NativeImageConfigurationUtils; import java.io.File; import java.io.IOException; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.Collections; import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.graalvm.buildtools.maven.NativeExtension.agentOutputDirectoryFor; -import static org.graalvm.buildtools.utils.NativeImageUtils.nativeImageConfigureFileName; - @Mojo(name = "merge-agent-files", defaultPhase = LifecyclePhase.TEST) -public class MergeAgentFilesMojo extends AbstractMojo { +public class MergeAgentFilesMojo extends AbstractMergeAgentFilesMojo { @Parameter(defaultValue = "${project}", readonly = true, required = true) protected MavenProject project; @@ -75,48 +72,63 @@ public class MergeAgentFilesMojo extends AbstractMojo { @Parameter(property = "native.agent.merge.context", required = true) protected String context; - @Component - protected Logger logger; + @Parameter(alias = "agent") + private AgentConfiguration agentConfiguration; + private static int numberOfExecutions = 0; @Override public void execute() throws MojoExecutionException { - String agentOutputDirectory = agentOutputDirectoryFor(target, NativeExtension.Context.valueOf(context)); + // we need this mojo to be executed only once + numberOfExecutions++; + if (numberOfExecutions > 1) { + return; + } + + // if we reached here and agent config is null, agent is enabled but there is no configuration in pom.xml + // that means that we enabled agent from command line, so we are using default agent configuration + if (agentConfiguration == null) { + agentConfiguration = new AgentConfiguration(); + } + + + if (agentConfiguration.getDefaultMode().equalsIgnoreCase("direct")) { + logger.info("Skipping files merge mojo since we are in direct mode"); + return; + } + + List disabledPhases = agentConfiguration.getMetadataCopyConfiguration().getDisabledStages(); + if (disabledPhases.size() == 2) { + logger.info("Both phases are skipped."); + return; + } + + Set dirs = new HashSet(2); + dirs.addAll(Arrays.asList("main", "test")); + dirs.removeAll(disabledPhases); + + for (String dir : dirs) { + String agentOutputDirectory = (target + "/native/agent-output/" + dir).replace('/', File.separatorChar); + mergeForGivenDir(agentOutputDirectory); + } + } + + private void mergeForGivenDir(String agentOutputDirectory) throws MojoExecutionException { File baseDir = new File(agentOutputDirectory); if (baseDir.exists()) { - Path nativeImageExecutable = Utils.getNativeImage(logger); - File mergerExecutable = tryInstall(nativeImageExecutable); + Path nativeImageExecutable = NativeImageConfigurationUtils.getNativeImage(logger); + tryInstallMergeExecutable(nativeImageExecutable); List sessionDirectories = sessionDirectoriesFrom(baseDir.listFiles()).collect(Collectors.toList()); + if (sessionDirectories.size() == 0) { + sessionDirectories = Collections.singletonList(baseDir); + } + invokeMerge(mergerExecutable, sessionDirectories, baseDir); } else { getLog().debug("Agent output directory " + baseDir + " doesn't exist. Skipping merge."); } } - private File tryInstall(Path nativeImageExecutablePath) { - File nativeImageExecutable = nativeImageExecutablePath.toAbsolutePath().toFile(); - File mergerExecutable = new File(nativeImageExecutable.getParentFile(), nativeImageConfigureFileName()); - if (!mergerExecutable.exists()) { - getLog().info("Installing native image merger to " + mergerExecutable); - ProcessBuilder processBuilder = new ProcessBuilder(nativeImageExecutable.toString()); - processBuilder.command().add("--macro:native-image-configure-launcher"); - processBuilder.directory(mergerExecutable.getParentFile()); - processBuilder.inheritIO(); - - try { - Process installProcess = processBuilder.start(); - if (installProcess.waitFor() != 0) { - getLog().warn("Installation of native image merging tool failed"); - } - NativeImageUtils.maybeCreateConfigureUtilSymlink(mergerExecutable, nativeImageExecutablePath); - } catch (IOException | InterruptedException e) { - // ignore since we will handle that if the installer doesn't exist later - } - - } - return mergerExecutable; - } - private static Stream sessionDirectoriesFrom(File[] files) { return Arrays.stream(files) .filter(File::isDirectory) @@ -133,28 +145,33 @@ private void invokeMerge(File mergerExecutable, List inputDirectories, Fil getLog().warn("Skipping merging of agent files since there are no input directories."); return; } + getLog().info("Merging agent " + inputDirectories.size() + " files into " + outputDirectory); - List args = new ArrayList<>(inputDirectories.size() + 2); - args.add("generate"); - inputDirectories.stream() - .map(f -> "--input-dir=" + f.getAbsolutePath()) - .forEach(args::add); - args.add("--output-dir=" + outputDirectory.getAbsolutePath()); + List optionsInputDirs = inputDirectories.stream().map(File::getAbsolutePath).collect(Collectors.toList()); + List optionsOutputDirs = Collections.singletonList(outputDirectory.getAbsolutePath()); + List args = agentConfiguration.getAgentMode().getNativeImageConfigureOptions(optionsInputDirs, optionsOutputDirs); + ProcessBuilder processBuilder = new ProcessBuilder(mergerExecutable.toString()); processBuilder.command().addAll(args); processBuilder.inheritIO(); - String commandString = String.join(" ", processBuilder.command()); Process imageBuildProcess = processBuilder.start(); if (imageBuildProcess.waitFor() != 0) { throw new MojoExecutionException("Execution of " + commandString + " returned non-zero result"); } - for (File inputDirectory : inputDirectories) { - FileUtils.deleteDirectory(inputDirectory); + + // in case inputDirectories has only one value which is the same as outputDirectory + // we shouldn't delete that directory, because we will delete outputDirectory + if (!(inputDirectories.size() == 1 && inputDirectories.get(0).equals(outputDirectory))) { + for (File inputDirectory : inputDirectories) { + FileUtils.deleteDirectory(inputDirectory); + } } + getLog().debug("Agent output: " + Arrays.toString(outputDirectory.listFiles())); } catch (IOException | InterruptedException e) { throw new MojoExecutionException("Merging agent files with " + mergerExecutable + " failed", e); } } + } diff --git a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/MetadataCopyMojo.java b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/MetadataCopyMojo.java new file mode 100644 index 000000000..8b4150873 --- /dev/null +++ b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/MetadataCopyMojo.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.buildtools.maven; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.graalvm.buildtools.agent.StandardAgentMode; +import org.graalvm.buildtools.maven.config.AbstractMergeAgentFilesMojo; +import org.graalvm.buildtools.maven.config.agent.AgentConfiguration; +import org.graalvm.buildtools.maven.config.agent.MetadataCopyConfiguration; +import org.graalvm.buildtools.utils.NativeImageConfigurationUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.ArrayList; +import java.util.Objects; +import java.util.stream.Collectors; + +@Mojo(name = "metadata-copy", defaultPhase = LifecyclePhase.PREPARE_PACKAGE) +public class MetadataCopyMojo extends AbstractMergeAgentFilesMojo { + + private static final String DEFAULT_OUTPUT_DIRECTORY = "/META-INF/native-image"; + private static final List FILES_REQUIRED_FOR_MERGE = Arrays.asList("reflect-config.json", "jni-config.json", "proxy-config.json", "resource-config.json"); + + @Parameter(alias = "agent") + private AgentConfiguration agentConfiguration; + + @Parameter(defaultValue = "${project}", readonly = true, required = true) + private MavenProject project; + + @Override + public void execute() throws MojoExecutionException { + if (agentConfiguration != null && agentConfiguration.isEnabled()) { + // in direct mode user is fully responsible for agent configuration, and we will not execute anything besides line that user provided + if (agentConfiguration.getDefaultMode().equalsIgnoreCase("direct")) { + logger.info("You are running agent in direct mode. Skipping both merge and metadata copy tasks."); + logger.info("In direct mode, user takes full responsibility for agent configuration."); + return; + } + + MetadataCopyConfiguration config = agentConfiguration.getMetadataCopyConfiguration(); + if (config == null) { + getLog().info("Metadata copy config not provided. Skipping this task."); + return; + } + + String buildDirectory = project.getBuild().getDirectory() + "/native/agent-output/"; + String destinationDir = config.getOutputDirectory(); + if (destinationDir == null) { + destinationDir = project.getBuild().getOutputDirectory() + DEFAULT_OUTPUT_DIRECTORY; + } + + if (!Files.isDirectory(Paths.get(destinationDir))) { + logger.warn("Destination directory " + destinationDir + " doesn't exist."); + logger.warn("Creating directory at: " + destinationDir); + boolean success = new File(destinationDir).mkdirs(); + if (!success) { + throw new MojoExecutionException("Cannot create directory at the given location: " + destinationDir); + } + } + + Path nativeImageExecutable = NativeImageConfigurationUtils.getNativeImage(logger); + tryInstallMergeExecutable(nativeImageExecutable); + executeCopy(buildDirectory, destinationDir); + getLog().info("Metadata copy process finished."); + } + } + + private void executeCopy(String buildDirectory, String destinationDir) throws MojoExecutionException { + MetadataCopyConfiguration config = agentConfiguration.getMetadataCopyConfiguration(); + List sourceDirectories = getSourceDirectories(config.getDisabledStages(), buildDirectory); + + // in case we have both main and test phase disabled, we don't need to copy anything + if (sourceDirectories.isEmpty()) { + logger.warn("Skipping metadata copy task. Both main and test stages are disabled in metadata copy configuration."); + return; + } + + // In case user wants to merge agent-output files with some existing files in output directory, we need to check if there are some + // files in outputDirectory that can be merged. If the output directory is empty, we ignore user instruction to merge files. + if (config.shouldMerge() && !isDirectoryEmpty(destinationDir)) { + // If output directory contains some files, we need to check if the directory contains all necessary files for merge + if (!dirContainsFilesForMerge(destinationDir)) { + List destinationDirContent = Arrays.stream(Objects.requireNonNull(new File(destinationDir).listFiles())).map(File::getName).collect(Collectors.toList()); + List missingFiles = getListDiff(FILES_REQUIRED_FOR_MERGE, destinationDirContent); + + throw new MojoExecutionException("There are missing files for merge in output directory. If you want to merge agent files with " + + "existing files in output directory, please make sure that output directory contains all of the following files: " + + "reflect-config.json, jni-config.json, proxy-config.json, resource-config.json. Currently the output directory is " + + "missing: " + missingFiles); + } + + sourceDirectories.add(destinationDir); + } + + if (!checkIfSourcesExists(sourceDirectories)) { + return; + } + + String sourceDirsInfo = sourceDirectories.stream().map(File::new).map(File::getName).collect(Collectors.joining(", ")); + logger.info("Copying files from: " + sourceDirsInfo); + + List nativeImageConfigureOptions = new StandardAgentMode().getNativeImageConfigureOptions(sourceDirectories, Collections.singletonList(destinationDir)); + nativeImageConfigureOptions.add(0, mergerExecutable.getAbsolutePath()); + ProcessBuilder processBuilder = new ProcessBuilder(nativeImageConfigureOptions); + + try { + Process start = processBuilder.start(); + int retCode = start.waitFor(); + if (retCode != 0) { + getLog().error("Metadata copy process failed with code: " + retCode); + throw new MojoExecutionException("Metadata copy process failed."); + } + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + + private boolean checkIfSourcesExists(List sourceDirectories) { + for (String source : sourceDirectories) { + File dir = new File(source); + if (!dir.isDirectory() || !dir.exists()) { + logger.warn("Cannot find source directory " + source + " for metadata copy. Please check if you configured agent" + + " properly and it generates all necessary directories. If you want to skipp copy from some source, please " + + "configure metadataCopy with disable stage you want to skipp."); + return false; + } + } + + return true; + } + + private List getSourceDirectories(List disabledStages, String buildDirectory) { + List sourceDirectories = new ArrayList<>(); + + sourceDirectories.add(buildDirectory + NativeExtension.Context.main); + sourceDirectories.add(buildDirectory + NativeExtension.Context.test); + + for (String disabledStage : disabledStages) { + sourceDirectories.remove(buildDirectory + disabledStage); + } + + return sourceDirectories; + } + + private boolean isDirectoryEmpty(String dirName) { + File directory = new File(dirName); + File[] content = directory.listFiles(); + + return content == null || content.length == 0; + } + + //check if we have all files needed for native-image-configure generate tool + private boolean dirContainsFilesForMerge(String dir) { + File baseDir = new File(dir); + File[] content = baseDir.listFiles(); + if (content == null) { + return false; + } + List dirContent = Arrays.stream(content).map(File::getName).collect(Collectors.toList()); + + return getListDiff(FILES_REQUIRED_FOR_MERGE, dirContent).isEmpty(); + } + + private List getListDiff(List list1, List list2) { + List diff = new ArrayList<>(list1); + diff.removeAll(list2); + return diff; + } + +} diff --git a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeExtension.java b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeExtension.java index 56d81d58b..cae7fba8d 100644 --- a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeExtension.java +++ b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeExtension.java @@ -52,7 +52,8 @@ import org.codehaus.plexus.logging.LogEnabled; import org.codehaus.plexus.logging.Logger; import org.codehaus.plexus.util.xml.Xpp3Dom; -import org.graalvm.buildtools.Utils; +import org.graalvm.buildtools.agent.AgentConfiguration; +import org.graalvm.buildtools.utils.AgentUtils; import org.graalvm.buildtools.utils.SharedConstants; import java.io.File; @@ -61,6 +62,9 @@ import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.stream.Collectors; + +import static org.graalvm.buildtools.utils.NativeImageConfigurationUtils.getNativeImage; /** * This extension is responsible for configuring the Surefire plugin to enable @@ -105,7 +109,8 @@ static String buildAgentArgument(String baseDir, Context context, List a // the same unit of work !). effectiveOutputDir = effectiveOutputDir + File.separator + SharedConstants.AGENT_SESSION_SUBDIR; } - options.add("config-output-dir=" + effectiveOutputDir); + String finalEffectiveOutputDir = effectiveOutputDir; + options = options.stream().map(option -> option.contains("{output_dir}") ? option.replace("{output_dir}", finalEffectiveOutputDir) : option).collect(Collectors.toList()); return "-agentlib:native-image-agent=" + String.join(",", options); } @@ -120,20 +125,26 @@ public void afterProjectsRead(MavenSession session) { withPlugin(build, "native-maven-plugin", nativePlugin -> { String target = build.getDirectory(); String testIdsDir = testIdsDirectory(target); - boolean isAgentEnabled = isAgentEnabled(session, nativePlugin); - String selectedOptionsName = getSelectedOptionsName(session); + + Xpp3Dom configurationRoot = (Xpp3Dom) nativePlugin.getConfiguration(); + AgentConfiguration agent; + try { + agent = AgentUtils.collectAgentProperties(session, configurationRoot); + } catch (Exception e) { + throw new RuntimeException(e); + } // Test configuration withPlugin(build, "maven-surefire-plugin", surefirePlugin -> { configureJunitListener(surefirePlugin, testIdsDir); - if (isAgentEnabled) { - List agentOptions = getAgentOptions(nativePlugin, Context.test, selectedOptionsName); + if (agent.isEnabled()) { + List agentOptions = agent.getAgentCommandLine(); configureAgentForSurefire(surefirePlugin, buildAgentArgument(target, Context.test, agentOptions)); } }); // Main configuration - if (isAgentEnabled) { + if (agent.isEnabled()) { withPlugin(build, "exec-maven-plugin", execPlugin -> updatePluginConfiguration(execPlugin, (exec, config) -> { if ("java-agent".equals(exec.getId())) { @@ -147,7 +158,7 @@ public void afterProjectsRead(MavenSession session) { // Agent argument Xpp3Dom arg = new Xpp3Dom("argument"); - List agentOptions = getAgentOptions(nativePlugin, Context.main, selectedOptionsName); + List agentOptions = agent.getAgentCommandLine(); arg.setValue(buildAgentArgument(target, Context.main, agentOptions)); children.add(0, arg); @@ -167,9 +178,7 @@ public void afterProjectsRead(MavenSession session) { Context context = exec.getGoals().stream().anyMatch("test"::equals) ? Context.test : Context.main; Xpp3Dom agentResourceDirectory = findOrAppend(configuration, "agentResourceDirectory"); agentResourceDirectory.setValue(agentOutputDirectoryFor(target, context)); - if (context == Context.test) { - setupMergeAgentFiles(exec, configuration, context); - } + setupMergeAgentFiles(exec, configuration, context); }); } }); @@ -193,118 +202,6 @@ private static void withPlugin(Build build, String artifactId, Consumer", enabled.getValue()); - } - } - - return false; - } - - private static String getSelectedOptionsName(MavenSession session) { - String selectedOptionsName = session.getSystemProperties().getProperty("agentOptions"); - if (selectedOptionsName == null) { - return null; - } - return assertNotEmptyAndTrim(selectedOptionsName, "agentOptions system property must have a value"); - } - - /** - * Parses the configuration block for the {@code native-maven-plugin}, searching for - * {@code } elements whose names match the name of the supplied {@code context} - * or {@code selectedOptionsName} and for unnamed, shared {@code } elements, - * and returns a list of the collected agent options. - * - * @param nativePlugin the {@code native-maven-plugin}; never null - * @param context the current execution context; never null - * @param selectedOptionsName the name of the set of custom agent options activated - * by the user via the {@code agentOptions} system property; may be null if not - * supplied via a system property - */ - private static List getAgentOptions(Plugin nativePlugin, Context context, String selectedOptionsName) { - // - // - // true - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - - List optionsList = new ArrayList<>(); - Xpp3Dom agent = getAgentNode(nativePlugin); - if (agent != null) { - for (Xpp3Dom options : agent.getChildren("options")) { - String name = options.getAttribute("name"); - if (name != null) { - name = assertNotEmptyAndTrim(name, " must declare a non-empty name attribute or omit the name attribute"); - } - // If unnamed/shared options, or options for the current context (main/test), or user-selected options: - if (name == null || name.equals(context.name()) || name.equals(selectedOptionsName)) { - processOptionNodes(options, optionsList); - } - } - } - return optionsList; - } - - private static void processOptionNodes(Xpp3Dom options, List optionsList) { - for (Xpp3Dom option : options.getChildren("option")) { - String value = assertNotEmptyAndTrim(option.getValue(), " diff --git a/samples/java-application-with-custom-tests/gradle.properties b/samples/java-application-with-custom-tests/gradle.properties index a35570695..ea067ed6e 100644 --- a/samples/java-application-with-custom-tests/gradle.properties +++ b/samples/java-application-with-custom-tests/gradle.properties @@ -1,3 +1,3 @@ -native.gradle.plugin.version = 0.9.19 +native.gradle.plugin.version = 0.9.20 junit.jupiter.version = 5.8.1 junit.platform.version = 1.8.1 diff --git a/samples/java-application-with-extra-sourceset/gradle.properties b/samples/java-application-with-extra-sourceset/gradle.properties index a35570695..ea067ed6e 100644 --- a/samples/java-application-with-extra-sourceset/gradle.properties +++ b/samples/java-application-with-extra-sourceset/gradle.properties @@ -1,3 +1,3 @@ -native.gradle.plugin.version = 0.9.19 +native.gradle.plugin.version = 0.9.20 junit.jupiter.version = 5.8.1 junit.platform.version = 1.8.1 diff --git a/samples/java-application-with-reflection/gradle.properties b/samples/java-application-with-reflection/gradle.properties index a35570695..ea067ed6e 100644 --- a/samples/java-application-with-reflection/gradle.properties +++ b/samples/java-application-with-reflection/gradle.properties @@ -1,3 +1,3 @@ -native.gradle.plugin.version = 0.9.19 +native.gradle.plugin.version = 0.9.20 junit.jupiter.version = 5.8.1 junit.platform.version = 1.8.1 diff --git a/samples/java-application-with-reflection/pom.xml b/samples/java-application-with-reflection/pom.xml index 846673f3c..a167e901f 100644 --- a/samples/java-application-with-reflection/pom.xml +++ b/samples/java-application-with-reflection/pom.xml @@ -52,8 +52,8 @@ 1.8 UTF-8 5.8.1 - 0.9.19 - 0.9.19 + 0.9.20 + 0.9.20 example-app org.graalvm.demo.Application @@ -88,6 +88,7 @@ exec + test java ${project.build.directory} @@ -139,35 +140,273 @@ - ${imageName} - false - - true - - - + Standard + + true + true + true + true + true - - + + true + + + + + + + + + agentConfigurationWithDisabledStages + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + org.codehaus.mojo + exec-maven-plugin + 3.0.0 + + + java-agent + + exec + + test + + java + ${project.build.directory} + + -classpath + + ${mainClass} + + + + + native + + exec + + + ${project.build.directory}/${imageName} + ${project.build.directory} + + + + + + org.graalvm.buildtools + native-maven-plugin + ${native.maven.plugin.version} + true + + + test-native + + test + + test + + + build-native + + compile-no-fork + + package + + + + + true + Standard + + true + true + false + false + true - - + + + main + + false + /tmp + + + + + + + + + agentConfigurationDirectMode + + + + org.graalvm.buildtools + native-maven-plugin + ${native.maven.plugin.version} + true + + + test-native + + test + + test + + + build-native + + compile-no-fork + + package + + + + + true + Direct + + config-output-dir=/tmp,builtin-caller-filter=true,builtin-heuristic-filter=true,experimental-class-define-support=false,experimental-unsafe-allocation-support=false,track-reflection-metadata=true + + + true + true + false + false + true - - - + + false + /tmp + + + + + + + + + agentConfigurationConditionalMode + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + + org.codehaus.mojo + exec-maven-plugin + 3.0.0 + + + java-agent + + exec + + test + + java + ${project.build.directory} + + -classpath + + ${mainClass} + + + + + + native + + exec + + + ${project.build.directory}/${imageName} + ${project.build.directory} + + + + + + + + + org.graalvm.buildtools + native-maven-plugin + ${native.maven.plugin.version} + true + + + + test-native + + test + + test + + + build-native + + compile-no-fork + + package + + + + + + + true + Conditional + + + user-code-filter.json + true + + + + true + true + false + false + true + + false + /tmp + - + + + + + + noAgentConfiguration + + + + org.graalvm.buildtools + native-maven-plugin + ${native.maven.plugin.version} + true - diff --git a/samples/java-application-with-resources/gradle.properties b/samples/java-application-with-resources/gradle.properties index a35570695..ea067ed6e 100644 --- a/samples/java-application-with-resources/gradle.properties +++ b/samples/java-application-with-resources/gradle.properties @@ -1,3 +1,3 @@ -native.gradle.plugin.version = 0.9.19 +native.gradle.plugin.version = 0.9.20 junit.jupiter.version = 5.8.1 junit.platform.version = 1.8.1 diff --git a/samples/java-application-with-resources/pom.xml b/samples/java-application-with-resources/pom.xml index b9daf5c02..1331d4ced 100644 --- a/samples/java-application-with-resources/pom.xml +++ b/samples/java-application-with-resources/pom.xml @@ -51,9 +51,9 @@ 1.8 UTF-8 - 0.9.19 + 0.9.20 5.8.1 - 0.9.19 + 0.9.20 example-app org.graalvm.demo.Application diff --git a/samples/java-application-with-tests/gradle.properties b/samples/java-application-with-tests/gradle.properties index a35570695..ea067ed6e 100644 --- a/samples/java-application-with-tests/gradle.properties +++ b/samples/java-application-with-tests/gradle.properties @@ -1,3 +1,3 @@ -native.gradle.plugin.version = 0.9.19 +native.gradle.plugin.version = 0.9.20 junit.jupiter.version = 5.8.1 junit.platform.version = 1.8.1 diff --git a/samples/java-application-with-tests/pom.xml b/samples/java-application-with-tests/pom.xml index 8edd4e493..d215002d8 100644 --- a/samples/java-application-with-tests/pom.xml +++ b/samples/java-application-with-tests/pom.xml @@ -52,8 +52,8 @@ 1.8 UTF-8 5.8.1 - 0.9.19 - 0.9.19 + 0.9.20 + 0.9.20 example-app org.graalvm.demo.Application diff --git a/samples/java-application/gradle.properties b/samples/java-application/gradle.properties index a35570695..ea067ed6e 100644 --- a/samples/java-application/gradle.properties +++ b/samples/java-application/gradle.properties @@ -1,3 +1,3 @@ -native.gradle.plugin.version = 0.9.19 +native.gradle.plugin.version = 0.9.20 junit.jupiter.version = 5.8.1 junit.platform.version = 1.8.1 diff --git a/samples/java-application/pom.xml b/samples/java-application/pom.xml index 77c920260..f554a0753 100644 --- a/samples/java-application/pom.xml +++ b/samples/java-application/pom.xml @@ -51,8 +51,8 @@ 1.8 UTF-8 - 0.9.19 - 0.9.19 + 0.9.20 + 0.9.20 example-app org.graalvm.demo.Application 22.2 diff --git a/samples/java-library/gradle.properties b/samples/java-library/gradle.properties index a35570695..ea067ed6e 100644 --- a/samples/java-library/gradle.properties +++ b/samples/java-library/gradle.properties @@ -1,3 +1,3 @@ -native.gradle.plugin.version = 0.9.19 +native.gradle.plugin.version = 0.9.20 junit.jupiter.version = 5.8.1 junit.platform.version = 1.8.1 diff --git a/samples/java-library/pom.xml b/samples/java-library/pom.xml index bab92db55..6522e76a5 100644 --- a/samples/java-library/pom.xml +++ b/samples/java-library/pom.xml @@ -51,8 +51,8 @@ 1.8 UTF-8 - 0.9.19 - 0.9.19 + 0.9.20 + 0.9.20 java-library diff --git a/samples/kotlin-application-with-tests/gradle.properties b/samples/kotlin-application-with-tests/gradle.properties index a35570695..ea067ed6e 100644 --- a/samples/kotlin-application-with-tests/gradle.properties +++ b/samples/kotlin-application-with-tests/gradle.properties @@ -1,3 +1,3 @@ -native.gradle.plugin.version = 0.9.19 +native.gradle.plugin.version = 0.9.20 junit.jupiter.version = 5.8.1 junit.platform.version = 1.8.1 diff --git a/samples/metadata-repo-integration/gradle.properties b/samples/metadata-repo-integration/gradle.properties index eb8b98245..dea8263a1 100644 --- a/samples/metadata-repo-integration/gradle.properties +++ b/samples/metadata-repo-integration/gradle.properties @@ -1,4 +1,4 @@ -native.gradle.plugin.version = 0.9.19 +native.gradle.plugin.version = 0.9.20 h2.version = 2.1.210 netty.version = 4.1.80.Final logback.version = 1.4.4 diff --git a/samples/metadata-repo-integration/pom.xml b/samples/metadata-repo-integration/pom.xml index 8d071a061..3287c6a1e 100644 --- a/samples/metadata-repo-integration/pom.xml +++ b/samples/metadata-repo-integration/pom.xml @@ -51,8 +51,8 @@ 1.8 UTF-8 - 0.9.19 - 0.9.19 + 0.9.20 + 0.9.20 2.1.210 4.1.80.Final 1.4.4 diff --git a/samples/multi-project-with-tests/gradle.properties b/samples/multi-project-with-tests/gradle.properties index a35570695..ea067ed6e 100644 --- a/samples/multi-project-with-tests/gradle.properties +++ b/samples/multi-project-with-tests/gradle.properties @@ -1,3 +1,3 @@ -native.gradle.plugin.version = 0.9.19 +native.gradle.plugin.version = 0.9.20 junit.jupiter.version = 5.8.1 junit.platform.version = 1.8.1 diff --git a/samples/native-config-integration/gradle.properties b/samples/native-config-integration/gradle.properties index a35570695..ea067ed6e 100644 --- a/samples/native-config-integration/gradle.properties +++ b/samples/native-config-integration/gradle.properties @@ -1,3 +1,3 @@ -native.gradle.plugin.version = 0.9.19 +native.gradle.plugin.version = 0.9.20 junit.jupiter.version = 5.8.1 junit.platform.version = 1.8.1 diff --git a/samples/native-config-integration/pom.xml b/samples/native-config-integration/pom.xml index bb0c0d56a..df500baf3 100644 --- a/samples/native-config-integration/pom.xml +++ b/samples/native-config-integration/pom.xml @@ -51,8 +51,8 @@ 1.8 UTF-8 - 0.9.19 - 0.9.19 + 0.9.20 + 0.9.20 example-app org.graalvm.example.Application