-
Notifications
You must be signed in to change notification settings - Fork 319
Drop the requirement of jvm environment variables for testJvm constraint
#9968
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
35a24a3
470c6e7
c889363
1104f19
a690268
501af3d
2214cb2
e5d8ca2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,88 +1,162 @@ | ||
| package datadog.gradle.plugin.testJvmConstraints | ||
|
|
||
| import org.gradle.kotlin.dsl.support.serviceOf | ||
| import org.gradle.api.GradleException | ||
| import org.gradle.api.Project | ||
| import org.gradle.api.internal.provider.PropertyFactory | ||
| import org.gradle.api.provider.Provider | ||
| import org.gradle.jvm.toolchain.JavaLanguageVersion | ||
| import org.gradle.jvm.toolchain.JavaLauncher | ||
| import org.gradle.jvm.toolchain.JavaToolchainService | ||
| import org.gradle.jvm.toolchain.JavaToolchainSpec | ||
| import org.gradle.jvm.toolchain.JvmImplementation | ||
| import org.gradle.jvm.toolchain.JvmVendorSpec | ||
| import org.gradle.jvm.toolchain.internal.DefaultToolchainSpec | ||
| import org.gradle.jvm.toolchain.internal.SpecificInstallationToolchainSpec | ||
| import org.gradle.kotlin.dsl.support.serviceOf | ||
| import java.nio.file.Files | ||
| import java.nio.file.Path | ||
| import java.nio.file.Paths | ||
|
|
||
|
|
||
| /** | ||
| * Handles the `testJvm` property to resolve a Java launcher for testing. | ||
| * | ||
| * The `testJvm` property can be set via command line or environment variable to specify | ||
| * which JVM to use for running tests. E.g. | ||
| * | ||
| * ```shell | ||
| * ./gradlew test -DtestJvm=ZULU11 | ||
| * ``` | ||
|
Comment on lines
21
to
28
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wish we have TESTING.md with all this useful documentation from this class :)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exactly, I started the kdoc here, so we can reuse this text for such a file. |
||
| * | ||
| * This handles local setup, and CI environment, where the environment variables are defined here: | ||
| * * https://github.com/DataDog/dd-trace-java-docker-build/blob/a4f4bfa9d7fe0708858e595697dc67970a2a458f/Dockerfile#L182-L188 | ||
| * * https://github.com/DataDog/dd-trace-java-docker-build/blob/a4f4bfa9d7fe0708858e595697dc67970a2a458f/Dockerfile#L222-L241 | ||
| */ | ||
| class TestJvmSpec(val project: Project) { | ||
| companion object { | ||
| const val TEST_JVM = "testJvm" | ||
| } | ||
|
|
||
| private val currentJavaHomePath = project.providers.systemProperty("java.home").map { it.normalizeToJDKJavaHome() } | ||
|
|
||
| val testJvmProperty = project.providers.gradleProperty(TEST_JVM) | ||
|
|
||
| val normalizedTestJvm = testJvmProperty.map { testJvm -> | ||
| /** | ||
| * The raw `testJvm` property as passed via command line or environment variable. | ||
| */ | ||
| val testJvmProperty: Provider<String> = project.providers.gradleProperty(TEST_JVM) | ||
|
|
||
| /** | ||
| * Normalized `stable` string to the highest JAVA_X_HOME found in environment variables. | ||
| */ | ||
| val normalizedTestJvm: Provider<String> = testJvmProperty.map { testJvm -> | ||
| if (testJvm.isBlank()) { | ||
| throw GradleException("testJvm property is blank") | ||
| } | ||
|
|
||
| // "stable" is calculated as the largest X found in JAVA_X_HOME | ||
| if (testJvm == "stable") { | ||
| val javaVersions = project.providers.environmentVariablesPrefixedBy("JAVA_").map { javaHomes -> | ||
| javaHomes | ||
| .filter { it.key.matches(Regex("^JAVA_[0-9]+_HOME$")) } | ||
| .map { Regex("^JAVA_(\\d+)_HOME$").find(it.key)!!.groupValues[1].toInt() } | ||
| }.get() | ||
|
|
||
| if (javaVersions.isEmpty()) { | ||
| throw GradleException("No valid JAVA_X_HOME environment variables found.") | ||
| when (testJvm) { | ||
| "stable" -> { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we rename
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm hesitant to that right now, as there were prior discussions on this. Maybe we can have both, like |
||
| val javaVersions = project.providers.environmentVariablesPrefixedBy("JAVA_").map { javaHomes -> | ||
| javaHomes | ||
| .filter { it.key.matches(Regex("^JAVA_[0-9]+_HOME$")) } | ||
| .map { Regex("^JAVA_(\\d+)_HOME$").find(it.key)!!.groupValues[1].toInt() } | ||
| }.get() | ||
|
|
||
| if (javaVersions.isEmpty()) { | ||
| throw GradleException("No valid JAVA_X_HOME environment variables found.") | ||
| } | ||
|
|
||
| javaVersions.max().toString() | ||
| } | ||
|
|
||
| javaVersions.max().toString() | ||
| } else { | ||
| testJvm | ||
| else -> testJvm | ||
| } | ||
| }.map { project.logger.info("normalized testJvm: $it"); it } | ||
|
|
||
| val testJvmHomePath = normalizedTestJvm.map { | ||
| if (Files.exists(Paths.get(it))) { | ||
| it.normalizeToJDKJavaHome() | ||
| } else { | ||
| val matcher = Regex("([a-zA-Z]*)([0-9]+)").find(it) | ||
| if (matcher == null) { | ||
| throw GradleException("Unable to find launcher for Java '$it'. It needs to match '([a-zA-Z]*)([0-9]+)'.") | ||
| } | ||
| val testJvmEnv = "JAVA_${it}_HOME" | ||
| val testJvmHome = project.providers.environmentVariable(testJvmEnv).orNull | ||
| if (testJvmHome == null) { | ||
| throw GradleException("Unable to find launcher for Java '$it'. Have you set '$testJvmEnv'?") | ||
| /** | ||
| * The home path of the test JVM. | ||
| * | ||
| * The `<testJvm>` string (`8`, `11`, `ZULU8`, `GRAALVM25`, etc.) is interpreted in that order: | ||
| * 1. Lookup for a valid path, | ||
| * 2. Look JVM via Gradle toolchains | ||
| * | ||
| * Holds the resolved JavaToolchainSpec for the test JVM. | ||
| */ | ||
| private val testJvmSpec = normalizedTestJvm.map { | ||
| val (distribution, version) = Regex("([a-zA-Z]*)([0-9]+)").matchEntire(it)?.groupValues?.drop(1) ?: listOf("", "") | ||
|
|
||
| when { | ||
| Files.exists(Paths.get(it)) -> it.normalizeToJDKJavaHome().toToolchainSpec() | ||
|
|
||
| version.isNotBlank() -> { | ||
| // Best effort to make a spec for the passed testJvm | ||
| // `8`, `11`, `ZULU8`, `GRAALVM25`, etc. | ||
| // if it is an integer, we assume it's a Java version | ||
| // also we can handle on macOs oracle, zulu, semeru, graalvm prefixes | ||
|
|
||
| // This is using internal APIs | ||
| DefaultToolchainSpec(project.serviceOf<PropertyFactory>()).apply { | ||
| languageVersion.set(JavaLanguageVersion.of(version.toInt())) | ||
| when (distribution.lowercase()) { | ||
| "oracle" -> { | ||
| vendor.set(JvmVendorSpec.ORACLE) | ||
| } | ||
|
|
||
| "zulu" -> { | ||
| vendor.set(JvmVendorSpec.AZUL) | ||
| } | ||
|
|
||
| "semeru" -> { | ||
| vendor.set(JvmVendorSpec.IBM) | ||
| implementation.set(JvmImplementation.J9) | ||
| } | ||
|
|
||
| "graalvm" -> { | ||
| vendor.set(JvmVendorSpec.GRAAL_VM) | ||
| nativeImageCapable.set(true) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| testJvmHome.normalizeToJDKJavaHome() | ||
| else -> throw GradleException( | ||
| """ | ||
| Unable to find launcher for Java '$it'. It needs to be: | ||
| 1. A valid path to a JDK home, or | ||
| 2. An environment variable named 'JAVA_<testJvm>_HOME' or '<testJvm>' pointing to a JDK home, or | ||
| 3. A Java version or a known distribution+version combination (e.g. '11', 'zulu8', 'graalvm11', etc.) that can be resolved via Gradle toolchains. | ||
| 4. If using Gradle toolchains, ensure that the requested JDK is installed and configured correctly. | ||
| """.trimIndent() | ||
| ) | ||
| } | ||
| }.map { project.logger.info("testJvm home path: $it"); it } | ||
|
|
||
| val javaTestLauncher = project.providers.zip(testJvmHomePath, normalizedTestJvm) { testJvmHome, testJvm -> | ||
| // Only change test JVM if it's not the one we are running the gradle build with | ||
| if (currentJavaHomePath.get() == testJvmHome) { | ||
| project.providers.provider<JavaLauncher?> { null } | ||
| } else { | ||
| // This is using internal APIs | ||
| val jvmSpec = org.gradle.jvm.toolchain.internal.SpecificInstallationToolchainSpec( | ||
| project.serviceOf<org.gradle.api.internal.provider.PropertyFactory>(), | ||
| project.file(testJvmHome) | ||
| ) | ||
|
|
||
| // The provider always says that a value is present so we need to wrap it for proper error messages | ||
| project.javaToolchains.launcherFor(jvmSpec).orElse(project.providers.provider { | ||
| throw GradleException("Unable to find launcher for Java $testJvm. Does '$testJvmHome' point to a JDK?") | ||
| }) | ||
| } | ||
| }.flatMap { it }.map { project.logger.info("testJvm launcher: ${it.executablePath}"); it } | ||
| /** | ||
| * The Java launcher for the test JVM. | ||
| * | ||
| * Current JVM or a launcher specified via the testJvm. | ||
| */ | ||
| val javaTestLauncher: Provider<JavaLauncher> = | ||
| project.providers.zip(testJvmSpec, normalizedTestJvm) { jvmSpec, testJvm -> | ||
| // Only change test JVM if it's not the one we are running the gradle build with | ||
| if ((jvmSpec as? SpecificInstallationToolchainSpec)?.javaHome == currentJavaHomePath.get()) { | ||
| project.providers.provider<JavaLauncher?> { null } | ||
| } else { | ||
| // The provider always says that a value is present so we need to wrap it for proper error messages | ||
| project.javaToolchains.launcherFor(jvmSpec).orElse(project.providers.provider { | ||
| throw GradleException("Unable to find launcher for Java '$testJvm'. Does $TEST_JVM point to a JDK?") | ||
| }) | ||
| } | ||
| }.flatMap { it }.map { project.logger.info("testJvm launcher: ${it.executablePath}"); it } | ||
|
|
||
| private fun String.normalizeToJDKJavaHome(): Path { | ||
| val javaHome = project.file(this).toPath().toRealPath() | ||
| return if (javaHome.endsWith("jre")) javaHome.parent else javaHome | ||
| } | ||
|
|
||
| private val Project.javaToolchains: JavaToolchainService get() = | ||
| extensions.getByName("javaToolchains") as JavaToolchainService | ||
| private fun Path.toToolchainSpec(): JavaToolchainSpec = | ||
| // This is using internal APIs | ||
| SpecificInstallationToolchainSpec(project.serviceOf<PropertyFactory>(), project.file(this)) | ||
|
|
||
| private val Project.javaToolchains: JavaToolchainService | ||
| get() = | ||
| extensions.getByName("javaToolchains") as JavaToolchainService | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,8 @@ | ||
| org.gradle.parallel=true | ||
| org.gradle.caching=true | ||
| org.gradle.jvmargs=-XX:MaxMetaspaceSize=1g | ||
| org.gradle.java.installations.auto-detect=false | ||
| org.gradle.java.installations.auto-download=false | ||
| # 8, 11, 17, 21 and 25 are needed to build | ||
| org.gradle.java.installations.fromEnv=JAVA_8_HOME,JAVA_11_HOME,JAVA_17_HOME,JAVA_21_HOME,JAVA_25_HOME | ||
|
|
||
| # Toggle on to get more details during IJ sync | ||
| #org.gradle.logging.level=info | ||
|
|
||
| # CI use constrained org.gradle.java.installations settings |
Uh oh!
There was an error while loading. Please reload this page.