From 8e5122d5a88c14fcf02c1574fab212c621ba833e Mon Sep 17 00:00:00 2001 From: Vladimir Sitnikov Date: Sat, 17 Jun 2023 14:28:54 +0300 Subject: [PATCH] chore: use Gradle toolchains for JDK provisioning 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 https://github.com/apache/jmeter/issues/5986 --- .github/workflows/main.yml | 16 ++++++++++++- .github/workflows/matrix.js | 12 +++++++++- .github/workflows/matrix_builder.js | 23 +++++++++++++++---- build-logic/build-parameters/build.gradle.kts | 16 +++++++++++++ .../main/kotlin/build-logic.java.gradle.kts | 19 ++++++++++++--- .../main/kotlin/build-logic.kotlin.gradle.kts | 21 ++++++++++++++++- settings.gradle.kts | 5 ++++ 7 files changed, 101 insertions(+), 11 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b25c8a08a27..ce457810c20 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -47,9 +47,19 @@ jobs: with: fetch-depth: 50 - name: Set up Java ${{ matrix.java_version }}, ${{ matrix.java_distribution }} + 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 ${{ matrix.java_version }}, ${{ matrix.java_distribution }} + if: ${{ matrix.java_distribution != 'oracle' }} 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 @@ -68,6 +78,10 @@ jobs: properties: | testExtraJvmArgs=${{ matrix.testExtraJvmArgs }} testDisableCaching=${{ matrix.testDisableCaching }} + jdkVersion=${{ matrix.java_version }} + jdkVendor=${{ matrix.java_distribution }} + # 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 }} diff --git a/.github/workflows/matrix.js b/.github/workflows/matrix.js index 20c32abeb71..49451def2bb 100644 --- a/.github/workflows/matrix.js +++ b/.github/workflows/matrix.js @@ -18,6 +18,8 @@ matrix.addAxis({ ] }); +const eaJava = '21'; + matrix.addAxis({ name: 'java_version', // Strings allow versions like 18-ea @@ -25,6 +27,7 @@ matrix.addAxis({ '8', '11', '17', + eaJava, ] }); @@ -78,6 +81,7 @@ matrix.exclude({java_distribution: {value: 'semeru'}, hash: {value: 'same'}}); 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) @@ -90,6 +94,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'); @@ -124,9 +130,13 @@ include.forEach(v => { jvmArgs.push(`-Duser.country=${v.locale.country}`); jvmArgs.push(`-Duser.language=${v.locale.language}`); v.java_distribution = v.java_distribution.value; + 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'); diff --git a/.github/workflows/matrix_builder.js b/.github/workflows/matrix_builder.js index fd765117e8b..0c17190eded 100644 --- a/.github/workflows/matrix_builder.js +++ b/.github/workflows/matrix_builder.js @@ -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}} @@ -68,6 +68,7 @@ class MatrixBuilder { this.duplicates = {}; this.excludes = []; this.includes = []; + this.implications = []; this.failOnUnsatisfiableFilters = false; } @@ -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); @@ -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) { @@ -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; } diff --git a/build-logic/build-parameters/build.gradle.kts b/build-logic/build-parameters/build.gradle.kts index b160323b603..68c9bf54ee6 100644 --- a/build-logic/build-parameters/build.gradle.kts +++ b/build-logic/build-parameters/build.gradle.kts @@ -33,6 +33,22 @@ 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("jdkVersion") { + 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("jdkVendor") { + description.set("JDK vendor to use building JMeter (see https://docs.gradle.org/8.0/userguide/toolchains.html#sec:vendors)") + } + string("jdkImplementation") { + 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)") + } bool("spotbugs") { defaultValue.set(false) description.set("Run SpotBugs verifications") diff --git a/build-logic/jvm/src/main/kotlin/build-logic.java.gradle.kts b/build-logic/jvm/src/main/kotlin/build-logic.java.gradle.kts index df91b33dd10..bd177e5da34 100644 --- a/build-logic/jvm/src/main/kotlin/build-logic.java.gradle.kts +++ b/build-logic/jvm/src/main/kotlin/build-logic.java.gradle.kts @@ -33,8 +33,19 @@ plugins { } java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + buildParameters.jdkVersion.takeIf { it > 0 }?.let { jdkVersion -> + toolchain { + languageVersion.set(JavaLanguageVersion.of(jdkVersion)) + buildParameters.jdkVendor.orNull?.let { + vendor.set(JvmVendorSpec.matching(it)) + } + buildParameters.jdkImplementation.orNull?.let { + if (it.equals("J9", ignoreCase = true)) { + implementation.set(JvmImplementation.J9) + } + } + } + } consistentResolution { useCompileClasspathVersions() } @@ -44,7 +55,9 @@ tasks.configureEach { // 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 + } } ) } diff --git a/build-logic/jvm/src/main/kotlin/build-logic.kotlin.gradle.kts b/build-logic/jvm/src/main/kotlin/build-logic.kotlin.gradle.kts index 628bbda55e4..cbe0311540c 100644 --- a/build-logic/jvm/src/main/kotlin/build-logic.kotlin.gradle.kts +++ b/build-logic/jvm/src/main/kotlin/build-logic.kotlin.gradle.kts @@ -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") @@ -36,6 +37,19 @@ kotlin { if (props.bool("kotlin.explicitApi", default = true)) { explicitApi() } + buildParameters.jdkVersion.takeIf { it > 0 }?.let { jdkVersion -> + jvmToolchain { + languageVersion.set(JavaLanguageVersion.of(jdkVersion)) + buildParameters.jdkVendor.orNull?.let { + vendor.set(JvmVendorSpec.matching(it)) + } + buildParameters.jdkImplementation.orNull?.let { + if (it.equals("J9", ignoreCase = true)) { + implementation.set(JvmImplementation.J9) + } + } + } + } } tasks.configureEach { @@ -44,7 +58,12 @@ tasks.configureEach { apiVersion = "kotlin.api".v } freeCompilerArgs += "-Xjvm-default=all" - jvmTarget = java.targetCompatibility.toString() + kotlinOptions.jvmTarget = buildParameters.targetJavaVersion.let { + when { + it < 9 -> "1.8" + else -> it.toString() + } + } } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 1c613ef91bf..b28f8fce777 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -27,6 +27,7 @@ pluginManagement { plugins { id("com.gradle.enterprise") version "3.13.2" id("com.gradle.common-custom-user-data-gradle-plugin") version "1.10" + id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0" } dependencyResolutionManagement { @@ -164,6 +165,10 @@ val expectedSha512 = mapOf( to "okhttp-4.1.0.jar", "93E7A41BE44CC17FB500EA5CD84D515204C180AEC934491D11FC6A71DAEA761FB0EECEF865D6FD5C3D88AAF55DCE3C2C424BE5BA5D43BEBF48D05F1FA63FA8A7" to "okio-2.2.2.jar", + "B9F87DECE28EABCCEDA58C77C3B602AEAE7A8AEF3D30DA838F4924A620B18C05D9DF86C5876BDE8AB5597C8C0CE808AD083CAF89C3A5AAC60C1E980C6C144A17" + to "foojay-resolver-0.5.0.jar", + "10BF91C79AB151B684834E3CA8BA7D7E19742A3EEB580BDE690FBA433F9FFFE3ABBD79ED3FE3F97986C3A2BADC4D14E28835A8EF89167B4B9CC6014242338769" + to "gson-2.9.1.jar", settings.extra["com.github.vlsi.checksum-dependency.sha512"].toString() to "checksum-dependency-plugin.jar" )