pgjdbc/pgjdbc GitHub issues and pull requests (mirror)  
help / color / mirror / Atom feed
From: vlsi (@vlsi) <[email protected]>
To: pgjdbc/pgjdbc <[email protected]>
Subject: [pgjdbc/pgjdbc] PR #4153: build: 42.5 compile with a Java 17 toolchain, run tests on multiple JDKs
Date: Thu, 04 Jun 2026 07:36:49 +0000
Message-ID: <[email protected]> (raw)

## 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<JavaPlugin> {
         configure<JavaPluginExtension> {
-            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<JavaCompile> {
                 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<Test> {
+                // Run tests on a specific Java version, independent of the build JVM.
+                if (jdkTestVersion != 0) {
+                    javaLauncher.set(
+                        project.the<JavaToolchainService>().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<String>) {
-                    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=<int> 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=<path>
 # localReleasePlugins=../vlsi-release-plugins


reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: github://pgjdbc/pgjdbc
  Cc: [email protected], [email protected]
  Subject: Re: [pgjdbc/pgjdbc] PR #4153: build: 42.5 compile with a Java 17 toolchain, run tests on multiple JDKs
  In-Reply-To: <<[email protected]>>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox