melix / jmh-gradle-plugin

Integrates the JMH benchmarking framework with Gradle

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to use java toolchain with plugin?

austinarbor opened this issue · comments

It looks like some code was added to help use a configured java toolchain (https://github.com/melix/jmh-gradle-plugin/blob/master/src/main/java/me/champeau/jmh/WithJavaToolchain.java), but I can't seem to get it to work.

When I try

tasks.withType(me.champeau.jmh.JmhBytecodeGeneratorTask).configureEach {
    javaLauncher = javaToolchains.launcherFor {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

I get the error > error: invalid source release: 17

When I try

tasks.withType(me.champeau.jmh.JMHTask).configureEach {
    javaLauncher = javaToolchains.launcherFor {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

I get the error
has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 55.0

If I already have jdk17 in the shell, everything works fine. Can you let me know what I am doing wrong to use the toolchain?

Thanks

I also just tried

jmh {
    javaLauncher.set(
            javaToolchains.launcherFor {
                languageVersion = JavaLanguageVersion.of(17)
            }
    )
}

But that gave me the same error as the 2nd attempt above

@austinarbor You may need to set this on the compile tasks as well.

Indeed the toolchain configures the default compileJava task but not the one that is created by the JMH Gradle plugin

This is what what I use (ignore the panama flags, options), to run Gradle with JDK 17 but run benchmarks of JDK 18 code.

Notice this code is snippet is kotlin script.

java {
  toolchain {
    languageVersion.set(JavaLanguageVersion.of(18))
  }
}
val launcher = javaToolchains.launcherFor(java.toolchain).get()

jmh {
  jvm.set(launcher.executablePath.asFile.absolutePath)
}

tasks {
  withType<JavaCompile>().configureEach {
    options.compilerArgs = listOf(
            "--add-modules", "jdk.incubator.foreign"
    )
    options.release.set(18)
    javaCompiler.set(project.javaToolchains.compilerFor(java.toolchain))
  }

  withType<JavaExec>().configureEach {
    environment("JAVA_LIBRARY_PATH", ".:${project.projectDir}/jni")
    jvmArgs("--enable-native-access=ALL-UNNAMED",
            "--add-modules", "jdk.incubator.foreign")
    javaLauncher.set(project.javaToolchains.launcherFor(java.toolchain))
  }

  withType<JmhBytecodeGeneratorTask>().configureEach {
    javaLauncher.set(project.javaToolchains.launcherFor(java.toolchain))
  }
}

@bric3 thanks for the suggestion - unfortunately that resulted in the same error for me
java.lang.UnsupportedClassVersionError: ... has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 52.0

What java version are you using when you run the commands? If I do everything with 17 set in my current shell it works fine - but once I switch to Java 8 to make sure the toolchain is working, it produces an error

Do you have a reproducer?

@bric3 try running ./gradlew jmh on https://github.com/austinarbor/jmh-toolchain-issue with anything lower than 17

Ah of course, I forgot to mention to set the jvm parameter in the jmh configuration, updating my comment above.

So basically in your reproducer you should add. I've kept the repetition of the javaToolchains.launcherFor but you might want to refactor this in a single variable.

diff --git i/build.gradle w/build.gradle
--- i/build.gradle
+++ w/build.gradle
@@ -16,6 +16,25 @@ java {
     }
 }
 
+jmh {
+    jvm = javaToolchains.launcherFor({
+        languageVersion = JavaLanguageVersion.of(17)
+    }).get().executablePath.asFile.absolutePath
+}
+
+tasks.withType(JavaCompile).configureEach {
+    javaCompiler = project.javaToolchains.compilerFor {
+        languageVersion = JavaLanguageVersion.of(17)
+    }
+}
+
+
+tasks.withType(JavaExec).configureEach {
+    javaLauncher = javaToolchains.launcherFor {
+        languageVersion = JavaLanguageVersion.of(17)
+    }
+}
+
 tasks.withType(me.champeau.jmh.JmhBytecodeGeneratorTask).configureEach {
     javaLauncher = javaToolchains.launcherFor {
         languageVersion = JavaLanguageVersion.of(17)

@bric3 thanks, that seems to get it working. I refactored into the below. Unfortunately it's still quite verbose, I wish there was an easier way. Do you know why the JavaCompile configuration is required? Based on gradle docs I thought that was handled automatically by the toolchain

  1. Setup all compile, test and javadoc tasks to use the defined toolchain which may be different than the one Gradle itself uses
def javaVersion = JavaLanguageVersion.of(17)
def compiler = javaToolchains.compilerFor {
    languageVersion = javaVersion
}

def launcher = javaToolchains.launcherFor {
    languageVersion = javaVersion
}

java {
    toolchain {
        languageVersion = javaVersion
    }
}

jmh {
    jvm = launcher.get().executablePath.asFile.absolutePath
}

tasks.withType(JavaCompile).configureEach {
    javaCompiler = compiler
}

tasks.withType(me.champeau.jmh.JmhBytecodeGeneratorTask).configureEach {
    javaLauncher = launcher
}

Do you know why the JavaCompile configuration is required

That's because the toolchain support only configures the default one from the java plugin. I think this a prudent default form the gradle team.

Ah yea this makes total sense. For some reason my brain wasn't grokking that the JMH task was using its own compilation task. Thanks for all the help

I ran into the same issue just now and quickly prototyped #228 to make the interaction with toolchains (in my opinion) more intuitive.