diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 47f966c91cb..eacfa18f98f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -172,10 +172,11 @@ default: - export ORG_GRADLE_PROJECT_mavenRepositoryProxy=$MAVEN_REPOSITORY_PROXY - export ORG_GRADLE_PROJECT_gradlePluginProxy=$GRADLE_PLUGIN_PROXY - | - cat >> gradle.properties <<'EOF' + JAVA_HOMES=$(env | grep -E '^JAVA_[A-Z0-9_]+_HOME=' | sed 's/=.*//' | paste -sd,) + cat >> gradle.properties < + /** + * The raw `testJvm` property as passed via command line or environment variable. + */ + val testJvmProperty: Provider = project.providers.gradleProperty(TEST_JVM) + + /** + * Normalized `stable` string to the highest JAVA_X_HOME found in environment variables. + */ + val normalizedTestJvm: Provider = 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" -> { + 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 `` 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()).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__HOME' or '' 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 { null } - } else { - // This is using internal APIs - val jvmSpec = org.gradle.jvm.toolchain.internal.SpecificInstallationToolchainSpec( - project.serviceOf(), - 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 = + 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 { 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(), project.file(this)) + + private val Project.javaToolchains: JavaToolchainService + get() = + extensions.getByName("javaToolchains") as JavaToolchainService } diff --git a/settings.gradle.kts b/settings.gradle.kts index aaeda03f65b..dcead141778 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,6 +21,7 @@ pluginManagement { plugins { id("com.gradle.develocity") version "4.2.2" + id("org.gradle.toolchains.foojay-resolver-convention") version "0.10.0" } val isCI = providers.environmentVariable("CI")