This is an automated email from the ASF dual-hosted git repository.

slawrence pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil-sbt.git


The following commit(s) were added to refs/heads/main by this push:
     new 814bd7b  Set a scalaVersion default based on the daffodilVersion
814bd7b is described below

commit 814bd7bc6594e700bf930faa2a6c1a8a18775b7c
Author: Steve Lawrence <[email protected]>
AuthorDate: Tue Jun 18 12:25:56 2024 -0400

    Set a scalaVersion default based on the daffodilVersion
    
    Schema projects shouldn't need to set scalaVersion, instead we should
    get use the version of Scala a specific version of Daffodil shipped
    with, raised to the minimum version of Scala supported by the current
    JDK.
    
    This also refactors semantic version selectors so that we can reuse
    similar logic in a number of places. Note that SemanticSelector already
    supports multiple space separated selectors, so the custom logic we had
    for that is removed.
    
    Closes #34
---
 README.md                                          |  6 ++
 .../scala/org/apache/daffodil/DaffodilPlugin.scala | 92 ++++++++++++++++++----
 2 files changed, 82 insertions(+), 16 deletions(-)

diff --git a/README.md b/README.md
index f95559b..88061c4 100644
--- a/README.md
+++ b/README.md
@@ -49,6 +49,11 @@ Daffodil version set the `daffodilVersion` setting in 
build.sbt, for example:
 daffodilVersion := "3.6.0"
 ```
 
+Notably, this plugin sets `scalaVersion`, which should usually not be defined
+in schema projects using this plugin. This setting is set to the version of
+Scala used for the release of `daffodilVersion` and raised to the [minimum JDK
+compatible Scala version] if required.
+
 ### Saved Parsers
 
 This plugin adds the ability to create and publish saved parsers of a schema.
@@ -214,3 +219,4 @@ Apache Daffodil SBT Plugin is licensed under the [Apache 
License, v2.0].
 
 [Apache License, v2.0]: https://www.apache.org/licenses/LICENSE-2.0
 [scripted test framework]: 
https://www.scala-sbt.org/1.x/docs/Testing-sbt-plugins.html
+[minimum JDK compatible Scala version]: 
https://docs.scala-lang.org/overviews/jdk-compatibility/overview.html
diff --git a/src/main/scala/org/apache/daffodil/DaffodilPlugin.scala 
b/src/main/scala/org/apache/daffodil/DaffodilPlugin.scala
index a262a6f..810b45c 100644
--- a/src/main/scala/org/apache/daffodil/DaffodilPlugin.scala
+++ b/src/main/scala/org/apache/daffodil/DaffodilPlugin.scala
@@ -18,6 +18,7 @@
 package org.apache.daffodil
 
 import java.io.File
+import scala.util.Properties
 
 import sbt.Keys._
 import sbt._
@@ -69,7 +70,70 @@ object DaffodilPlugin extends AutoPlugin {
     (optName.toSeq ++ Seq(cfg)).mkString("-")
   }
 
+  /**
+   * Filter a Map based on a version and whether or not it matches 
SemanticSelectors. See the
+   * SBT documenation for the syntax here:
+   *
+   * 
https://github.com/sbt/librarymanagement/blob/develop/core/src/main/contraband-scala/sbt/librarymanagement/SemanticSelector.scala
+   *
+   * For each mapping, if the SemanticSelector matches the given version, then 
the value of that
+   * mapping is selected. All mapping values that are selected are added to a 
Seq and returned.
+   * There is no implied order in the mapping and it is possible for multiple 
mappings to be
+   * selected. If only one mapping should be selected, the SemanticSelector 
keys should be made
+   * to be non-overlapping.
+   */
+  def filterVersions[T](version: String, mappings: Map[String, T]): Seq[T] = {
+    val vn = VersionNumber(version)
+    val filteredValues = mappings
+      .filterKeys { semSel => SemanticSelector(semSel).matches(vn) }
+      .values
+      .toSeq
+    filteredValues
+  }
+
   override lazy val projectSettings: Seq[Setting[_]] = Seq(
+    /**
+     * Even though Daffodil defines a transitive dependency to a specific 
version of Scala, once
+     * SBT sees that dependency it replaces it with a dependency to the value 
of the
+     * scalaVersion setting, ignoring whatever version Daffodil depends on. 
Settings like
+     * autoScalaLibrary and managedScalaInstance do not seem to change this 
behavior. So we
+     * maintain a mapping of scalaVersions used for each Daffodil release and 
set the
+     * scalaVersion setting based on the daffodilVersion setting. As long as 
schema projects do
+     * not override this setting, it ensures they use the same Scala version 
that Daffodil was
+     * released with. Schema projects can override this setting if they really 
need a specific
+     * Scala version, but that should be rare. We also take into account the 
minimum scala
+     * version supported by the current JDK.
+     */
+    scalaVersion := {
+      val jdkMinScalaVersionMapping = Map(
+        ">=23    " -> "2.12.20",
+        ">=22 <23" -> "2.12.19",
+        ">=21 <22" -> "2.12.18",
+        ">=17 <21" -> "2.12.15",
+        ">=11 <17" -> "2.12.4",
+        "     <11" -> "2.12.0",
+      )
+
+      val daffodilScalaVersionMapping = Map(
+        ">=3.7.0       " -> "2.12.19",
+        ">=3.5.0 <3.7.0" -> "2.12.18",
+        ">=3.4.0 <3.5.0" -> "2.12.17",
+        ">=3.2.0 <3.4.0" -> "2.12.15",
+        ">=3.1.0 <3.2.0" -> "2.12.13",
+        "        <3.1.0" -> "2.12.11",
+      )
+
+      val dafScalaVersion =
+        filterVersions(daffodilVersion.value, daffodilScalaVersionMapping).head
+      val jdkScalaVersion =
+        filterVersions(Properties.javaSpecVersion, 
jdkMinScalaVersionMapping).head
+      if (SemanticSelector("<" + 
jdkScalaVersion).matches(VersionNumber(dafScalaVersion))) {
+        jdkScalaVersion
+      } else {
+        dafScalaVersion
+      }
+    },
+
     /**
      * Default Daffodil version
      */
@@ -90,23 +154,19 @@ object DaffodilPlugin extends AutoPlugin {
       // dependencies to add if the daffodilVersion matches all of those 
specifiers. If the
       // version specifier Seq is empty, the associated dependencies are added 
regardless of
       // Daffodil version
-      val versionedDeps = Seq(
+      val versionedDeps = Map(
         // always add Daffodil and junit test dependencies
-        Nil -> Seq(
+        ">=3.0.0" -> Seq(
           "org.apache.daffodil" %% "daffodil-tdml-processor" % 
daffodilVersion.value % "test",
           "junit" % "junit" % "4.13.2" % "test",
           "com.github.sbt" % "junit-interface" % "0.13.2" % "test",
         ),
         // Add log4j with older versions of Daffodil to silence warnings about 
missing loggers
-        Seq(">=3.2.0", "<=3.4.0") -> Seq(
+        ">=3.2.0 <=3.4.0" -> Seq(
           "org.apache.logging.log4j" % "log4j-core" % "2.20.0" % "test",
         ),
       )
-
-      val dafVer = VersionNumber(daffodilVersion.value)
-      val dependencies = versionedDeps
-        .filter { case (vers, _) => vers.forall { v => 
SemanticSelector(v).matches(dafVer) } }
-        .flatMap { case (_, deps) => deps }
+      val dependencies = filterVersions(daffodilVersion.value, 
versionedDeps).flatten
       dependencies
     },
 
@@ -167,16 +227,16 @@ object DaffodilPlugin extends AutoPlugin {
       Configuration.of(cfg.capitalize, cfg)
     }.toSeq,
     libraryDependencies ++= {
-      daffodilPackageBinVersions.value.flatMap { daffodilVersion =>
-        val cfg = ivyConfigName(daffodilVersion)
-        val dafDep = "org.apache.daffodil" %% "daffodil-japi" % 
daffodilVersion % cfg
+      daffodilPackageBinVersions.value.flatMap { binDaffodilVersion =>
+        val cfg = ivyConfigName(binDaffodilVersion)
+        val dafDep = "org.apache.daffodil" %% "daffodil-japi" % 
binDaffodilVersion % cfg
         // logging backends used to hide warnings about missing backends, 
Daffodil won't
         // actually output logs that we care about, so this doesn't really 
matter
-        val logDep = if 
(SemanticSelector(">=3.5.0").matches(VersionNumber(daffodilVersion))) {
-          "org.slf4j" % "slf4j-nop" % "2.0.9" % cfg
-        } else {
-          "org.apache.logging.log4j" % "log4j-core" % "2.20.0" % cfg
-        }
+        val logMappings = Map(
+          ">=3.5.0" -> "org.slf4j" % "slf4j-nop" % "2.0.9" % cfg,
+          "<3.5.0" -> "org.apache.logging.log4j" % "log4j-core" % "2.20.0" % 
cfg,
+        )
+        val logDep = filterVersions(binDaffodilVersion, logMappings).head
         Seq(dafDep, logDep)
       }.toSeq
     },

Reply via email to