11package datadog.gradle.plugin.testJvmConstraints
22
3- import org.gradle.kotlin.dsl.support.serviceOf
43import org.gradle.api.GradleException
54import org.gradle.api.Project
5+ import org.gradle.api.internal.provider.PropertyFactory
6+ import org.gradle.api.provider.Provider
7+ import org.gradle.jvm.toolchain.JavaLanguageVersion
68import org.gradle.jvm.toolchain.JavaLauncher
79import org.gradle.jvm.toolchain.JavaToolchainService
10+ import org.gradle.jvm.toolchain.JavaToolchainSpec
11+ import org.gradle.jvm.toolchain.JvmImplementation
12+ import org.gradle.jvm.toolchain.JvmVendorSpec
13+ import org.gradle.jvm.toolchain.internal.DefaultToolchainSpec
14+ import org.gradle.jvm.toolchain.internal.SpecificInstallationToolchainSpec
15+ import org.gradle.kotlin.dsl.support.serviceOf
816import java.nio.file.Files
917import java.nio.file.Path
1018import java.nio.file.Paths
@@ -16,10 +24,16 @@ class TestJvmSpec(val project: Project) {
1624 }
1725
1826 private val currentJavaHomePath = project.providers.systemProperty(" java.home" ).map { it.normalizeToJDKJavaHome() }
19-
20- val testJvmProperty = project.providers.gradleProperty(TEST_JVM )
2127
22- val normalizedTestJvm = testJvmProperty.map { testJvm ->
28+ /* *
29+ * The raw `testJvm` property as passed via command line or environment variable.
30+ */
31+ val testJvmProperty: Provider <String > = project.providers.gradleProperty(TEST_JVM )
32+
33+ /* *
34+ * Normalized `stable` string to the highest JAVA_X_HOME found in environment variables.
35+ */
36+ val normalizedTestJvm: Provider <String > = testJvmProperty.map { testJvm ->
2337 if (testJvm.isBlank()) {
2438 throw GradleException (" testJvm property is blank" )
2539 }
@@ -42,38 +56,92 @@ class TestJvmSpec(val project: Project) {
4256 }
4357 }.map { project.logger.info(" normalized testJvm: $it " ); it }
4458
45- val testJvmHomePath = normalizedTestJvm.map {
46- if (Files .exists(Paths .get(it))) {
47- it.normalizeToJDKJavaHome()
48- } else {
49- val matcher = Regex (" ([a-zA-Z]*)([0-9]+)" ).find(it)
50- if (matcher == null ) {
51- throw GradleException (" Unable to find launcher for Java '$it '. It needs to match '([a-zA-Z]*)([0-9]+)'." )
59+ /* *
60+ * The home path of the test JVM.
61+ *
62+ * The `<testJvm>` string (`8`, `11`, `ZULU8`, `GRAALVM25`, etc.) is interpreted in that order:
63+ * 1. Lookup for a valid path,
64+ * 2. Look for an environment variable named `JAVA_<testJvm>_HOME`
65+ * (e.g. `JAVA_8_HOME`, `JAVA_11_HOME`, etc. but also `JAVA_ZULU8_HOME`, `JAVA_GRAALVM25_HOME`, etc.)
66+ * 3. Look for an environment variable named `<testJvm>`
67+ * 4. Look JVM via Gradle toolchains
68+ *
69+ * Known forms
70+ */
71+ private val testJvmSpec = normalizedTestJvm.map {
72+ // https://github.com/DataDog/dd-trace-java-docker-build/blob/a4f4bfa9d7fe0708858e595697dc67970a2a458f/Dockerfile#L182-L188
73+ // https://github.com/DataDog/dd-trace-java-docker-build/blob/a4f4bfa9d7fe0708858e595697dc67970a2a458f/Dockerfile#L222-L241
74+ val testJvmSpec = when {
75+ Files .exists(Paths .get(it)) -> it.logSource(" existing path" )
76+ Regex (" ([a-zA-Z]*)([0-9]+)" ).find(it) != null ->
77+ project.providers.environmentVariable(" JAVA_${it} _HOME" ).orNull
78+ ?.logSource(" env var JAVA_${it} _HOME" )
79+
80+ project.providers.environmentVariable(it).isPresent ->
81+ project.providers.environmentVariable(it).orNull
82+ ?.logSource(" env var $it " )
83+ else -> null
84+ }?.normalizeToJDKJavaHome()?.toToolchainSpec() ? : run {
85+ // Best effort to make a spec for the passed testJvm
86+ // `8`, `11`, `ZULU8`, `GRAALVM25`, etc.
87+ // if it is an integer, we assume it's a Java version
88+ // also we can handle on macOs oracle, zulu, semeru, graalvm prefixes
89+
90+ val (distribution, version) = Regex (" ([a-zA-Z]*)([0-9]+)" ).matchEntire(it)?.groupValues?.drop(1 ) ? : listOf (" " , " " )
91+ version.ifBlank {
92+ return @run null
5293 }
53- val testJvmEnv = " JAVA_${it} _HOME"
54- val testJvmHome = project.providers.environmentVariable(testJvmEnv).orNull
55- if (testJvmHome == null ) {
56- throw GradleException (" Unable to find launcher for Java '$it '. Have you set '$testJvmEnv '?" )
94+
95+ // This is using internal APIs
96+ DefaultToolchainSpec (project.serviceOf<PropertyFactory >()).apply {
97+ languageVersion.set(JavaLanguageVersion .of(version.toInt()))
98+ when (distribution.lowercase()) {
99+ " oracle" -> {
100+ vendor.set(JvmVendorSpec .ORACLE )
101+ }
102+ " zulu" -> {
103+ vendor.set(JvmVendorSpec .AZUL )
104+ }
105+ " semeru" -> {
106+ vendor.set(JvmVendorSpec .IBM )
107+ implementation.set(JvmImplementation .J9 )
108+ }
109+ " graalvm" -> {
110+ vendor.set(JvmVendorSpec .GRAAL_VM )
111+ nativeImageCapable.set(true )
112+ }
113+ }
57114 }
115+ }
58116
59- testJvmHome.normalizeToJDKJavaHome()
117+ if (testJvmSpec == null ) {
118+ throw GradleException (
119+ """
120+ Unable to find launcher for Java '$it '. It needs to be:
121+ 1. A valid path to a JDK home, or
122+ 2. An environment variable named 'JAVA_<testJvm>_HOME' or '<testJvm>' pointing to a JDK home, or
123+ 3. A Java version or a known distribution+version combination (e.g. '11', 'zulu8', 'graalvm11', etc.) that can be resolved via Gradle toolchains.
124+ 4. If using Gradle toolchains, ensure that the requested JDK is installed and configured correctly.
125+ """ .trimIndent())
60126 }
127+
128+ // The test JVM spec for the toolchain service
129+ testJvmSpec
61130 }.map { project.logger.info(" testJvm home path: $it " ); it }
62131
63- val javaTestLauncher = project.providers.zip(testJvmHomePath, normalizedTestJvm) { testJvmHome, testJvm ->
132+ /* *
133+ * The Java launcher for the test JVM.
134+ *
135+ * Current JVM or a launcher specified via the testJvm.
136+ */
137+ val javaTestLauncher: Provider <JavaLauncher > = project.providers.zip(testJvmSpec, normalizedTestJvm) { jvmSpec, testJvm ->
64138 // Only change test JVM if it's not the one we are running the gradle build with
65- if (currentJavaHomePath.get() == testJvmHome ) {
139+ if ((jvmSpec as ? SpecificInstallationToolchainSpec )?.javaHome == currentJavaHomePath.get() ) {
66140 project.providers.provider<JavaLauncher ?> { null }
67141 } else {
68- // This is using internal APIs
69- val jvmSpec = org.gradle.jvm.toolchain.internal.SpecificInstallationToolchainSpec (
70- project.serviceOf< org.gradle.api.internal.provider.PropertyFactory > (),
71- project.file(testJvmHome)
72- )
73-
74142 // The provider always says that a value is present so we need to wrap it for proper error messages
75143 project.javaToolchains.launcherFor(jvmSpec).orElse(project.providers.provider {
76- throw GradleException (" Unable to find launcher for Java $testJvm . Does ' $testJvmHome ' point to a JDK?" )
144+ throw GradleException (" Unable to find launcher for Java ' $testJvm ' . Does $TEST_JVM point to a JDK?" )
77145 })
78146 }
79147 }.flatMap { it }.map { project.logger.info(" testJvm launcher: ${it.executablePath} " ); it }
@@ -83,6 +151,16 @@ class TestJvmSpec(val project: Project) {
83151 return if (javaHome.endsWith(" jre" )) javaHome.parent else javaHome
84152 }
85153
86- private val Project .javaToolchains: JavaToolchainService get() =
87- extensions.getByName(" javaToolchains" ) as JavaToolchainService
154+ private fun String.logSource (resolutionMode : String ): String {
155+ project.logger.info(" $TEST_JVM =$this resolved from $resolutionMode " )
156+ return this
157+ }
158+
159+ private fun Path.toToolchainSpec () : JavaToolchainSpec =
160+ // This is using internal APIs
161+ SpecificInstallationToolchainSpec (project.serviceOf<PropertyFactory >(), project.file(this ))
162+
163+ private val Project .javaToolchains: JavaToolchainService
164+ get() =
165+ extensions.getByName(" javaToolchains" ) as JavaToolchainService
88166}
0 commit comments