From c6ec43d22011f0dcbb1736b63a835e1f21e92f81 Mon Sep 17 00:00:00 2001
From: Wojciech Mazur <wmazur@virtuslab.com>
Date: Fri, 20 Dec 2024 01:07:54 +0100
Subject: [PATCH 1/4] Cross build with Scala Native

---
 build.sbt           | 67 +++++++++++++++++++++++++++++++++++----------
 project/plugins.sbt |  2 ++
 2 files changed, 54 insertions(+), 15 deletions(-)

diff --git a/build.sbt b/build.sbt
index 198c956..093fe4e 100644
--- a/build.sbt
+++ b/build.sbt
@@ -1,6 +1,8 @@
-ThisBuild / crossScalaVersions := Seq("2.13.15", "3.3.4")
+val scalaVersions =  Seq("2.13.15", "3.3.4")
+ThisBuild / crossScalaVersions := scalaVersions
 ThisBuild / scalaVersion := (ThisBuild / crossScalaVersions).value.head
 
+Global / concurrentRestrictions += Tags.limit(NativeTags.Link, 1)
 Global / cancelable := true
 publish / skip := true // in root
 
@@ -18,43 +20,78 @@ lazy val commonSettings: Seq[Setting[_]] =
     }),
   )
 
-lazy val core = project.in(file("core"))
+lazy val testNativeSettings: Seq[Setting[_]] = Seq(
+    // Required by Scala Native testing infrastructure
+    Test / fork := false,
+)
+  
+lazy val core = projectMatrix.in(file("core"))
   .settings(commonSettings)
   .settings(
     name := "scala-parallel-collections",
     Compile / doc / autoAPIMappings := true,
   )
+  .jvmPlatform(scalaVersions)
+  .nativePlatform(scalaVersions, settings = testNativeSettings ++ Seq(
+    versionPolicyPreviousArtifacts := Nil, // TODO: not yet published ,
+    mimaPreviousArtifacts := Set.empty
+  ))
 
-lazy val junit = project.in(file("junit"))
+lazy val junit = projectMatrix.in(file("junit"))
   .settings(commonSettings)
   .settings(
-    libraryDependencies += "com.github.sbt" % "junit-interface" % "0.13.3" % Test,
-    libraryDependencies += "junit" % "junit" % "4.13.2" % Test,
-    // for javax.xml.bind.DatatypeConverter, used in SerializationStabilityTest
-    libraryDependencies += "javax.xml.bind" % "jaxb-api" % "2.3.1" % Test,
     testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-v"),
-    Test / fork := true,
     publish / skip := true,
   ).dependsOn(testmacros, core)
+  .jvmPlatform(scalaVersions, 
+    settings = Seq(
+      libraryDependencies += "com.github.sbt" % "junit-interface" % "0.13.3" % Test,
+      libraryDependencies += "junit" % "junit" % "4.13.2" % Test,
+        // for javax.xml.bind.DatatypeConverter, used in SerializationStabilityTest
+      libraryDependencies += "javax.xml.bind" % "jaxb-api" % "2.3.1" % Test,
+      Test / fork := true,
+    )
+  )
+  .nativePlatform(scalaVersions = scalaVersions, 
+    axisValues = Nil,
+    configure = _
+      .enablePlugins(ScalaNativeJUnitPlugin)
+      .settings(
+      Test/unmanagedSources/excludeFilter ~= { _ || 
+        "SerializationTest.scala" || // requires ObjectOutputStream
+        "SerializationStability.scala" || // requires jaxb-api
+        "SerializationStabilityBase.scala" ||
+        "SerializationStabilityTest.scala"
+      },
+      Test / fork := false
+      )
+  )  
 
-lazy val scalacheck = project.in(file("scalacheck"))
+lazy val scalacheck = projectMatrix.in(file("scalacheck"))
   .settings(commonSettings)
   .settings(
-    libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.18.1",
-    Test / fork := true,
+    libraryDependencies += "org.scalacheck" %%% "scalacheck" % "1.18.1",
     Test / testOptions += Tests.Argument(TestFrameworks.ScalaCheck, "-workers", "1", "-minSize", "0", "-maxSize", "4000", "-minSuccessfulTests", "5"),
     publish / skip := true
   ).dependsOn(core)
+    .jvmPlatform(scalaVersions,
+      settings = Seq(
+        Test / fork := true
+      )
+    )
+    .nativePlatform(scalaVersions, settings = testNativeSettings)
 
-lazy val testmacros = project.in(file("testmacros"))
+lazy val testmacros = projectMatrix.in(file("testmacros"))
   .settings(commonSettings)
   .settings(
-    libraryDependencies += (CrossVersion.partialVersion(scalaVersion.value) match {
-      case Some((3, _)) => scalaOrganization.value %% "scala3-compiler" % scalaVersion.value
-      case _            => scalaOrganization.value % "scala-compiler" % scalaVersion.value
+    libraryDependencies ++= (CrossVersion.partialVersion(scalaVersion.value) match {
+      case Some((3, _)) => Nil
+      case _            => List(scalaOrganization.value % "scala-compiler" % scalaVersion.value)
     }),
     publish / skip := true,
   )
+  .jvmPlatform(scalaVersions)
+  .nativePlatform(scalaVersions, settings = testNativeSettings)
 
 commands += Command.single("setScalaVersion") { (state, arg) =>
   val command = arg match {
diff --git a/project/plugins.sbt b/project/plugins.sbt
index a985cc4..9b6525c 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -1 +1,3 @@
 addSbtPlugin("org.scala-lang.modules" % "sbt-scala-module" % "3.2.0")
+addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.10.1")
+addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.6")

From 350a2bed169f8be3698a5b03bb284e48782a2e57 Mon Sep 17 00:00:00 2001
From: Wojciech Mazur <wmazur@virtuslab.com>
Date: Fri, 20 Dec 2024 12:38:07 +0100
Subject: [PATCH 2/4] Don't create Scala binary specific subprojects

---
 build.sbt | 49 ++++++++++++++++++++++++++++---------------------
 1 file changed, 28 insertions(+), 21 deletions(-)

diff --git a/build.sbt b/build.sbt
index 093fe4e..a96177b 100644
--- a/build.sbt
+++ b/build.sbt
@@ -1,6 +1,11 @@
 val scalaVersions =  Seq("2.13.15", "3.3.4")
+val defaultScalaVersion = scalaVersions.head
+
+// When defining JVM / Scala Native matrix we don't want duplicated projects for Scala 2/3
+val matrixScalaVersions = Seq(defaultScalaVersion)
+
 ThisBuild / crossScalaVersions := scalaVersions
-ThisBuild / scalaVersion := (ThisBuild / crossScalaVersions).value.head
+ThisBuild / scalaVersion := defaultScalaVersion
 
 Global / concurrentRestrictions += Tags.limit(NativeTags.Link, 1)
 Global / cancelable := true
@@ -10,6 +15,7 @@ lazy val commonSettings: Seq[Setting[_]] =
   Seq(scalaModuleAutomaticModuleName := Some("scala.collection.parallel")) ++
   ScalaModulePlugin.scalaModuleSettings ++ Seq(
     versionPolicyIntention := Compatibility.BinaryAndSourceCompatible,
+    crossScalaVersions := scalaVersions,
     Compile / compile / scalacOptions --= (CrossVersion.partialVersion(scalaVersion.value) match {
       case Some((3, _)) => Seq("-Xlint")
       case _            => Seq()
@@ -31,9 +37,9 @@ lazy val core = projectMatrix.in(file("core"))
     name := "scala-parallel-collections",
     Compile / doc / autoAPIMappings := true,
   )
-  .jvmPlatform(scalaVersions)
-  .nativePlatform(scalaVersions, settings = testNativeSettings ++ Seq(
-    versionPolicyPreviousArtifacts := Nil, // TODO: not yet published ,
+  .jvmPlatform(matrixScalaVersions)
+  .nativePlatform(matrixScalaVersions, settings = testNativeSettings ++ Seq(
+    versionPolicyPreviousArtifacts := Nil, // TODO: not yet published
     mimaPreviousArtifacts := Set.empty
   ))
 
@@ -43,7 +49,7 @@ lazy val junit = projectMatrix.in(file("junit"))
     testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-v"),
     publish / skip := true,
   ).dependsOn(testmacros, core)
-  .jvmPlatform(scalaVersions, 
+  .jvmPlatform(matrixScalaVersions, 
     settings = Seq(
       libraryDependencies += "com.github.sbt" % "junit-interface" % "0.13.3" % Test,
       libraryDependencies += "junit" % "junit" % "4.13.2" % Test,
@@ -52,18 +58,18 @@ lazy val junit = projectMatrix.in(file("junit"))
       Test / fork := true,
     )
   )
-  .nativePlatform(scalaVersions = scalaVersions, 
+  .nativePlatform(matrixScalaVersions, 
     axisValues = Nil,
     configure = _
       .enablePlugins(ScalaNativeJUnitPlugin)
       .settings(
-      Test/unmanagedSources/excludeFilter ~= { _ || 
-        "SerializationTest.scala" || // requires ObjectOutputStream
-        "SerializationStability.scala" || // requires jaxb-api
-        "SerializationStabilityBase.scala" ||
-        "SerializationStabilityTest.scala"
-      },
-      Test / fork := false
+        Test/unmanagedSources/excludeFilter ~= { _ || 
+          "SerializationTest.scala" || // requires ObjectOutputStream
+          "SerializationStability.scala" || // requires jaxb-api
+          "SerializationStabilityBase.scala" ||
+          "SerializationStabilityTest.scala"
+        },
+        Test / fork := false
       )
   )  
 
@@ -73,13 +79,14 @@ lazy val scalacheck = projectMatrix.in(file("scalacheck"))
     libraryDependencies += "org.scalacheck" %%% "scalacheck" % "1.18.1",
     Test / testOptions += Tests.Argument(TestFrameworks.ScalaCheck, "-workers", "1", "-minSize", "0", "-maxSize", "4000", "-minSuccessfulTests", "5"),
     publish / skip := true
-  ).dependsOn(core)
-    .jvmPlatform(scalaVersions,
-      settings = Seq(
-        Test / fork := true
-      )
+  )
+  .dependsOn(core)
+  .jvmPlatform(matrixScalaVersions,
+    settings = Seq(
+      Test / fork := true
     )
-    .nativePlatform(scalaVersions, settings = testNativeSettings)
+  )
+  .nativePlatform(matrixScalaVersions, settings = testNativeSettings)
 
 lazy val testmacros = projectMatrix.in(file("testmacros"))
   .settings(commonSettings)
@@ -90,8 +97,8 @@ lazy val testmacros = projectMatrix.in(file("testmacros"))
     }),
     publish / skip := true,
   )
-  .jvmPlatform(scalaVersions)
-  .nativePlatform(scalaVersions, settings = testNativeSettings)
+  .jvmPlatform(matrixScalaVersions)
+  .nativePlatform(matrixScalaVersions, settings = testNativeSettings)
 
 commands += Command.single("setScalaVersion") { (state, arg) =>
   val command = arg match {

From 9e9b8c9298d1933b4ef646b3b983bbad7e122cf7 Mon Sep 17 00:00:00 2001
From: Wojciech Mazur <wmazur@virtuslab.com>
Date: Fri, 20 Dec 2024 12:54:39 +0100
Subject: [PATCH 3/4] Add command for JVM/Native tests, use them in the CI

---
 .github/workflows/ci.yml |  8 +++++---
 build.sbt                | 11 +++++++++++
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 47e6162..d9ad3c1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,8 +25,10 @@ jobs:
         distribution: temurin
         java-version: ${{matrix.java}}
     - uses: sbt/setup-sbt@v1
-    - name: Test
-      run: sbt "setScalaVersion ${{matrix.scala}}" test core/headerCheck package
+    - name: Test JVM
+      run: sbt "setScalaVersion ${{matrix.scala}}" testJVM core/headerCheck package
+    - name: Test Native
+      run: sbt "setScalaVersion ${{matrix.scala}}" testNative
 
   test-rc:
     strategy:
@@ -52,4 +54,4 @@ jobs:
         java-version: ${{matrix.java}}
     - uses: sbt/setup-sbt@v1
     - name: Test
-      run: sbt "setScalaVersion ${{matrix.scala}}" test core/headerCheck package
+      run: sbt "setScalaVersion ${{matrix.scala}}" testJVM core/headerCheck package
diff --git a/build.sbt b/build.sbt
index a96177b..db274f0 100644
--- a/build.sbt
+++ b/build.sbt
@@ -107,3 +107,14 @@ commands += Command.single("setScalaVersion") { (state, arg) =>
   }
   command :: state
 }
+
+def testPlatformCommand(name: String, selector: ProjectMatrix => sbt.internal.ProjectFinder): Command = 
+  Command.command(name) { state =>
+    List(junit, scalacheck, testmacros)
+    .flatMap(selector(_).get)
+    .map{ project => s"${project.id}/test"}
+    .toList ::: state
+  }
+
+commands += testPlatformCommand("testNative", _.native)
+commands += testPlatformCommand("testJVM", _.jvm)

From 8a0501e59873934d898cae628c7c17d6dc637d38 Mon Sep 17 00:00:00 2001
From: Wojciech Mazur <wmazur@virtuslab.com>
Date: Fri, 20 Dec 2024 12:58:14 +0100
Subject: [PATCH 4/4] Fix compilation

---
 build.sbt | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/build.sbt b/build.sbt
index db274f0..6e5ecae 100644
--- a/build.sbt
+++ b/build.sbt
@@ -108,7 +108,8 @@ commands += Command.single("setScalaVersion") { (state, arg) =>
   command :: state
 }
 
-def testPlatformCommand(name: String, selector: ProjectMatrix => sbt.internal.ProjectFinder): Command = 
+import sbt.internal.{ProjectMatrix, ProjectFinder}
+def testPlatformCommand(name: String, selector: ProjectMatrix => ProjectFinder): Command = 
   Command.command(name) { state =>
     List(junit, scalacheck, testmacros)
     .flatMap(selector(_).get)