diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 26c11bfb..9b10fa35 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -40,11 +40,16 @@ jobs: if: ${{ needs.changes.outputs.code == 'true' }} steps: - uses: actions/checkout@v6 + # sbt 2 requires JDK 17+ to run + - uses: actions/setup-java@v5 + with: + distribution: 'zulu' + java-version: '21' - name: jcheckstyle run: ./sbt jcheckStyle - name: scalafmtCheckAll run: ./sbt scalafmtCheckAll - + test: name: Test JDK${{ matrix.java }} runs-on: ubuntu-latest @@ -55,16 +60,29 @@ jobs: java: ['8', '11', '17', '21', '24'] steps: - uses: actions/checkout@v6 + # The JDK under test: msgpack-core forks its tests onto this JDK (via + # TEST_JAVA_HOME) so we still verify runtime behavior on each target JDK. - uses: actions/setup-java@v5 + id: target-jdk with: distribution: 'zulu' java-version: ${{ matrix.java }} + # sbt 2 itself requires JDK 17+ to run, so install a fixed, newer JDK to + # actually launch sbt. This becomes the default JDK on PATH/JAVA_HOME. + - uses: actions/setup-java@v5 + with: + distribution: 'zulu' + java-version: '21' - uses: actions/cache@v5 with: path: ~/.cache key: ${{ runner.os }}-jdk${{ matrix.java }}-${{ hashFiles('**/*.sbt') }} restore-keys: ${{ runner.os }}-jdk${{ matrix.java }}- - name: Test + env: + TEST_JAVA_HOME: ${{ steps.target-jdk.outputs.path }} run: ./sbt test - name: Universal Buffer Test + env: + TEST_JAVA_HOME: ${{ steps.target-jdk.outputs.path }} run: ./sbt test -J-Dmsgpack.universal-buffer=true \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0cec5029..84b7d9de 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,12 +16,19 @@ jobs: fetch-depth: 10000 # Fetch all tags so that sbt-dynver can find the previous release version - run: git fetch --tags -f - # Install OpenJDK 8 + # We need to compile with JDK8 for Android compatibility + # https://github.com/msgpack/msgpack-java/issues/516 - uses: actions/setup-java@v5 + id: jdk8 with: - # We need to use JDK8 for Android compatibility https://github.com/msgpack/msgpack-java/issues/516 java-version: 8 distribution: adopt + # sbt 2 itself requires JDK 17+ to run; build.sbt forks the compiler (and tests) + # onto JDK8 via TEST_JAVA_HOME, so this becomes the default JDK on PATH/JAVA_HOME. + - uses: actions/setup-java@v5 + with: + java-version: 21 + distribution: adopt - name: Setup GPG env: PGP_SECRET: ${{ secrets.PGP_SECRET }} @@ -29,6 +36,7 @@ jobs: - name: Build bundle env: PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} + TEST_JAVA_HOME: ${{ steps.jdk8.outputs.path }} run: | ./sbt publishSigned - name: Release to Sonatype diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index dc0fac1d..02594583 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -22,11 +22,19 @@ jobs: # Fetch all tags so that sbt-dynver can find the previous release version - run: git fetch --tags - uses: actions/setup-java@v5 + id: jdk11 with: java-version: 11 distribution: adopt + # sbt 2 itself requires JDK 17+ to run; build.sbt forks the compiler onto JDK11 + # via TEST_JAVA_HOME, so this becomes the default JDK on PATH/JAVA_HOME. + - uses: actions/setup-java@v5 + with: + java-version: 21 + distribution: adopt - name: Publish snapshots env: SONATYPE_USERNAME: '${{ secrets.SONATYPE_USERNAME }}' SONATYPE_PASSWORD: '${{ secrets.SONATYPE_PASSWORD }}' + TEST_JAVA_HOME: ${{ steps.jdk11.outputs.path }} run: ./sbt publish diff --git a/build.sbt b/build.sbt index 233abf0c..87315d0f 100644 --- a/build.sbt +++ b/build.sbt @@ -1,3 +1,5 @@ +import scala.language.implicitConversions + Global / onChangedBuildSource := ReloadOnSourceChanges // For performance testing, ensure each test run one-by-one @@ -67,8 +69,25 @@ val buildSettings = Seq[Setting[?]]( crossPaths := false, publishMavenStyle := true, // JVM options for building - scalacOptions ++= Seq("-encoding", "UTF-8", "-deprecation", "-unchecked", "-feature"), + // -release 8 pins scalac's resolution of JDK API calls to the JDK8 surface (e.g. + // test code calling ByteBuffer.flip() resolves to the inherited Buffer.flip():Buffer + // rather than JDK9's covariant ByteBuffer.flip():ByteBuffer override, which doesn't + // exist on a real JDK8 at runtime -> NoSuchMethodError). Unlike javac's --release, + // this doesn't need an ignore-symbol-file escape hatch since test code never touches + // JDK-internal APIs the way the main sources' Unsafe usage does. + scalacOptions ++= + Seq("-encoding", "UTF-8", "-deprecation", "-unchecked", "-feature", "-release", "8"), Test / javaOptions ++= Seq("-ea"), + // sbt 2 itself requires JDK 17+ to run, but each CI lane still needs to compile and + // test against its own target JDK (e.g. 8) to faithfully reproduce runtime behavior: + // javac resolves API calls against whichever JDK actually runs it (-source/-target + // only constrain language level and bytecode version, not API resolution), so e.g. + // compiling on JDK9+ can bind to covariant overloads like ByteBuffer.flip(): + // ByteBuffer that don't exist on a real JDK8 at runtime. When TEST_JAVA_HOME is set, + // fork both compilation and test execution onto that JDK; otherwise use the JDK + // running sbt, as before. + javaHome := sys.env.get("TEST_JAVA_HOME").map(file), + Test / fork := true, javacOptions ++= Seq("-source", "1.8", "-target", "1.8"), Compile / compile / javacOptions ++= Seq("-encoding", "UTF-8", "-Xlint:unchecked", "-Xlint:deprecation"), @@ -92,8 +111,8 @@ val buildSettings = Seq[Setting[?]]( // Style check config: (sbt-jchekcstyle) jcheckStyleConfig := "facebook", // Run jcheckstyle both for main and test codes - Compile / compile := ((Compile / compile) dependsOn (Compile / jcheckStyle)).value, - Test / compile := ((Test / compile) dependsOn (Test / jcheckStyle)).value + Compile / compile := Def.uncached((Compile / compile).dependsOn(Compile / jcheckStyle).value), + Test / compile := Def.uncached((Test / compile).dependsOn(Test / jcheckStyle).value) ) val junitJupiter = "org.junit.jupiter" % "junit-jupiter" % "5.14.4" % "test" @@ -134,7 +153,6 @@ lazy val msgpackCore = Project(id = "msgpack-core", base = file("msgpack-core")) "--add-opens=java.base/java.nio=ALL-UNNAMED", "--add-opens=java.base/sun.nio.ch=ALL-UNNAMED" ), - Test / fork := true, libraryDependencies ++= Seq( // msgpack-core should have no external dependencies diff --git a/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferInputTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferInputTest.scala index 0465aa69..5d3c3d9f 100644 --- a/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferInputTest.scala +++ b/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferInputTest.scala @@ -58,8 +58,9 @@ class MessageBufferInputTest extends AirSpec: def toByteBuffer = ByteBuffer.wrap(b) def saveToTmpFile: File = - val tmp = File.createTempFile("testbuf", ".dat", new File("target")) - tmp.getParentFile.mkdirs() + val dir = new File("target") + dir.mkdirs() + val tmp = File.createTempFile("testbuf", ".dat", dir) tmp.deleteOnExit() withResource(new FileOutputStream(tmp)) { out => out.write(b) diff --git a/project/build.properties b/project/build.properties index 2af813ac..4bf10378 100755 --- a/project/build.properties +++ b/project/build.properties @@ -1,2 +1,2 @@ -sbt.version=1.12.13 +sbt.version=2.0.1 diff --git a/project/plugins.sbt b/project/plugins.sbt index 2d12cde5..e0c7486a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -2,8 +2,8 @@ addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1") // TODO: Fixes jacoco error: // java.lang.NoClassDefFoundError: Could not initialize class org.jacoco.core.internal.flow.ClassProbesAdapter //addSbtPlugin("com.github.sbt" % "sbt-jacoco" % "3.3.0") -addSbtPlugin("org.xerial.sbt" % "sbt-jcheckstyle" % "0.2.1") -addSbtPlugin("com.github.sbt" % "sbt-osgi" % "0.10.0") +addSbtPlugin("org.xerial.sbt" % "sbt-jcheckstyle" % "0.3.0") +addSbtPlugin("com.github.sbt" % "sbt-osgi" % "0.11.0-RC1") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.6.1") addSbtPlugin("com.github.sbt" % "sbt-dynver" % "5.1.1")