From 628ba1458f23a4df26f3eee5847a26f3401f1abf Mon Sep 17 00:00:00 2001 From: Sam Carlberg Date: Fri, 17 Apr 2026 00:02:17 -0400 Subject: [PATCH] [build] Java 25 support (#8775) Commands v3 had a few changes due to the upgrade: - Java 24 removed the Pinned: MONITOR IllegalStateException when yielding in a synchronized block, so we no longer need to special case for it - Lambda method name generation was tweaked, requiring tests to be updated - Bazel java_rules needed to be bumped to support Java 25 Closes #8425 --- .bazelrc | 8 ++++---- .github/workflows/cmake-android.yml | 2 +- .github/workflows/gradle.yml | 4 ++-- .github/workflows/sentinel-build.yml | 2 +- MODULE.bazel | 2 +- MODULE.bazel.lock | 9 +++++++-- README-CMake.md | 2 +- README.md | 8 ++++---- build.gradle | 4 ++-- .../java/org/wpilib/command3/Coroutine.java | 19 +------------------ .../org/wpilib/command3/CoroutineTest.java | 9 ++------- .../command3/SchedulerErrorHandlingTests.java | 4 ++-- docs/build.gradle | 2 +- .../processor/AnnotationProcessor.java | 2 +- shared/java/javacommon.gradle | 2 +- .../wpilib/util/runtime/RuntimeLoader.java | 1 + 16 files changed, 32 insertions(+), 48 deletions(-) diff --git a/.bazelrc b/.bazelrc index d7735ff777..1a27fd9741 100644 --- a/.bazelrc +++ b/.bazelrc @@ -9,10 +9,10 @@ common --enable_workspace build --experimental_cc_static_library build --experimental_cc_shared_library -build --java_language_version=21 -build --java_runtime_version=remotejdk_21 -build --tool_java_language_version=21 -build --tool_java_runtime_version=remotejdk_21 +build --java_language_version=25 +build --java_runtime_version=remotejdk_25 +build --tool_java_language_version=25 +build --tool_java_runtime_version=remotejdk_25 test --test_output=errors test --test_verbose_timeout_warnings diff --git a/.github/workflows/cmake-android.yml b/.github/workflows/cmake-android.yml index dbaf7c5fc9..ce2d330a23 100644 --- a/.github/workflows/cmake-android.yml +++ b/.github/workflows/cmake-android.yml @@ -36,7 +36,7 @@ jobs: - uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Install sccache uses: mozilla-actions/sccache-action@v0.0.9 diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 5f091dd025..11047f82f4 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -145,7 +145,7 @@ jobs: - uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 architecture: ${{ matrix.architecture }} - name: Import Developer ID Certificate uses: wpilibsuite/import-signing-certificate@v3 @@ -325,7 +325,7 @@ jobs: (github.ref == 'refs/heads/2027' || startsWith(github.ref, 'refs/tags/v2027')) with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Combine (2027) if: | github.repository == 'wpilibsuite/allwpilib' && diff --git a/.github/workflows/sentinel-build.yml b/.github/workflows/sentinel-build.yml index 64c96a0c62..543f39c541 100644 --- a/.github/workflows/sentinel-build.yml +++ b/.github/workflows/sentinel-build.yml @@ -115,7 +115,7 @@ jobs: - uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 architecture: ${{ matrix.architecture }} - name: Import Developer ID Certificate uses: wpilibsuite/import-signing-certificate@v3 diff --git a/MODULE.bazel b/MODULE.bazel index 5e46a6bf80..1f031bbaf8 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -21,7 +21,7 @@ bazel_dep(name = "bazel_features", version = "1.33.0") bazel_dep(name = "aspect_bazel_lib", version = "2.14.0") bazel_dep(name = "bazel_skylib", version = "1.8.2") bazel_dep(name = "platforms", version = "1.0.0") -bazel_dep(name = "rules_java", version = "8.14.0") +bazel_dep(name = "rules_java", version = "8.16.1") bazel_dep(name = "rules_python", version = "1.7.0") bazel_dep(name = "rules_pycross", version = "0.8.1") diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 2dbaaa788f..31bf709821 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -12,7 +12,8 @@ "https://bcr.bazel.build/modules/abseil-cpp/20240116.2/MODULE.bazel": "73939767a4686cd9a520d16af5ab440071ed75cec1a876bf2fcfaf1f71987a16", "https://bcr.bazel.build/modules/abseil-cpp/20250127.1/MODULE.bazel": "c4a89e7ceb9bf1e25cf84a9f830ff6b817b72874088bf5141b314726e46a57c1", "https://bcr.bazel.build/modules/abseil-cpp/20250512.1/MODULE.bazel": "d209fdb6f36ffaf61c509fcc81b19e81b411a999a934a032e10cd009a0226215", - "https://bcr.bazel.build/modules/abseil-cpp/20250512.1/source.json": "d725d73707d01bb46ab3ca59ba408b8e9bd336642ca77a2269d4bfb8bbfd413d", + "https://bcr.bazel.build/modules/abseil-cpp/20250814.0/MODULE.bazel": "c43c16ca2c432566cdb78913964497259903ebe8fb7d9b57b38e9f1425b427b8", + "https://bcr.bazel.build/modules/abseil-cpp/20250814.0/source.json": "b88bff599ceaf0f56c264c749b1606f8485cec3b8c38ba30f88a4df9af142861", "https://bcr.bazel.build/modules/apple_support/1.11.1/MODULE.bazel": "1843d7cd8a58369a444fc6000e7304425fba600ff641592161d9f15b179fb896", "https://bcr.bazel.build/modules/apple_support/1.15.1/MODULE.bazel": "a0556fefca0b1bb2de8567b8827518f94db6a6e7e7d632b4c48dc5f865bc7c85", "https://bcr.bazel.build/modules/apple_support/1.23.1/MODULE.bazel": "53763fed456a968cf919b3240427cf3a9d5481ec5466abc9d5dc51bc70087442", @@ -49,6 +50,7 @@ "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", "https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d", "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", + "https://bcr.bazel.build/modules/bazel_skylib/1.8.1/MODULE.bazel": "88ade7293becda963e0e3ea33e7d54d3425127e0a326e0d17da085a5f1f03ff6", "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/MODULE.bazel": "69ad6927098316848b34a9142bcc975e018ba27f08c4ff403f50c1b6e646ca67", "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/source.json": "34a3c8bcf233b835eb74be9d628899bb32999d3e0eadef1947a0a562a2b16ffb", "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", @@ -112,9 +114,11 @@ "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513", "https://bcr.bazel.build/modules/rules_cc/0.1.5/MODULE.bazel": "88dfc9361e8b5ae1008ac38f7cdfd45ad738e4fa676a3ad67d19204f045a1fd8", + "https://bcr.bazel.build/modules/rules_cc/0.2.0/MODULE.bazel": "b5c17f90458caae90d2ccd114c81970062946f49f355610ed89bebf954f5783c", "https://bcr.bazel.build/modules/rules_cc/0.2.13/MODULE.bazel": "eecdd666eda6be16a8d9dc15e44b5c75133405e820f620a234acc4b1fdc5aa37", "https://bcr.bazel.build/modules/rules_cc/0.2.14/MODULE.bazel": "353c99ed148887ee89c54a17d4100ae7e7e436593d104b668476019023b58df8", "https://bcr.bazel.build/modules/rules_cc/0.2.14/source.json": "55d0a4587c5592fad350f6e698530f4faf0e7dd15e69d43f8d87e220c78bea54", + "https://bcr.bazel.build/modules/rules_cc/0.2.8/MODULE.bazel": "f1df20f0bf22c28192a794f29b501ee2018fa37a3862a1a2132ae2940a23a642", "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", @@ -129,7 +133,8 @@ "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", "https://bcr.bazel.build/modules/rules_java/8.14.0/MODULE.bazel": "717717ed40cc69994596a45aec6ea78135ea434b8402fb91b009b9151dd65615", - "https://bcr.bazel.build/modules/rules_java/8.14.0/source.json": "8a88c4ca9e8759da53cddc88123880565c520503321e2566b4e33d0287a3d4bc", + "https://bcr.bazel.build/modules/rules_java/8.16.1/MODULE.bazel": "0f20b1cecaa8e52f60a8f071e59a20b4e3b9a67f6c56c802ea256f6face692d3", + "https://bcr.bazel.build/modules/rules_java/8.16.1/source.json": "072f8d11264edc499621be2dc9ea01d6395db5aa6f8799c034ae01a3e857f2e4", "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", "https://bcr.bazel.build/modules/rules_java/8.6.1/MODULE.bazel": "f4808e2ab5b0197f094cabce9f4b006a27766beb6a9975931da07099560ca9c2", diff --git a/README-CMake.md b/README-CMake.md index b986f699c4..63e6de66e6 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -34,7 +34,7 @@ By default, all libraries get built with a default CMake setup. The libraries ar OpenCV needs to be findable by CMake. On systems like the Jetson, this is installed by default. Otherwise, you will need to build OpenCV from source and install it. -If you want JNI and Java, you will need a JDK of at least version 21 installed. In addition, you need a `JAVA_HOME` environment variable set properly and set to the JDK directory. +If you want JNI and Java, you will need a JDK of at least version 25 installed. In addition, you need a `JAVA_HOME` environment variable set properly and set to the JDK directory. If you are building with unit tests or simulation modules, you will also need an Internet connection for the initial setup process, as CMake will clone google-test and imgui from GitHub. diff --git a/README.md b/README.md index 519f4139cb..84bbbfe176 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,11 @@ Using Gradle makes building WPILib very straightforward. It only has a few depen ## Requirements -- [JDK 21](https://adoptium.net/temurin/releases/?version=21) +- [JDK 25](https://adoptium.net/temurin/releases/?version=25) - Note that the JRE is insufficient; the full JDK is required - - On Ubuntu, run `sudo apt install openjdk-21-jdk` - - On Windows, install the JDK 21 .msi from the link above - - On macOS, install the JDK 21 .pkg from the link above + - On Ubuntu, run `sudo apt install openjdk-25-jdk` + - On Windows, install the JDK 25 .msi from the link above + - On macOS, install the JDK 25 .pkg from the link above - C++ compiler - On Linux, install GCC 11 or greater - On Windows, install [Visual Studio Community 2022](https://visualstudio.microsoft.com/vs/community/) and select the C++ programming language during installation (Gradle can't use the build tools for Visual Studio) diff --git a/build.gradle b/build.gradle index 6cacf14d4f..89fc6f8c02 100644 --- a/build.gradle +++ b/build.gradle @@ -120,8 +120,8 @@ subprojects { plugins.withType(JavaPlugin) { java { - sourceCompatibility = 21 - targetCompatibility = 21 + sourceCompatibility = 25 + targetCompatibility = 25 } } diff --git a/commandsv3/src/main/java/org/wpilib/command3/Coroutine.java b/commandsv3/src/main/java/org/wpilib/command3/Coroutine.java index 38c4d72c84..88c710d3cf 100644 --- a/commandsv3/src/main/java/org/wpilib/command3/Coroutine.java +++ b/commandsv3/src/main/java/org/wpilib/command3/Coroutine.java @@ -48,24 +48,7 @@ public final class Coroutine { public boolean yield() { requireMounted(); - try { - return m_backingContinuation.yield(); - } catch (IllegalStateException e) { - if ("Pinned: MONITOR".equals(e.getMessage())) { - // Raised when a continuation yields inside a synchronized block or method: - // https://github.com/openjdk/jdk/blob/jdk-21%2B35/src/java.base/share/classes/jdk/internal/vm/Continuation.java#L396-L402 - // Note: Not a thing in Java 24+ - // Rethrow with an error message that's more helpful for our users - throw new IllegalStateException( - "Coroutine.yield() cannot be called inside a synchronized block or method. " - + "Consider using a Lock instead of synchronized, " - + "or rewrite your code to avoid locks and mutexes altogether.", - e); - } else { - // rethrow - throw e; - } - } + return m_backingContinuation.yield(); } /** diff --git a/commandsv3/src/test/java/org/wpilib/command3/CoroutineTest.java b/commandsv3/src/test/java/org/wpilib/command3/CoroutineTest.java index b257a5e8f0..9e32ad9883 100644 --- a/commandsv3/src/test/java/org/wpilib/command3/CoroutineTest.java +++ b/commandsv3/src/test/java/org/wpilib/command3/CoroutineTest.java @@ -58,13 +58,8 @@ class CoroutineTest extends CommandTestBase { .named("Yield In Synchronized Block"); m_scheduler.schedule(yieldInSynchronized); - - var error = assertThrows(IllegalStateException.class, m_scheduler::run); - assertEquals( - "Coroutine.yield() cannot be called inside a synchronized block or method. " - + "Consider using a Lock instead of synchronized, " - + "or rewrite your code to avoid locks and mutexes altogether.", - error.getMessage()); + m_scheduler.run(); + assertEquals(1, i.get()); } @Test diff --git a/commandsv3/src/test/java/org/wpilib/command3/SchedulerErrorHandlingTests.java b/commandsv3/src/test/java/org/wpilib/command3/SchedulerErrorHandlingTests.java index 347a1c1d3c..97e20d83fb 100644 --- a/commandsv3/src/test/java/org/wpilib/command3/SchedulerErrorHandlingTests.java +++ b/commandsv3/src/test/java/org/wpilib/command3/SchedulerErrorHandlingTests.java @@ -88,8 +88,8 @@ class SchedulerErrorHandlingTests extends CommandTestBase { // user code trace for where the command was scheduled (the `.onTrue()` line) assertEquals("=== Command Binding Trace ===", stackTrace[nestedIndex + 2].getClassName()); - assertEquals("lambda$nestedErrorDetection$4", stackTrace[nestedIndex + 3].getMethodName()); - assertEquals("lambda$nestedErrorDetection$5", stackTrace[nestedIndex + 4].getMethodName()); + assertEquals("lambda$nestedErrorDetection$1", stackTrace[nestedIndex + 3].getMethodName()); + assertEquals("lambda$nestedErrorDetection$0", stackTrace[nestedIndex + 4].getMethodName()); assertEquals("nestedErrorDetection", stackTrace[nestedIndex + 5].getMethodName()); } diff --git a/docs/build.gradle b/docs/build.gradle index 28a5bb1c96..d4376ab0a4 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -158,7 +158,7 @@ configurations { task generateJavaDocs(type: Javadoc) { classpath += project(":wpilibj").sourceSets.main.compileClasspath - options.links("https://docs.oracle.com/en/java/javase/21/docs/api/") + options.links("https://docs.oracle.com/en/java/javase/25/docs/api/") // workaround for opencv site blocking javadoc tool. If the link is changed, // docs/opencv/element-list must be redownloaded options.linksOffline("https://docs.opencv.org/4.10.0/javadoc/", "opencv") diff --git a/epilogue-processor/src/main/java/org/wpilib/epilogue/processor/AnnotationProcessor.java b/epilogue-processor/src/main/java/org/wpilib/epilogue/processor/AnnotationProcessor.java index 84b1de6a0b..fb264967cb 100644 --- a/epilogue-processor/src/main/java/org/wpilib/epilogue/processor/AnnotationProcessor.java +++ b/epilogue-processor/src/main/java/org/wpilib/epilogue/processor/AnnotationProcessor.java @@ -37,7 +37,7 @@ import org.wpilib.epilogue.Logged; import org.wpilib.epilogue.NotLogged; @SupportedAnnotationTypes({"org.wpilib.epilogue.CustomLoggerFor", "org.wpilib.epilogue.Logged"}) -@SupportedSourceVersion(SourceVersion.RELEASE_21) +@SupportedSourceVersion(SourceVersion.RELEASE_25) public class AnnotationProcessor extends AbstractProcessor { private static final String kCustomLoggerFqn = "org.wpilib.epilogue.CustomLoggerFor"; private static final String kClassSpecificLoggerFqn = diff --git a/shared/java/javacommon.gradle b/shared/java/javacommon.gradle index 0d5ffd7cc9..70a7431ad3 100644 --- a/shared/java/javacommon.gradle +++ b/shared/java/javacommon.gradle @@ -106,7 +106,7 @@ configurations { tasks.withType(JavaCompile).configureEach { options.compilerArgs = [ '--release', - '21', + '25', '-encoding', 'UTF8', "-Werror", diff --git a/wpiutil/src/main/java/org/wpilib/util/runtime/RuntimeLoader.java b/wpiutil/src/main/java/org/wpilib/util/runtime/RuntimeLoader.java index 1d21aacb5f..1fb9e48dab 100644 --- a/wpiutil/src/main/java/org/wpilib/util/runtime/RuntimeLoader.java +++ b/wpiutil/src/main/java/org/wpilib/util/runtime/RuntimeLoader.java @@ -76,6 +76,7 @@ public final class RuntimeLoader { * @param libraryName the name of the library to load. * @throws IOException if the library fails to load */ + @SuppressWarnings("restricted") public static void loadLibrary(String libraryName) throws IOException { try { System.loadLibrary(libraryName);