Skip to content

Commit 1104f19

Browse files
committed
feat: Allow discovery of testJvm without environment variables
1 parent c889363 commit 1104f19

File tree

2 files changed

+106
-27
lines changed

2 files changed

+106
-27
lines changed
Lines changed: 105 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
package datadog.gradle.plugin.testJvmConstraints
22

3-
import org.gradle.kotlin.dsl.support.serviceOf
43
import org.gradle.api.GradleException
54
import 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
68
import org.gradle.jvm.toolchain.JavaLauncher
79
import 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
816
import java.nio.file.Files
917
import java.nio.file.Path
1018
import 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
}

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pluginManagement {
2121

2222
plugins {
2323
id("com.gradle.develocity") version "4.2.2"
24+
id("org.gradle.toolchains.foojay-resolver-convention") version "0.10.0"
2425
}
2526

2627
val isCI = providers.environmentVariable("CI")

0 commit comments

Comments
 (0)