Skip to content

Commit

Permalink
chore: use Gradle toolchains for JDK provisioning
Browse files Browse the repository at this point in the history
It enables automatic JDK provisioning (e.g. download), and it enables
using different JDKs for executing Gradle and for building JMeter

Java version can be specified when building with -PjdkVersion=11

You could use ./gradlew -q javaToolchains to list the detected toolchains.

See https://docs.gradle.org/8.0/userguide/toolchains.html#sec:consuming

Fixes apache#5986
  • Loading branch information
vlsi committed Jun 19, 2023
1 parent e024bf3 commit bc15d5c
Show file tree
Hide file tree
Showing 13 changed files with 227 additions and 25 deletions.
18 changes: 16 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,19 @@ jobs:
- uses: actions/checkout@v3
with:
fetch-depth: 50
- name: Set up Java ${{ matrix.java_version }}, ${{ matrix.java_distribution }}
- name: Set up Java ${{ matrix.java_version }}, oracle
if: ${{ matrix.oracle_java_website != '' }}
uses: oracle-actions/setup-java@1611a647972adb8b04779be3529a044d650fd510 # v1
with:
website: ${{ matrix.oracle_java_website }}
release: ${{ matrix.java_version }}
- name: Set up Java 17 and ${{ matrix.non_ea_java_version }}, ${{ matrix.java_distribution }}
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java_version }}
# The latest one will be the default, so we use Java 17 for launching Gradle
java-version: |
${{ matrix.non_ea_java_version }}
17
distribution: ${{ matrix.java_distribution }}
architecture: x64
- name: Steps to reproduce
Expand All @@ -68,6 +77,11 @@ jobs:
properties: |
testExtraJvmArgs=${{ matrix.testExtraJvmArgs }}
testDisableCaching=${{ matrix.testDisableCaching }}
jdkBuildVersion=17
jdkTestVersion=${{ matrix.java_version }}
jdkTestVendor=${{ matrix.java_vendor }}
# We provision JDKs with GitHub Actions for caching purposes, so Gradle should rather fail in case JDK is not found
org.gradle.java.installations.auto-download=false
env:
_JAVA_OPTIONS: ${{ matrix.extraJvmArgs }}
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }}
Expand Down
29 changes: 21 additions & 8 deletions .github/workflows/matrix.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,26 @@ const matrix = new MatrixBuilder();
matrix.addAxis({
name: 'java_distribution',
values: [
{value: 'corretto', weight: 1},
{value: 'liberica', weight: 1},
{value: 'microsoft', weight: 1},
{value: 'oracle', weight: 1},
{value: 'semeru', weight: 4},
{value: 'temurin', weight: 1},
{value: 'zulu', weight: 1},
{value: 'corretto', vendor: 'amazon', weight: 1},
{value: 'liberica', vendor: 'bellsoft', weight: 1},
{value: 'microsoft', vendor: 'microsoft', weight: 1},
{value: 'oracle', vendor: 'oracle', weight: 1},
{value: 'semeru', vendor: 'ibm', weight: 4},
{value: 'temurin', vendor: 'eclipse', weight: 1},
{value: 'zulu', vendor: 'azul', weight: 1},
]
});

const eaJava = '21';

matrix.addAxis({
name: 'java_version',
// Strings allow versions like 18-ea
values: [
'8',
'11',
'17',
eaJava,
]
});

Expand Down Expand Up @@ -74,10 +77,13 @@ matrix.setNamePattern(['java_version', 'java_distribution', 'hash', 'os', 'tz',

// Semeru uses OpenJ9 jit which has no option for making hash codes the same
matrix.exclude({java_distribution: {value: 'semeru'}, hash: {value: 'same'}});
// Semeru 8 fails when Semeru 17 is on the PATH (we use 17 for build)
matrix.exclude({java_distribution: {value: 'semeru'}, java_version: '8'});
// Microsoft Java has no distribution for 8
matrix.exclude({java_distribution: {value: 'microsoft'}, java_version: '8'});
// Oracle JDK is only supported for JDK 17 and later
matrix.exclude({java_distribution: {value: 'oracle'}, java_version: ['8', '11']});
matrix.imply({java_version: eaJava}, {java_distribution: {value: 'oracle'}})
// Ensure at least one job with "same" hashcode exists
matrix.generateRow({hash: {value: 'same'}});
// Ensure at least one Windows and at least one Linux job is present (macOS is almost the same as Linux)
Expand All @@ -90,6 +96,8 @@ matrix.generateRow({java_version: "8"});
matrix.generateRow({java_version: "11"});
// Ensure there will be at least one job with Java 17
matrix.generateRow({java_version: "17"});
// Ensure there will be at least one job with Java EA
matrix.generateRow({java_version: eaJava});
const include = matrix.generateRows(process.env.MATRIX_JOBS || 5);
if (include.length === 0) {
throw new Error('Matrix list is empty');
Expand Down Expand Up @@ -124,9 +132,14 @@ include.forEach(v => {
jvmArgs.push(`-Duser.country=${v.locale.country}`);
jvmArgs.push(`-Duser.language=${v.locale.language}`);
v.java_distribution = v.java_distribution.value;
v.java_vendor = v.java_distribution.vendor;
if (v.java_distribution === 'oracle') {
v.oracle_java_website = v.java_version === eaJava ? 'jdk.java.net' : 'oracle.com';
}
v.non_ea_java_version = v.java_version === eaJava ? '' : v.java_version;
if (v.java_distribution !== 'semeru' && Math.random() > 0.5) {
// The following options randomize instruction selection in JIT compiler
// so it might reveal missing synchronization in TestNG code
// so it might reveal missing synchronization
v.name += ', stress JIT';
v.testDisableCaching = 'JIT randomization should not be cached';
jvmArgs.push('-XX:+UnlockDiagnosticVMOptions');
Expand Down
23 changes: 18 additions & 5 deletions .github/workflows/matrix_builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Axis {
}
if (Array.isArray(filter)) {
// e.g. row={os: 'windows'}; filter=[{os: 'linux'}, {os: 'linux'}]
return filter.find(v => Axis.matches(row, v));
return filter.some(v => Axis.matches(row, v));
}
if (typeof filter === 'object') {
// e.g. row={jdk: {name: 'openjdk', version: 8}}; filter={jdk: {version: 8}}
Expand Down Expand Up @@ -68,6 +68,7 @@ class MatrixBuilder {
this.duplicates = {};
this.excludes = [];
this.includes = [];
this.implications = [];
this.failOnUnsatisfiableFilters = false;
}

Expand All @@ -80,13 +81,23 @@ class MatrixBuilder {
}

/**
* Specifies exclude filter (e.g. exclude a forbidden combination)
* Specifies exclude filter (e.g. exclude a forbidden combination).
* @param filter
*/
exclude(filter) {
this.excludes.push(filter);
}

/**
* Adds implication like `antecedent -> consequent`.
* In other words, if `antecedent` holds, then `consequent` must also hold.
* @param antecedent
* @param consequent
*/
imply(antecedent, consequent) {
this.implications.push({antecedent: antecedent, consequent: consequent});
}

addAxis({name, title, values}) {
const axis = new Axis({name, title, values});
this.axes.push(axis);
Expand All @@ -104,8 +115,10 @@ class MatrixBuilder {
* @returns {boolean}
*/
matches(row) {
return (this.excludes.length === 0 || !this.excludes.find(f => Axis.matches(row, f))) &&
(this.includes.length === 0 || this.includes.find(f => Axis.matches(row, f)));
return (this.excludes.length === 0 || !this.excludes.some(f => Axis.matches(row, f))) &&
(this.includes.length === 0 || this.includes.some(f => Axis.matches(row, f))) &&
(this.implications.length === 0 || (
this.implications.every(i => !Axis.matches(row, i.antecedent) || Axis.matches(row, i.consequent))));
}

failOnUnsatisfiableFilters(value) {
Expand All @@ -125,7 +138,7 @@ class MatrixBuilder {
let res;
if (filter) {
// If matching row already exists, no need to generate more
res = this.rows.find(v => Axis.matches(v, filter));
res = this.rows.some(v => Axis.matches(v, filter));
if (res) {
return res;
}
Expand Down
4 changes: 4 additions & 0 deletions build-logic/basics/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@
plugins {
id("build-logic.kotlin-dsl-gradle-plugin")
}

dependencies {
api(projects.buildParameters)
}
33 changes: 33 additions & 0 deletions build-logic/basics/src/main/kotlin/ToolchainProperties.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import buildparameters.BuildParametersExtension

class ToolchainProperties(
val version: Int,
val vendor: String?,
val implementation: String?,
)

val BuildParametersExtension.buildJdk: ToolchainProperties?
get() = jdkBuildVersion.takeIf { it != 0 }
?.let { ToolchainProperties(it, jdkBuildVendor.orNull, jdkBuildImplementation.orNull) }

val BuildParametersExtension.testJdk: ToolchainProperties?
get() = jdkTestVersion.orNull?.takeIf { it != 0 }
?.let { ToolchainProperties(it, jdkTestVendor.orNull, jdkTestImplementation.orNull) }
?: buildJdk
41 changes: 41 additions & 0 deletions build-logic/basics/src/main/kotlin/configureToolchain.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

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

fun JavaToolchainService.launcherFor(jdk: ToolchainProperties): Provider<JavaLauncher> = launcherFor {
configureToolchain(jdk)
}

fun JavaToolchainSpec.configureToolchain(jdk: ToolchainProperties?) {
if (jdk == null) {
return
}
languageVersion.set(JavaLanguageVersion.of(jdk.version))
jdk.vendor?.let {
vendor.set(JvmVendorSpec.matching(it))
}
if (jdk.implementation.equals("J9", ignoreCase = true)) {
implementation.set(JvmImplementation.J9)
}
}
25 changes: 25 additions & 0 deletions build-logic/build-parameters/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,31 @@ buildParameters {
defaultValue.set(false)
description.set("Collect test coverage")
}
integer("targetJavaVersion") {
defaultValue.set(8)
mandatory.set(true)
description.set("Java version for source and target compatibility")
}
integer("jdkBuildVersion") {
defaultValue.set(17)
mandatory.set(true)
description.set("JDK version to use for building JMeter. If the value is 0, then current Java is used. (see https://docs.gradle.org/8.0/userguide/toolchains.html#sec:consuming)")
}
string("jdkBuildVendor") {
description.set("JDK vendor to use building JMeter (see https://docs.gradle.org/8.0/userguide/toolchains.html#sec:vendors)")
}
string("jdkBuildImplementation") {
description.set("Vendor-specific virtual machine implementation to use building JMeter (see https://docs.gradle.org/8.0/userguide/toolchains.html#selecting_toolchains_by_virtual_machine_implementation)")
}
integer("jdkTestVersion") {
description.set("JDK version to use for testing JMeter. If the value is 0, then current Java is used. (see https://docs.gradle.org/8.0/userguide/toolchains.html#sec:consuming)")
}
string("jdkTestVendor") {
description.set("JDK vendor to use testing JMeter (see https://docs.gradle.org/8.0/userguide/toolchains.html#sec:vendors)")
}
string("jdkTestImplementation") {
description.set("Vendor-specific virtual machine implementation to use testing JMeter (see https://docs.gradle.org/8.0/userguide/toolchains.html#selecting_toolchains_by_virtual_machine_implementation)")
}
bool("spotbugs") {
defaultValue.set(false)
description.set("Run SpotBugs verifications")
Expand Down
12 changes: 12 additions & 0 deletions build-logic/jvm/src/main/kotlin/build-logic.groovy.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,20 @@
* limitations under the License.
*/

import com.github.vlsi.gradle.dsl.configureEach

plugins {
id("java")
id("groovy")
id("build-logic.test-spock")
id("com.github.vlsi.gradle-extensions")
id("build-logic.build-params")
}

tasks.configureEach<GroovyCompile> {
buildParameters.testJdk?.let {
javaLauncher.set(javaToolchains.launcherFor(it))
}
// Support jdk-release to configure the target Java release when compiling the bytecode
// See https://issues.apache.org/jira/browse/GROOVY-11105
}
21 changes: 18 additions & 3 deletions build-logic/jvm/src/main/kotlin/build-logic.java.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ plugins {
}

java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
toolchain {
configureToolchain(buildParameters.buildJdk)
}
consistentResolution {
useCompileClasspathVersions()
}
Expand All @@ -44,11 +45,25 @@ tasks.configureEach<JavaCompile> {
// Use --release=8 for Java 10+ so the generated bytecode does not include methods introduced in Java 9+
options.release.set(
provider {
8.takeIf { javaCompiler.get().metadata.languageVersion.asInt() > 9 }
buildParameters.targetJavaVersion.takeIf {
javaCompiler.get().metadata.languageVersion.asInt() > 9
}
}
)
}

tasks.configureEach<JavaExec> {
buildParameters.testJdk?.let {
javaLauncher.set(javaToolchains.launcherFor(it))
}
}

tasks.configureEach<Checkstyle> {
buildParameters.buildJdk?.let {
javaLauncher.set(javaToolchains.launcherFor(it))
}
}

dependencies {
findProject(":src:bom")?.let {
api(platform(it))
Expand Down
13 changes: 12 additions & 1 deletion build-logic/jvm/src/main/kotlin/build-logic.kotlin.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
id("java-library")
id("build-logic.build-params")
id("build-logic.java")
id("build-logic.test-base")
id("com.github.autostyle")
Expand All @@ -36,6 +37,9 @@ kotlin {
if (props.bool("kotlin.explicitApi", default = true)) {
explicitApi()
}
jvmToolchain {
configureToolchain(buildParameters.buildJdk)
}
}

tasks.configureEach<KotlinCompile> {
Expand All @@ -44,7 +48,14 @@ tasks.configureEach<KotlinCompile> {
apiVersion = "kotlin.api".v
}
freeCompilerArgs += "-Xjvm-default=all"
jvmTarget = java.targetCompatibility.toString()
val jdkRelease = buildParameters.targetJavaVersion.let {
when {
it < 9 -> "1.8"
else -> it.toString()
}
}
freeCompilerArgs += "-Xjdk-release=$jdkRelease"
kotlinOptions.jvmTarget = jdkRelease
}
}

Expand Down
Loading

0 comments on commit bc15d5c

Please sign in to comment.