From 03dc8edaf26ba6bc182d8cc2a6d8025a1b9f1cb1 Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Tue, 5 Mar 2024 11:17:44 +0100 Subject: [PATCH] [7.17] Allow resolving runtime java via Gradle tool chain provider (#105916) * [7.17] Resolve runtime java via Gradle tool chain provider #95319 * Cleanup VersionPropertiesPlugin --- .../internal/conventions/GitInfoPlugin.java | 6 +- .../conventions/VersionPropertiesPlugin.java | 39 +++++- build-tools-internal/build.gradle | 4 + build-tools-internal/settings.gradle | 5 + .../internal/info/GlobalBuildInfoPlugin.java | 2 + .../AbstractCustomJavaToolchainResolver.java | 42 ++++++ .../AdoptiumJdkToolchainResolver.java | 125 ++++++++++++++++++ .../ArchivedOracleJdkToolchainResolver.java | 82 ++++++++++++ .../JavaToolChainResolverPlugin.java | 28 ++++ .../OracleOpenJdkToolchainResolver.java | 99 ++++++++++++++ .../AbstractToolchainResolverSpec.groovy | 107 +++++++++++++++ .../AdoptiumJdkToolchainResolverSpec.groovy | 83 ++++++++++++ ...hivedOracleJdkToolchainResolverSpec.groovy | 62 +++++++++ .../OracleOpenJdkToolchainResolverSpec.groovy | 51 +++++++ build-tools/settings.gradle | 4 + .../gradle/JdkDownloadPlugin.java | 8 +- .../gradle/VersionProperties.java | 7 + settings.gradle | 29 +++- 18 files changed, 767 insertions(+), 16 deletions(-) create mode 100644 build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/AbstractCustomJavaToolchainResolver.java create mode 100644 build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/AdoptiumJdkToolchainResolver.java create mode 100644 build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/ArchivedOracleJdkToolchainResolver.java create mode 100644 build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/JavaToolChainResolverPlugin.java create mode 100644 build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/OracleOpenJdkToolchainResolver.java create mode 100644 build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/AbstractToolchainResolverSpec.groovy create mode 100644 build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/AdoptiumJdkToolchainResolverSpec.groovy create mode 100644 build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/ArchivedOracleJdkToolchainResolverSpec.groovy create mode 100644 build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/OracleOpenJdkToolchainResolverSpec.groovy diff --git a/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/GitInfoPlugin.java b/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/GitInfoPlugin.java index db8002ba2afa..a0fb8ee0504a 100644 --- a/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/GitInfoPlugin.java +++ b/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/GitInfoPlugin.java @@ -9,6 +9,7 @@ package org.elasticsearch.gradle.internal.conventions; import org.elasticsearch.gradle.internal.conventions.info.GitInfo; +import org.elasticsearch.gradle.internal.conventions.util.Util; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.model.ObjectFactory; @@ -35,10 +36,7 @@ class GitInfoPlugin implements Plugin { @Override public void apply(Project project) { - File rootDir = (project.getGradle().getParent() == null) ? - project.getRootDir() : - project.getGradle().getParent().getRootProject().getRootDir(); - + File rootDir = Util.locateElasticsearchWorkspace(project.getGradle()); gitInfo = objectFactory.property(GitInfo.class).value(factory.provider(() -> GitInfo.gitInfo(rootDir) )); diff --git a/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/VersionPropertiesPlugin.java b/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/VersionPropertiesPlugin.java index c4c664df46bc..b39d69297fe6 100644 --- a/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/VersionPropertiesPlugin.java +++ b/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/VersionPropertiesPlugin.java @@ -11,20 +11,40 @@ package org.elasticsearch.gradle.internal.conventions; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.provider.Provider; +import org.gradle.api.initialization.IncludedBuild; +import org.gradle.api.invocation.Gradle; import java.io.File; public class VersionPropertiesPlugin implements Plugin { + public static File locateElasticsearchWorkspace(Gradle gradle) { + if (gradle.getRootProject().getName().startsWith("build-tools")) { + File buildToolsParent = gradle.getRootProject().getRootDir().getParentFile(); + if (versionFileExists(buildToolsParent)) { + return buildToolsParent; + } + return buildToolsParent; + } + if (gradle.getParent() == null) { + // See if any of these included builds is the Elasticsearch gradle + for (IncludedBuild includedBuild : gradle.getIncludedBuilds()) { + if (versionFileExists(includedBuild.getProjectDir())) { + return includedBuild.getProjectDir(); + } + } + + // Otherwise assume this gradle is the root elasticsearch workspace + return gradle.getRootProject().getRootDir(); + } else { + // We're an included build, so keep looking + return locateElasticsearchWorkspace(gradle.getParent()); + } + } + @Override public void apply(Project project) { - File workspaceDir; - if (project.getGradle().getIncludedBuilds().isEmpty()) { - // This is an included build, use the parent directory as workspace root - workspaceDir = project.getRootDir().getParentFile(); - } else { - workspaceDir = project.getRootDir(); - } + File workspaceDir = locateElasticsearchWorkspace(project.getGradle()); // Register the service if not done yet File infoPath = new File(workspaceDir, "build-tools-internal"); @@ -34,4 +54,9 @@ public class VersionPropertiesPlugin implements Plugin { }); project.getExtensions().add("versions", serviceProvider.get().getProperties()); } + + private static boolean versionFileExists(File rootDir) { + return new File(rootDir, "build-tools-internal/version.properties").exists(); + } + } diff --git a/build-tools-internal/build.gradle b/build-tools-internal/build.gradle index a7411ebf77d2..cd521bfa8427 100644 --- a/build-tools-internal/build.gradle +++ b/build-tools-internal/build.gradle @@ -108,6 +108,10 @@ gradlePlugin { id = 'elasticsearch.internal-test-rerun' implementationClass = 'org.elasticsearch.gradle.internal.test.rerun.TestRerunPlugin' } + javaToolChainPlugin { + id = 'elasticsearch.java-toolchain' + implementationClass = 'org.elasticsearch.gradle.internal.toolchain.JavaToolChainResolverPlugin' + } javaDoc { id = 'elasticsearch.java-doc' implementationClass = 'org.elasticsearch.gradle.internal.ElasticsearchJavadocPlugin' diff --git a/build-tools-internal/settings.gradle b/build-tools-internal/settings.gradle index 48271149e578..6423750872ca 100644 --- a/build-tools-internal/settings.gradle +++ b/build-tools-internal/settings.gradle @@ -1,3 +1,8 @@ +pluginManagement { + includeBuild "../build-conventions" + includeBuild "../build-tools" +} + dependencyResolutionManagement { versionCatalogs { buildLibs { diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java index fc03ee2de3c7..2d21241b3c7c 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java @@ -128,6 +128,8 @@ public class GlobalBuildInfoPlugin implements Plugin { System.getenv("JENKINS_URL") != null || System.getenv("BUILDKITE_BUILD_URL") != null || System.getProperty("isCI") != null ); params.setDefaultParallel(ParallelDetector.findDefaultParallel(project)); + // TODO: Test if cc issues are coming from here + params.setDefaultParallel(8); params.setInFipsJvm(Util.getBooleanProperty("tests.fips.enabled", false)); params.setIsSnapshotBuild(Util.getBooleanProperty("build.snapshot", true)); AtomicReference cache = new AtomicReference<>(); diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/AbstractCustomJavaToolchainResolver.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/AbstractCustomJavaToolchainResolver.java new file mode 100644 index 000000000000..267af6e4f2b3 --- /dev/null +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/AbstractCustomJavaToolchainResolver.java @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.gradle.internal.toolchain; + +import org.gradle.jvm.toolchain.JavaToolchainResolver; +import org.gradle.jvm.toolchain.JvmVendorSpec; +import org.gradle.platform.Architecture; +import org.gradle.platform.OperatingSystem; + +abstract class AbstractCustomJavaToolchainResolver implements JavaToolchainResolver { + + static String toOsString(OperatingSystem operatingSystem) { + return toOsString(operatingSystem, null); + } + + static String toOsString(OperatingSystem operatingSystem, JvmVendorSpec v) { + return switch (operatingSystem) { + case MAC_OS -> (v == null || v.equals(JvmVendorSpec.ADOPTIUM) == false) ? "macos" : "mac"; + case LINUX -> "linux"; + case WINDOWS -> "windows"; + default -> throw new UnsupportedOperationException("Operating system " + operatingSystem); + }; + } + + static String toArchString(Architecture architecture) { + return switch (architecture) { + case X86_64 -> "x64"; + case AARCH64 -> "aarch64"; + case X86 -> "x86"; + }; + } + + protected static boolean anyVendorOr(JvmVendorSpec givenVendor, JvmVendorSpec expectedVendor) { + return givenVendor.matches("any") || givenVendor.equals(expectedVendor); + } +} diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/AdoptiumJdkToolchainResolver.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/AdoptiumJdkToolchainResolver.java new file mode 100644 index 000000000000..1dd8b0b9d9e2 --- /dev/null +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/AdoptiumJdkToolchainResolver.java @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.gradle.internal.toolchain; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.apache.commons.compress.utils.Lists; +import org.gradle.jvm.toolchain.JavaLanguageVersion; +import org.gradle.jvm.toolchain.JavaToolchainDownload; +import org.gradle.jvm.toolchain.JavaToolchainRequest; +import org.gradle.jvm.toolchain.JvmVendorSpec; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.util.Comparator; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +import static org.gradle.jvm.toolchain.JavaToolchainDownload.fromUri; + +public abstract class AdoptiumJdkToolchainResolver extends AbstractCustomJavaToolchainResolver { + + // package protected for better testing + final Map> CACHED_SEMVERS = new ConcurrentHashMap<>(); + + @Override + public Optional resolve(JavaToolchainRequest request) { + if (requestIsSupported(request) == false) { + return Optional.empty(); + } + AdoptiumVersionRequest versionRequestKey = toVersionRequest(request); + Optional versionInfo = CACHED_SEMVERS.computeIfAbsent( + versionRequestKey, + (r) -> resolveAvailableVersion(versionRequestKey) + ); + + return versionInfo.map(v -> fromUri(resolveDownloadURI(versionRequestKey, v))); + } + + private AdoptiumVersionRequest toVersionRequest(JavaToolchainRequest request) { + String platform = toOsString(request.getBuildPlatform().getOperatingSystem(), JvmVendorSpec.ADOPTIUM); + String arch = toArchString(request.getBuildPlatform().getArchitecture()); + JavaLanguageVersion javaLanguageVersion = request.getJavaToolchainSpec().getLanguageVersion().get(); + return new AdoptiumVersionRequest(platform, arch, javaLanguageVersion); + } + + private Optional resolveAvailableVersion(AdoptiumVersionRequest requestKey) { + ObjectMapper mapper = new ObjectMapper(); + try { + int languageVersion = requestKey.languageVersion.asInt(); + URL source = new URL( + "https://api.adoptium.net/v3/info/release_versions?architecture=" + + requestKey.arch + + "&image_type=jdk&os=" + + requestKey.platform + + "&project=jdk&release_type=ga" + + "&version=[" + + languageVersion + + "," + + (languageVersion + 1) + + ")" + ); + JsonNode jsonNode = mapper.readTree(source); + JsonNode versionsNode = jsonNode.get("versions"); + return Optional.of( + Lists.newArrayList(versionsNode.iterator()) + .stream() + .map(node -> toVersionInfo(node)) + .sorted(Comparator.comparing(AdoptiumVersionInfo::semver).reversed()) + .findFirst() + .get() + ); + } catch (FileNotFoundException e) { + // request combo not supported (e.g. aarch64 + windows + return Optional.empty(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private AdoptiumVersionInfo toVersionInfo(JsonNode node) { + return new AdoptiumVersionInfo( + node.get("build").asInt(), + node.get("major").asInt(), + node.get("minor").asInt(), + node.get("openjdk_version").asText(), + node.get("security").asInt(), + node.get("semver").asText() + ); + } + + private URI resolveDownloadURI(AdoptiumVersionRequest request, AdoptiumVersionInfo versionInfo) { + return URI.create( + "https://api.adoptium.net/v3/binary/version/jdk-" + + versionInfo.openjdkVersion + + "/" + + request.platform + + "/" + + request.arch + + "/jdk/hotspot/normal/eclipse?project=jdk" + ); + } + + /** + * Check if request can be full-filled by this resolver: + * 1. vendor must be "any" or adoptium + */ + private boolean requestIsSupported(JavaToolchainRequest request) { + return anyVendorOr(request.getJavaToolchainSpec().getVendor().get(), JvmVendorSpec.ADOPTIUM); + } + + record AdoptiumVersionInfo(int build, int major, int minor, String openjdkVersion, int security, String semver) {} + + record AdoptiumVersionRequest(String platform, String arch, JavaLanguageVersion languageVersion) {} +} diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/ArchivedOracleJdkToolchainResolver.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/ArchivedOracleJdkToolchainResolver.java new file mode 100644 index 000000000000..7965a1840879 --- /dev/null +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/ArchivedOracleJdkToolchainResolver.java @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.gradle.internal.toolchain; + +import org.apache.groovy.util.Maps; +import org.elasticsearch.gradle.VersionProperties; +import org.gradle.jvm.toolchain.JavaLanguageVersion; +import org.gradle.jvm.toolchain.JavaToolchainDownload; +import org.gradle.jvm.toolchain.JavaToolchainRequest; +import org.gradle.jvm.toolchain.JavaToolchainSpec; +import org.gradle.jvm.toolchain.JvmVendorSpec; +import org.gradle.platform.Architecture; +import org.gradle.platform.BuildPlatform; +import org.gradle.platform.OperatingSystem; + +import java.net.URI; +import java.util.Map; +import java.util.Optional; + +public abstract class ArchivedOracleJdkToolchainResolver extends AbstractCustomJavaToolchainResolver { + + private static final Map ARCHIVED_BASE_VERSIONS = Maps.of(19, "19.0.2", 18, "18.0.2.1", 17, "17.0.7"); + + @Override + public Optional resolve(JavaToolchainRequest request) { + if (requestIsSupported(request) == false) { + return Optional.empty(); + } + Integer majorVersion = request.getJavaToolchainSpec().getLanguageVersion().get().asInt(); + String baseVersion = ARCHIVED_BASE_VERSIONS.get(majorVersion); + if (baseVersion == null) { + return Optional.empty(); + } + + OperatingSystem operatingSystem = request.getBuildPlatform().getOperatingSystem(); + String extension = operatingSystem.equals(OperatingSystem.WINDOWS) ? "zip" : "tar.gz"; + String arch = toArchString(request.getBuildPlatform().getArchitecture()); + String os = toOsString(operatingSystem); + return Optional.of( + () -> URI.create( + "https://download.oracle.com/java/" + + majorVersion + + "/archive/jdk-" + + baseVersion + + "_" + + os + + "-" + + arch + + "_bin." + + extension + ) + ); + } + + /** + * Check if request can be full-filled by this resolver: + * 1. language version not matching bundled jdk version + * 2. vendor must be any or oracle + * 3. Aarch64 windows images are not supported + */ + private boolean requestIsSupported(JavaToolchainRequest request) { + JavaToolchainSpec javaToolchainSpec = request.getJavaToolchainSpec(); + JavaLanguageVersion bundledJdkMajorVersion = JavaLanguageVersion.of(VersionProperties.getBundledJdkMajorVersion()); + if (javaToolchainSpec.getLanguageVersion().get().equals(bundledJdkMajorVersion)) { + return false; + } + if (anyVendorOr(javaToolchainSpec.getVendor().get(), JvmVendorSpec.ORACLE) == false) { + return false; + } + BuildPlatform buildPlatform = request.getBuildPlatform(); + Architecture architecture = buildPlatform.getArchitecture(); + OperatingSystem operatingSystem = buildPlatform.getOperatingSystem(); + return Architecture.AARCH64 != architecture || OperatingSystem.WINDOWS != operatingSystem; + } + +} diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/JavaToolChainResolverPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/JavaToolChainResolverPlugin.java new file mode 100644 index 000000000000..c24b23477811 --- /dev/null +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/JavaToolChainResolverPlugin.java @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.gradle.internal.toolchain; + +import org.gradle.api.Plugin; +import org.gradle.api.initialization.Settings; +import org.gradle.jvm.toolchain.JavaToolchainResolverRegistry; + +import javax.inject.Inject; + +public abstract class JavaToolChainResolverPlugin implements Plugin { + @Inject + protected abstract JavaToolchainResolverRegistry getToolchainResolverRegistry(); + + public void apply(Settings settings) { + settings.getPlugins().apply("jvm-toolchain-management"); + JavaToolchainResolverRegistry registry = getToolchainResolverRegistry(); + registry.register(OracleOpenJdkToolchainResolver.class); + registry.register(AdoptiumJdkToolchainResolver.class); + registry.register(ArchivedOracleJdkToolchainResolver.class); + } +} diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/OracleOpenJdkToolchainResolver.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/OracleOpenJdkToolchainResolver.java new file mode 100644 index 000000000000..e29fdc109a10 --- /dev/null +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/OracleOpenJdkToolchainResolver.java @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.gradle.internal.toolchain; + +import org.elasticsearch.gradle.VersionProperties; +import org.gradle.jvm.toolchain.JavaLanguageVersion; +import org.gradle.jvm.toolchain.JavaToolchainDownload; +import org.gradle.jvm.toolchain.JavaToolchainRequest; +import org.gradle.jvm.toolchain.JavaToolchainSpec; +import org.gradle.jvm.toolchain.JvmVendorSpec; +import org.gradle.platform.Architecture; +import org.gradle.platform.BuildPlatform; +import org.gradle.platform.OperatingSystem; + +import java.net.URI; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class OracleOpenJdkToolchainResolver extends AbstractCustomJavaToolchainResolver { + + private static final Pattern VERSION_PATTERN = Pattern.compile( + "(\\d+)(\\.\\d+\\.\\d+(?:\\.\\d+)?)?\\+(\\d+(?:\\.\\d+)?)(@([a-f0-9]{32}))?" + ); + + // for testing reasons we keep that a package private field + String bundledJdkVersion = VersionProperties.getBundledJdkVersion(); + JavaLanguageVersion bundledJdkMajorVersion = JavaLanguageVersion.of(VersionProperties.getBundledJdkMajorVersion()); + + /** + * We need some place to map JavaLanguageVersion to build, minor version etc. + * */ + @Override + public Optional resolve(JavaToolchainRequest request) { + if (requestIsSupported(request) == false) { + return Optional.empty(); + } + Matcher jdkVersionMatcher = VERSION_PATTERN.matcher(bundledJdkVersion); + if (jdkVersionMatcher.matches() == false) { + throw new IllegalStateException("Unable to parse bundled JDK version " + bundledJdkVersion); + } + String baseVersion = jdkVersionMatcher.group(1) + (jdkVersionMatcher.group(2) != null ? (jdkVersionMatcher.group(2)) : ""); + String build = jdkVersionMatcher.group(3); + String hash = jdkVersionMatcher.group(5); + + OperatingSystem operatingSystem = request.getBuildPlatform().getOperatingSystem(); + String extension = operatingSystem.equals(OperatingSystem.WINDOWS) ? "zip" : "tar.gz"; + String arch = toArchString(request.getBuildPlatform().getArchitecture()); + String os = toOsString(operatingSystem); + return Optional.of( + () -> URI.create( + "https://download.oracle.com/java/GA/jdk" + + baseVersion + + "/" + + hash + + "/" + + build + + "/GPL/openjdk-" + + baseVersion + + "_" + + os + + "-" + + arch + + "_bin." + + extension + ) + ); + } + + /** + * Check if request can be full-filled by this resolver: + * 1. BundledJdkVendor should match openjdk + * 2. language version should match bundled jdk version + * 3. vendor must be any or oracle + * 4. Aarch64 windows images are not supported + */ + private boolean requestIsSupported(JavaToolchainRequest request) { + if (VersionProperties.getBundledJdkVendor().toLowerCase().equals("openjdk") == false) { + return false; + } + JavaToolchainSpec javaToolchainSpec = request.getJavaToolchainSpec(); + if (javaToolchainSpec.getLanguageVersion().get().equals(bundledJdkMajorVersion) == false) { + return false; + } + if (anyVendorOr(javaToolchainSpec.getVendor().get(), JvmVendorSpec.ORACLE) == false) { + return false; + } + BuildPlatform buildPlatform = request.getBuildPlatform(); + Architecture architecture = buildPlatform.getArchitecture(); + OperatingSystem operatingSystem = buildPlatform.getOperatingSystem(); + return Architecture.AARCH64 != architecture || OperatingSystem.WINDOWS != operatingSystem; + } +} diff --git a/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/AbstractToolchainResolverSpec.groovy b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/AbstractToolchainResolverSpec.groovy new file mode 100644 index 000000000000..de0d969c730d --- /dev/null +++ b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/AbstractToolchainResolverSpec.groovy @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.gradle.internal.toolchain + +import org.gradle.api.provider.Property +import org.gradle.jvm.toolchain.JavaLanguageVersion +import org.gradle.jvm.toolchain.JavaToolchainDownload +import org.gradle.jvm.toolchain.JavaToolchainRequest +import org.gradle.jvm.toolchain.JavaToolchainResolver +import org.gradle.jvm.toolchain.JavaToolchainSpec +import org.gradle.jvm.toolchain.JvmVendorSpec +import org.gradle.platform.Architecture +import org.gradle.platform.BuildPlatform +import org.gradle.platform.OperatingSystem +import spock.lang.Specification + +import static org.gradle.platform.Architecture.X86_64 +import static org.gradle.platform.OperatingSystem.MAC_OS + +abstract class AbstractToolchainResolverSpec extends Specification { + + def "resolves #os #arch #vendor jdk #langVersion"() { + given: + def resolver = resolverImplementation() + + when: + Optional download = resolver.resolve(request(JavaLanguageVersion.of(langVersion), vendor, platform(os, arch))) + + then: + download.get().uri == URI.create(expectedUrl) + where: + + [langVersion, vendor, os, arch, expectedUrl] << supportedRequests() + } + + + def "does not resolve #os #arch #vendor jdk #langVersion"() { + given: + def resolver = resolverImplementation() + + when: + Optional download = resolver.resolve(request(JavaLanguageVersion.of(langVersion), vendor, platform(os, arch))) + + then: + download.isEmpty() + where: + [langVersion, vendor, os, arch] << unsupportedRequests() + } + + abstract JavaToolchainResolver resolverImplementation(); + + abstract supportedRequests(); + + abstract unsupportedRequests(); + + JavaToolchainRequest request(JavaLanguageVersion languageVersion = null, + JvmVendorSpec vendorSpec = anyVendor(), + BuildPlatform platform = platform()) { + + JavaToolchainSpec toolchainSpec = Mock() + Property languageVersionProperty = Mock() + _ * toolchainSpec.getLanguageVersion() >> languageVersionProperty + _ * languageVersionProperty.get() >> languageVersion + + Property vendorSpecProperty = Mock() + _ * vendorSpecProperty.get() >> vendorSpec + _ * toolchainSpec.getVendor() >> vendorSpecProperty + + JavaToolchainRequest request = Mock() + + _ * request.getJavaToolchainSpec() >> toolchainSpec + _ * request.getBuildPlatform() >> platform + return request + } + + JvmVendorSpec anyVendor() { + return new AnyJvmVendorSpec(); + } + + BuildPlatform platform(OperatingSystem os = MAC_OS, Architecture arch = X86_64) { + return new TestBuildPlatform(operatingSystem: os, architecture: arch) + } + + + static class TestBuildPlatform implements BuildPlatform { + OperatingSystem operatingSystem + Architecture architecture + } + + static class AnyJvmVendorSpec extends JvmVendorSpec { + @Override + boolean matches(String vendor) { + return vendor == "any" + } + + @Override + String toString() { + return "any" + } + } +} diff --git a/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/AdoptiumJdkToolchainResolverSpec.groovy b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/AdoptiumJdkToolchainResolverSpec.groovy new file mode 100644 index 000000000000..7b8129f8dbae --- /dev/null +++ b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/AdoptiumJdkToolchainResolverSpec.groovy @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.gradle.internal.toolchain + +import org.gradle.api.services.BuildServiceParameters +import org.gradle.jvm.toolchain.JavaLanguageVersion +import org.gradle.jvm.toolchain.JavaToolchainResolver +import org.gradle.platform.OperatingSystem + +import static org.elasticsearch.gradle.internal.toolchain.AbstractCustomJavaToolchainResolver.toArchString +import static org.elasticsearch.gradle.internal.toolchain.AbstractCustomJavaToolchainResolver.toOsString +import static org.gradle.jvm.toolchain.JvmVendorSpec.ADOPTIUM +import static org.gradle.platform.Architecture.AARCH64 +import static org.gradle.platform.Architecture.X86_64 +import static org.gradle.platform.OperatingSystem.LINUX +import static org.gradle.platform.OperatingSystem.MAC_OS +import static org.gradle.platform.OperatingSystem.WINDOWS + +class AdoptiumJdkToolchainResolverSpec extends AbstractToolchainResolverSpec { + + @Override + JavaToolchainResolver resolverImplementation() { + def resolver = new AdoptiumJdkToolchainResolver() { + @Override + BuildServiceParameters.None getParameters() { + return null + } + } + supportedRequests().each { + def languageVersion = JavaLanguageVersion.of(it[0]) + def request = new AdoptiumJdkToolchainResolver.AdoptiumVersionRequest( + toOsString(it[2], it[1]), + toArchString(it[3]), + languageVersion); + resolver.CACHED_SEMVERS.put(request, Optional.of(new AdoptiumJdkToolchainResolver.AdoptiumVersionInfo(languageVersion.asInt(), + 1, + 1, + "" + languageVersion.asInt() + ".1.1.1+37", + 0, "" + languageVersion.asInt() + ".1.1.1" + ))) + + } + return resolver + } + + @Override + def supportedRequests() { + return [ + [19, ADOPTIUM, MAC_OS, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-19.1.1.1+37/mac/x64/jdk/hotspot/normal/eclipse?project=jdk"], + [19, ADOPTIUM, LINUX, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-19.1.1.1+37/linux/x64/jdk/hotspot/normal/eclipse?project=jdk"], + [19, ADOPTIUM, WINDOWS, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-19.1.1.1+37/windows/x64/jdk/hotspot/normal/eclipse?project=jdk"], + [19, ADOPTIUM, MAC_OS, AARCH64, "https://api.adoptium.net/v3/binary/version/jdk-19.1.1.1+37/mac/aarch64/jdk/hotspot/normal/eclipse?project=jdk"], + [19, ADOPTIUM, LINUX, AARCH64, "https://api.adoptium.net/v3/binary/version/jdk-19.1.1.1+37/linux/aarch64/jdk/hotspot/normal/eclipse?project=jdk"], + + [18, ADOPTIUM, MAC_OS, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-18.1.1.1+37/mac/x64/jdk/hotspot/normal/eclipse?project=jdk"], + [18, ADOPTIUM, LINUX, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-18.1.1.1+37/linux/x64/jdk/hotspot/normal/eclipse?project=jdk"], + [18, ADOPTIUM, WINDOWS, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-18.1.1.1+37/windows/x64/jdk/hotspot/normal/eclipse?project=jdk"], + [18, ADOPTIUM, MAC_OS, AARCH64, "https://api.adoptium.net/v3/binary/version/jdk-18.1.1.1+37/mac/aarch64/jdk/hotspot/normal/eclipse?project=jdk"], + [18, ADOPTIUM, LINUX, AARCH64, "https://api.adoptium.net/v3/binary/version/jdk-18.1.1.1+37/linux/aarch64/jdk/hotspot/normal/eclipse?project=jdk"], + [17, ADOPTIUM, MAC_OS, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-17.1.1.1+37/mac/x64/jdk/hotspot/normal/eclipse?project=jdk"], + [17, ADOPTIUM, LINUX, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-17.1.1.1+37/linux/x64/jdk/hotspot/normal/eclipse?project=jdk"], + [17, ADOPTIUM, WINDOWS, X86_64, "https://api.adoptium.net/v3/binary/version/jdk-17.1.1.1+37/windows/x64/jdk/hotspot/normal/eclipse?project=jdk"], + [17, ADOPTIUM, MAC_OS, AARCH64, "https://api.adoptium.net/v3/binary/version/jdk-17.1.1.1+37/mac/aarch64/jdk/hotspot/normal/eclipse?project=jdk"], + [17, ADOPTIUM, LINUX, AARCH64, "https://api.adoptium.net/v3/binary/version/jdk-17.1.1.1+37/linux/aarch64/jdk/hotspot/normal/eclipse?project=jdk"] + ] + } + + @Override + def unsupportedRequests() { + [ + [19, ADOPTIUM, WINDOWS, AARCH64], + [18, ADOPTIUM, WINDOWS, AARCH64], + [17, ADOPTIUM, WINDOWS, AARCH64] + ] + } + +} diff --git a/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/ArchivedOracleJdkToolchainResolverSpec.groovy b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/ArchivedOracleJdkToolchainResolverSpec.groovy new file mode 100644 index 000000000000..7ad97480e5f2 --- /dev/null +++ b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/ArchivedOracleJdkToolchainResolverSpec.groovy @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.gradle.internal.toolchain + +import org.gradle.api.services.BuildServiceParameters +import org.gradle.jvm.toolchain.JavaToolchainResolver; + +import static org.gradle.jvm.toolchain.JvmVendorSpec.ORACLE +import static org.gradle.platform.Architecture.AARCH64 +import static org.gradle.platform.Architecture.X86_64 +import static org.gradle.platform.OperatingSystem.LINUX +import static org.gradle.platform.OperatingSystem.MAC_OS +import static org.gradle.platform.OperatingSystem.WINDOWS; + +class ArchivedOracleJdkToolchainResolverSpec extends AbstractToolchainResolverSpec { + + @Override + def supportedRequests() { + return [ + [19, ORACLE, MAC_OS, X86_64, "https://download.oracle.com/java/19/archive/jdk-19.0.2_macos-x64_bin.tar.gz"], + [19, ORACLE, MAC_OS, AARCH64, "https://download.oracle.com/java/19/archive/jdk-19.0.2_macos-aarch64_bin.tar.gz"], + [19, ORACLE, LINUX, X86_64, "https://download.oracle.com/java/19/archive/jdk-19.0.2_linux-x64_bin.tar.gz"], + [19, ORACLE, LINUX, AARCH64, "https://download.oracle.com/java/19/archive/jdk-19.0.2_linux-aarch64_bin.tar.gz"], + [19, ORACLE, WINDOWS, X86_64, "https://download.oracle.com/java/19/archive/jdk-19.0.2_windows-x64_bin.zip"], + + [18, ORACLE, MAC_OS, X86_64, "https://download.oracle.com/java/18/archive/jdk-18.0.2.1_macos-x64_bin.tar.gz"], + [18, ORACLE, MAC_OS, AARCH64, "https://download.oracle.com/java/18/archive/jdk-18.0.2.1_macos-aarch64_bin.tar.gz"], + [18, ORACLE, LINUX, X86_64, "https://download.oracle.com/java/18/archive/jdk-18.0.2.1_linux-x64_bin.tar.gz"], + [18, ORACLE, LINUX, AARCH64, "https://download.oracle.com/java/18/archive/jdk-18.0.2.1_linux-aarch64_bin.tar.gz"], + [18, ORACLE, WINDOWS, X86_64, "https://download.oracle.com/java/18/archive/jdk-18.0.2.1_windows-x64_bin.zip"], + + [17, ORACLE, MAC_OS, X86_64, "https://download.oracle.com/java/17/archive/jdk-17.0.7_macos-x64_bin.tar.gz"], + [17, ORACLE, MAC_OS, AARCH64, "https://download.oracle.com/java/17/archive/jdk-17.0.7_macos-aarch64_bin.tar.gz"], + [17, ORACLE, LINUX, X86_64, "https://download.oracle.com/java/17/archive/jdk-17.0.7_linux-x64_bin.tar.gz"], + [17, ORACLE, LINUX, AARCH64, "https://download.oracle.com/java/17/archive/jdk-17.0.7_linux-aarch64_bin.tar.gz"], + [17, ORACLE, WINDOWS, X86_64, "https://download.oracle.com/java/17/archive/jdk-17.0.7_windows-x64_bin.zip"] + ] + } + + def unsupportedRequests() { + [ + [19, ORACLE, WINDOWS, AARCH64], + [18, ORACLE, WINDOWS, AARCH64], + [17, ORACLE, WINDOWS, AARCH64] + ] + } + + JavaToolchainResolver resolverImplementation() { + new ArchivedOracleJdkToolchainResolver() { + @Override + BuildServiceParameters.None getParameters() { + return null + } + } + } +} diff --git a/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/OracleOpenJdkToolchainResolverSpec.groovy b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/OracleOpenJdkToolchainResolverSpec.groovy new file mode 100644 index 000000000000..b49e734c087c --- /dev/null +++ b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/OracleOpenJdkToolchainResolverSpec.groovy @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.gradle.internal.toolchain + + +import org.gradle.api.services.BuildServiceParameters +import org.gradle.jvm.toolchain.JavaLanguageVersion +import static org.gradle.jvm.toolchain.JvmVendorSpec.ORACLE +import static org.gradle.platform.Architecture.* +import static org.gradle.platform.OperatingSystem.* + +class OracleOpenJdkToolchainResolverSpec extends AbstractToolchainResolverSpec { + + OracleOpenJdkToolchainResolver resolverImplementation() { + var toolChain = new OracleOpenJdkToolchainResolver() { + @Override + BuildServiceParameters.None getParameters() { + return null + } + } + toolChain.bundledJdkVersion = "20+36@bdc68b4b9cbc4ebcb30745c85038d91d" + toolChain.bundledJdkMajorVersion = JavaLanguageVersion.of(20) + toolChain + } + + def supportedRequests() { + [[20, ORACLE, MAC_OS, X86_64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_macos-x64_bin.tar.gz"], + [20, ORACLE, MAC_OS, AARCH64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_macos-aarch64_bin.tar.gz"], + [20, ORACLE, LINUX, X86_64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_linux-x64_bin.tar.gz"], + [20, ORACLE, LINUX, AARCH64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_linux-aarch64_bin.tar.gz"], + [20, ORACLE, WINDOWS, X86_64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_windows-x64_bin.zip"], + [20, anyVendor(), MAC_OS, X86_64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_macos-x64_bin.tar.gz"], + [20, anyVendor(), MAC_OS, AARCH64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_macos-aarch64_bin.tar.gz"], + [20, anyVendor(), LINUX, X86_64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_linux-x64_bin.tar.gz"], + [20, anyVendor(), LINUX, AARCH64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_linux-aarch64_bin.tar.gz"], + [20, anyVendor(), WINDOWS, X86_64, "https://download.oracle.com/java/GA/jdk20/bdc68b4b9cbc4ebcb30745c85038d91d/36/GPL/openjdk-20_windows-x64_bin.zip"]] + } + + def unsupportedRequests() { + [ + [20, ORACLE, WINDOWS, AARCH64] + ] + } + +} diff --git a/build-tools/settings.gradle b/build-tools/settings.gradle index 4c7fb225b12a..63d80efcd505 100644 --- a/build-tools/settings.gradle +++ b/build-tools/settings.gradle @@ -5,6 +5,10 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ +pluginManagement { + includeBuild "../build-conventions" +} + include 'reaper' dependencyResolutionManagement { diff --git a/build-tools/src/main/java/org/elasticsearch/gradle/JdkDownloadPlugin.java b/build-tools/src/main/java/org/elasticsearch/gradle/JdkDownloadPlugin.java index bcca5333d759..5ea44e906f5f 100644 --- a/build-tools/src/main/java/org/elasticsearch/gradle/JdkDownloadPlugin.java +++ b/build-tools/src/main/java/org/elasticsearch/gradle/JdkDownloadPlugin.java @@ -22,6 +22,13 @@ import org.gradle.api.attributes.Attribute; import java.util.Arrays; +/** + * @deprecated We wanna get rid from this and custom jdk downloads via this plugin and + * make leverage the gradle toolchain resolver capabilities. + * + * @See @org.elasticsearch.gradle.internal.toolchain.JavaToolChainResolverPlugin + * */ +@Deprecated public class JdkDownloadPlugin implements Plugin { public static final String VENDOR_ADOPTIUM = "adoptium"; @@ -161,7 +168,6 @@ public class JdkDownloadPlugin implements Plugin { private static String dependencyNotation(Jdk jdk) { String platformDep = isJdkOnMacOsPlatform(jdk) ? (jdk.getVendor().equals(VENDOR_ADOPTIUM) ? "mac" : "macos") : jdk.getPlatform(); String extension = jdk.getPlatform().equals("windows") ? "zip" : "tar.gz"; - return groupName(jdk) + ":" + platformDep + ":" + jdk.getBaseVersion() + ":" + jdk.getArchitecture() + "@" + extension; } diff --git a/build-tools/src/main/java/org/elasticsearch/gradle/VersionProperties.java b/build-tools/src/main/java/org/elasticsearch/gradle/VersionProperties.java index 895d0c60198e..c4312d113ffd 100644 --- a/build-tools/src/main/java/org/elasticsearch/gradle/VersionProperties.java +++ b/build-tools/src/main/java/org/elasticsearch/gradle/VersionProperties.java @@ -30,6 +30,10 @@ public class VersionProperties { return lucene; } + public static String getBundledJdkMajorVersion() { + return bundledJdkMajorVersion; + } + public static String getBundledJdkVersion() { return bundledJdkVersion; } @@ -45,7 +49,9 @@ public class VersionProperties { private static final String elasticsearch; private static final String lucene; private static final String bundledJdkVersion; + private static final String bundledJdkMajorVersion; private static final String bundledJdkVendor; + private static final Map versions = new HashMap(); static { @@ -54,6 +60,7 @@ public class VersionProperties { lucene = props.getProperty("lucene"); bundledJdkVendor = props.getProperty("bundled_jdk_vendor"); bundledJdkVersion = props.getProperty("bundled_jdk"); + bundledJdkMajorVersion = bundledJdkVersion.split("[.+]")[0]; for (String property : props.stringPropertyNames()) { versions.put(property, props.getProperty(property)); diff --git a/settings.gradle b/settings.gradle index 0c923a9aa3e0..2cbe2f841daf 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,18 +1,23 @@ +import org.elasticsearch.gradle.internal.toolchain.OracleOpenJdkToolchainResolver +import org.elasticsearch.gradle.internal.toolchain.ArchivedOracleJdkToolchainResolver +import org.elasticsearch.gradle.internal.toolchain.AdoptiumJdkToolchainResolver + pluginManagement { repositories { mavenCentral() gradlePluginPortal() } + + includeBuild "build-conventions" + includeBuild "build-tools" + includeBuild "build-tools-internal" } plugins { id "com.gradle.enterprise" version "3.16.1" + id 'elasticsearch.java-toolchain' } -includeBuild "build-conventions" -includeBuild "build-tools" -includeBuild "build-tools-internal" - rootProject.name = "elasticsearch" dependencyResolutionManagement { @@ -23,6 +28,22 @@ dependencyResolutionManagement { } } +toolchainManagement { + jvm { + javaRepositories { + repository('bundledOracleOpendJdk') { + resolverClass = OracleOpenJdkToolchainResolver + } + repository('adoptiumJdks') { + resolverClass = AdoptiumJdkToolchainResolver + } + repository('archivedOracleJdks') { + resolverClass = ArchivedOracleJdkToolchainResolver + } + } + } +} + List projects = [ 'rest-api-spec', 'docs',