Message-ID: From: "vlsi (@vlsi)" To: "pgjdbc/pgjdbc" Date: Thu, 04 Jun 2026 07:36:49 +0000 Subject: [pgjdbc/pgjdbc] PR #4153: build: 42.5 compile with a Java 17 toolchain, run tests on multiple JDKs List-Id: X-GitHub-Additions: 76 X-GitHub-Author-Id: 213894 X-GitHub-Author-Login: vlsi X-GitHub-Base: release/42.5.x X-GitHub-Changed-Files: 4 X-GitHub-Commits: 1 X-GitHub-Deletions: 20 X-GitHub-Head-Branch: claude/toolchain-42.5 X-GitHub-Head-SHA: 49fba5489f42685f5d84aa1f471669dd2f934424 X-GitHub-Issue: 4153 X-GitHub-Merge-SHA: 2b1060ddd635a059787944abee4981b564cef98e X-GitHub-Merged-By: vlsi X-GitHub-Repo: pgjdbc/pgjdbc X-GitHub-State: merged X-GitHub-Type: pull_request X-GitHub-Url: https://github.com/pgjdbc/pgjdbc/pull/4153 Content-Type: text/plain; charset=utf-8 ## Why The 42.5.x build ties the compile JDK to the JVM that runs Gradle and sets the target through `sourceCompatibility`/`targetCompatibility`. That makes it hard to build on a fixed JDK while running the test suite against several Java versions, which is the model already used on `master`. ## What - Add a Gradle toolchain for the main modules. `jdkBuildVersion` (default 17) selects the compile JDK; the driver still produces Java 8 bytecode. - Replace `sourceCompatibility`/`targetCompatibility` with `javac --release 8`, guarded so it is only passed on `javac` 9+ (no effect on JDK 8). - Run the test task on a separate JDK via `-PjdkTestVersion` (0 reuses the build JVM), using a toolchain launcher. - Stop forwarding the build JVM's `java.*` system properties to the test JVM. Forwarding `java.home`/`java.version` broke a test JVM running on a different Java version; only `pgjdbc.*` is forwarded now, matching `master`. - CI: build on JDK 17 and run the test matrix on `matrix.java_version` (8, 11, 17) through the toolchain launcher (`jdkTestVersion` + `org.gradle.java.installations.fromEnv`). The `code-style`, `CheckerFramework`, `source-distribution` and `buildcache` jobs move to JDK 17. The build runs on JDK 17 and still targets Java 8 bytecode (`javac --release 8`). ## How to verify ``` # Compiles via the JDK 17 toolchain, produces Java 8 bytecode ./gradlew :postgresql:compileJava # Driver.class major version = 52 # Runs the tests on JDK 8 while the build stays on JDK 17 ./gradlew :postgresql:test -PjdkTestVersion=8 ``` Verified locally with `JAVA_HOME` on JDK 17: - `:postgresql:compileJava` compiles via the JDK 17 toolchain ("Compiling with toolchain '.../17.0.10-librca'") and emits Java 8 bytecode (`Driver.class` major version 52). - `:postgresql:test -PjdkTestVersion=8 --tests 'org.postgresql.util.PGtokenizerTest'` runs the test JVM on JDK 8 (`.../8.0.402-librca/bin/java`) and passes (5 tests, 0 failures). - `autostyleCheck checkstyleAll jandex` passes on JDK 17. The CI changes were verified by reading the workflows and validating the edited YAML; they were not run on GitHub Actions from this branch. ## Notes - `jdkBuildVersion` defaults to 17, so the build now expects a JDK 17 toolchain. Pass `-PjdkBuildVersion=0` to compile with the current JVM instead. - The x64 CI path installs the matrix test JDK first and JDK 17 last, so JDK 17 becomes the default `JAVA_HOME` (Gradle runs on 17) while the test task switches to `matrix.java_version` via the toolchain launcher. The matrix only generates `ubuntu-latest` (x64) rows, so x64 is the path that runs in practice. - The ARM64 path (`AdoptOpenJDK/install-jdk`) is updated on a best-effort basis: it installs both the test JDK and JDK 17, but the `fromEnv` list and architecture suffix are tuned for `_X64`. ARM64 is not currently generated by the matrix. 🤖 Generated with [Claude Code](https://claude.com/claude-code) diff --git a/.github/workflows/buildcache.yml b/.github/workflows/buildcache.yml index 683d9806e6..1deb7bd049 100644 --- a/.github/workflows/buildcache.yml +++ b/.github/workflows/buildcache.yml @@ -43,7 +43,8 @@ jobs: strategy: matrix: os: [ubuntu, macos, windows] - jdk: [8, 11] + # The driver compiles via a JDK 17 toolchain (still targeting Java 8 bytecode). + jdk: [17] name: '${{ matrix.os }}, ${{ matrix.jdk }} seed build cache' runs-on: ${{ matrix.os }}-latest @@ -52,13 +53,10 @@ jobs: with: fetch-depth: 50 - name: 'Set up JDK ${{ matrix.jdk }}' - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: + distribution: zulu java-version: ${{ matrix.jdk }} - - name: Skip javadoc with Java 11 - if: ${{ matrix.jdk == '11' }} - run: | - echo "skipJavadoc=-PskipJavadoc" >> $GITHUB_ENV - uses: burrunan/gradle-cache-action@v1 name: Build pgjdbc env: @@ -66,4 +64,4 @@ jobs: S3_BUILD_CACHE_SECRET_KEY: ${{ secrets.S3_BUILD_CACHE_SECRET_KEY }} with: job-id: jdk${{ matrix.jdk }} - arguments: build -x test --scan -i ${{ env.skipJavadoc }} + arguments: build -x test --scan -i diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a93f71cc6b..3fa0b20ef0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -44,18 +44,18 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 50 - - name: 'Set up JDK 8' + - name: 'Set up JDK 17' uses: actions/setup-java@v3 with: distribution: zulu - java-version: 8 + java-version: 17 - uses: burrunan/gradle-cache-action@v1 name: Verify code style env: S3_BUILD_CACHE_ACCESS_KEY_ID: ${{ secrets.S3_BUILD_CACHE_ACCESS_KEY_ID }} S3_BUILD_CACHE_SECRET_KEY: ${{ secrets.S3_BUILD_CACHE_SECRET_KEY }} with: - job-id: jdk8 + job-id: jdk17 arguments: autostyleCheck checkstyleAll jandex linux-checkerframework: @@ -65,6 +65,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 50 + # CheckerFramework 3.5.0 cannot access javac internals on JDK 16+, so run it on JDK 11. - name: 'Set up JDK 11' uses: actions/setup-java@v3 with: @@ -78,10 +79,10 @@ jobs: with: read-only: ${{ matrix.os == 'self-hosted' }} job-id: checker-jdk11 - arguments: --scan --no-parallel --no-daemon -PenableCheckerframework classes + arguments: --scan --no-parallel --no-daemon -PenableCheckerframework classes -PjdkBuildVersion=11 source-distribution-check: - name: 'Source distribution (JDK 11)' + name: 'Source distribution (JDK 17)' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -90,18 +91,18 @@ jobs: - name: Start PostgreSQL working-directory: docker/postgres-server run: docker compose up -d && docker compose logs - - name: 'Set up JDK 11' + - name: 'Set up JDK 17' uses: actions/setup-java@v3 with: distribution: zulu - java-version: 11 + java-version: 17 - uses: burrunan/gradle-cache-action@v1 name: Prepare source distribution env: S3_BUILD_CACHE_ACCESS_KEY_ID: ${{ secrets.S3_BUILD_CACHE_ACCESS_KEY_ID }} S3_BUILD_CACHE_SECRET_KEY: ${{ secrets.S3_BUILD_CACHE_SECRET_KEY }} with: - job-id: source-release-jdk11 + job-id: source-release-jdk17 arguments: --scan --no-parallel --no-daemon sourceDistribution -Ppgjdbc.version=1.0 -Prelease - name: Verify source distribution working-directory: pgjdbc/build/distributions @@ -160,20 +161,37 @@ jobs: - name: 'Get test node ARCH' run: echo "arch_name=$(uname -i)" >> $GITHUB_OUTPUT id: get_arch_name - - name: Set up Java ${{ matrix.java_version }}, ${{ matrix.java_distribution }} + - name: Set up test Java ${{ matrix.java_version }}, ${{ matrix.java_distribution }} if: ${{ steps.get_arch_name.outputs.arch_name != 'aarch64' }} uses: actions/setup-java@v3 with: java-version: ${{ matrix.java_version }} distribution: ${{ matrix.java_distribution }} architecture: x64 - - name: 'Setup JDK ${{ matrix.java_version }} on ARM64' + - name: Set up build JDK 17 + # Installed last so JDK 17 becomes the default JAVA_HOME and Gradle runs on it. + # The test task switches to matrix.java_version via the jdkTestVersion toolchain. + if: ${{ steps.get_arch_name.outputs.arch_name != 'aarch64' }} + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: zulu + architecture: x64 + - name: 'Setup test JDK ${{ matrix.java_version }} on ARM64' if: ${{ steps.get_arch_name.outputs.arch_name == 'aarch64' }} uses: AdoptOpenJDK/install-jdk@v1 with: impl: hotspot # or openj9 version: ${{ matrix.java_version }} architecture: aarch64 + - name: 'Setup build JDK 17 on ARM64' + # Installed last so JDK 17 becomes the default JAVA_HOME and Gradle runs on it. + if: ${{ steps.get_arch_name.outputs.arch_name == 'aarch64' }} + uses: AdoptOpenJDK/install-jdk@v1 + with: + impl: hotspot # or openj9 + version: '17' + architecture: aarch64 - name: Prepare local properties run: | # See https://github.com/actions/runner/issues/409 @@ -196,6 +214,11 @@ jobs: arguments: --scan --no-parallel --no-daemon jandex test ${{ matrix.extraGradleArgs }} properties: | includeTestTags=${{ matrix.includeTestTags }} + jdkBuildVersion=17 + jdkTestVersion=${{ matrix.java_version }} + org.gradle.java.installations.fromEnv=JAVA_HOME_${{ matrix.java_version }}_X64,JAVA_HOME_17_X64 + # 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 - name: 'Install krb5 for GSS tests' if: ${{ matrix.gss == 'yes' }} diff --git a/build.gradle.kts b/build.gradle.kts index e13c170421..34eeee97cc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,6 +13,8 @@ import com.github.vlsi.gradle.publishing.dsl.simplifyXml import com.github.vlsi.gradle.publishing.dsl.versionFromResolution import de.thetaphi.forbiddenapis.gradle.CheckForbiddenApis import de.thetaphi.forbiddenapis.gradle.CheckForbiddenApisExtension +import org.gradle.jvm.toolchain.JavaLanguageVersion +import org.gradle.jvm.toolchain.JavaToolchainService import org.postgresql.buildtools.JavaCommentPreprocessorTask plugins { @@ -51,6 +53,10 @@ val skipJavadoc by props() val skipForbiddenApis by props() val enableMavenLocal by props() val enableGradleMetadata by props() +// Java version used to compile the driver via a toolchain. 0 falls back to the JVM that runs Gradle. +val jdkBuildVersion = props.int("jdkBuildVersion", 17) +// Java version used to run the test task via a toolchain. 0 reuses the build JVM. +val jdkTestVersion = props.int("jdkTestVersion", 0) // For instance -PincludeTestTags=!org.postgresql.test.SlowTests // or -PincludeTestTags=!org.postgresql.test.Replication val includeTestTags by props("") @@ -363,12 +369,17 @@ allprojects { plugins.withType { configure { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 withSourcesJar() if (!skipJavadoc) { withJavadocJar() } + // Compile the driver with a fixed JDK via a toolchain, independent of the JVM + // that runs Gradle. The bytecode target stays at Java 8 (see javac --release below). + if (jdkBuildVersion != 0) { + toolchain { + languageVersion.set(JavaLanguageVersion.of(jdkBuildVersion)) + } + } } val sourceSets: SourceSetContainer by project @@ -521,8 +532,23 @@ allprojects { configureEach { options.encoding = "UTF-8" + // Target Java 8 bytecode without referencing Java 9+ API. + // --release is only understood by javac 9+, so skip it on JDK 8. + options.release.set( + provider { + 8.takeIf { javaCompiler.get().metadata.languageVersion.asInt() > 9 } + } + ) } configureEach { + // Run tests on a specific Java version, independent of the build JVM. + if (jdkTestVersion != 0) { + javaLauncher.set( + project.the().launcherFor { + languageVersion.set(JavaLanguageVersion.of(jdkTestVersion)) + } + ) + } useJUnitPlatform { if (includeTestTags.isNotBlank()) { includeTags.add(includeTestTags) @@ -558,7 +584,10 @@ allprojects { passProperty("user.country", "tr") val props = System.getProperties() for (e in props.propertyNames() as `java.util`.Enumeration) { - if (e.startsWith("pgjdbc.") || e.startsWith("java")) { + // Forward only pgjdbc.* here. Forwarding the build JVM's java.* properties + // (java.home, java.version, ...) would break a test JVM running on a + // different Java version selected via jdkTestVersion. + if (e.startsWith("pgjdbc.")) { passProperty(e) } } diff --git a/gradle.properties b/gradle.properties index b6a8df0274..8698dae7e8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,6 +15,12 @@ kotlin.code.style=official # Release version can be generated by using -Prelease or -Prc= arguments pgjdbc.version=42.5.7 +# Java version used to compile the driver via a Gradle toolchain (still targets Java 8 bytecode). +# Set to 0 to compile with the JVM that runs Gradle. +jdkBuildVersion=17 +# Java version used to run tests via a Gradle toolchain. 0 reuses the build JVM. e.g. -PjdkTestVersion=8 +jdkTestVersion=0 + # The options below configures the use of local clone (e.g. testing development versions) # You can pass un-comment it, or pass option -PlocalReleasePlugins, or -PlocalReleasePlugins= # localReleasePlugins=../vlsi-release-plugins