From 29a3e8e38271b5991e632bbb7e98b375b7d62ccd Mon Sep 17 00:00:00 2001 From: James Earl Douglas Date: Thu, 10 Oct 2024 05:35:18 -0700 Subject: [PATCH] [WIP] Cross-build for sbt 2.0.0-M2 --- CONTRIBUTING.md | 2 +- build.sbt | 17 +++++-- .../com/earldouglas/sbt/war/Compat.scala | 35 +++++++++++++ .../com/earldouglas/sbt/war/Compat.scala | 51 +++++++++++++++++++ .../com/earldouglas/sbt/war/SbtWar.scala | 26 ++++------ .../sbt/war/WarPackagePlugin.scala | 9 ++-- .../sbt/war/WebappComponents.scala | 5 +- .../sbt/war/WebappComponentsPlugin.scala | 13 ++--- .../com/earldouglas/sbt/war/TestCompat.scala | 13 +++++ .../com/earldouglas/sbt/war/TestCompat.scala | 20 ++++++++ .../com/earldouglas/sbt/war/HttpClient.scala | 14 ++--- 11 files changed, 164 insertions(+), 41 deletions(-) create mode 100644 src/main/scala-2.12/com/earldouglas/sbt/war/Compat.scala create mode 100644 src/main/scala-3/com/earldouglas/sbt/war/Compat.scala create mode 100644 src/test/scala-2/com/earldouglas/sbt/war/TestCompat.scala create mode 100644 src/test/scala-3/com/earldouglas/sbt/war/TestCompat.scala diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 84e674f4..9e143adf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -61,7 +61,7 @@ Create a staging release in Sonatype: $ nix-shell $ sbt > set version := "4.2.5" -> publishSigned +> +publishSigned ``` Review it: diff --git a/build.sbt b/build.sbt index 3d3a57d8..fd7a617d 100644 --- a/build.sbt +++ b/build.sbt @@ -4,7 +4,14 @@ name := "sbt-war" organization := "com.earldouglas" sbtPlugin := true scalacOptions ++= Seq("-feature", "-deprecation") -scalaVersion := "2.12.18" // https://scalameta.org/metals/blog/2023/07/19/silver#support-for-scala-21218 +scalaVersion := "2.12.20" +crossScalaVersions += "3.3.4" +pluginCrossBuild / sbtVersion := { + scalaBinaryVersion.value match { + case "2.12" => (pluginCrossBuild / sbtVersion).value + case _ => "2.0.0-M2" + } +} // scripted-plugin scriptedBufferLog := false @@ -17,8 +24,12 @@ scriptedParallelInstances := 8 // Scalafix semanticdbEnabled := true semanticdbVersion := scalafixSemanticdb.revision -scalacOptions += "-Ywarn-unused-import" -scalacOptions += s"-P:semanticdb:sourceroot:${baseDirectory.value}" +scalacOptions ++= { + scalaBinaryVersion.value match { + case "2.12" => Seq("-Ywarn-unused-import") + case _ => Seq.empty // ("-Wunused:imports") + } +} // webapp-components-runner lazy val warRunnerVersion = diff --git a/src/main/scala-2.12/com/earldouglas/sbt/war/Compat.scala b/src/main/scala-2.12/com/earldouglas/sbt/war/Compat.scala new file mode 100644 index 00000000..0414bbc3 --- /dev/null +++ b/src/main/scala-2.12/com/earldouglas/sbt/war/Compat.scala @@ -0,0 +1,35 @@ +package com.earldouglas.sbt.war + +import sbt.Def.Initialize +import sbt.Keys._ +import sbt.Keys.{`package` => pkg} +import sbt._ + +import java.io.{File => JavaFile} + +object Compat { + + val Compile_pkg_artifact = Compile / pkg / artifact + val Compile_sourceDirectory = Compile / sourceDirectory + val Compile_target = Compile / target + val Global_onLoad = Global / onLoad + val pkg_artifact = pkg / artifact + + val warContents: Initialize[Task[Map[String, JavaFile]]] = + WebappComponentsPlugin.warContents + + def managedJars(config: Configuration): Initialize[Task[Seq[File]]] = + Def.task { + Classpaths + .managedJars(config, classpathTypes.value, update.value) + .map(_.data) + .toSeq + } + + def toFile(file: TaskKey[File]): Initialize[Task[File]] = + file + + val classpathFiles: Initialize[Task[Seq[File]]] = + (Runtime / fullClasspath) + .map(_.files) +} diff --git a/src/main/scala-3/com/earldouglas/sbt/war/Compat.scala b/src/main/scala-3/com/earldouglas/sbt/war/Compat.scala new file mode 100644 index 00000000..920aabee --- /dev/null +++ b/src/main/scala-3/com/earldouglas/sbt/war/Compat.scala @@ -0,0 +1,51 @@ +package com.earldouglas.sbt.war + +import sbt.Def.Initialize +import sbt.Keys._ +import sbt.Keys.{`package` => pkg} +import sbt._ +import sbt.given + +import java.io.{File => JavaFile} + +import scala.language.implicitConversions + +object Compat: + + val Compile_pkg_artifact = Compile / pkg / artifact + val Compile_sourceDirectory = Compile / sourceDirectory + val Compile_target = Compile / target + val Global_onLoad = Global / onLoad + val pkg_artifact = pkg / artifact + + val warContents: Initialize[Task[Map[String, HashedVirtualFileRef]]] = + Def.task: + val conv: FileConverter = fileConverter.value + WebappComponentsPlugin + .warContents + .value + .map: + case (dst, src) => dst -> conv.toVirtualFile(src.toPath()) + + def managedJars(config: Configuration): Initialize[Task[Seq[File]]] = + Def.task: + Classpaths + .managedJars(config, classpathTypes.value, update.value, fileConverter.value) + .map(_.data) + .map(fileConverter.value.toPath(_)) + .map(_.toFile()) + .toList + + def toFile(file: TaskKey[HashedVirtualFileRef]): Initialize[Task[File]] = + Def.task: + fileConverter.value + .toPath(file.value) + .toFile() + + val classpathFiles: Initialize[Task[Seq[File]]] = + Def.task: + (Runtime / fullClasspath) + .value + .map(_.data) + .map(fileConverter.value.toPath(_)) + .map(_.toFile()) diff --git a/src/main/scala/com/earldouglas/sbt/war/SbtWar.scala b/src/main/scala/com/earldouglas/sbt/war/SbtWar.scala index 400082ec..82b7fb75 100644 --- a/src/main/scala/com/earldouglas/sbt/war/SbtWar.scala +++ b/src/main/scala/com/earldouglas/sbt/war/SbtWar.scala @@ -53,12 +53,7 @@ object SbtWar extends AutoPlugin { } val runnerJars: Initialize[Task[Seq[File]]] = - Def.task { - Classpaths - .managedJars(War, classpathTypes.value, update.value) - .map(_.data) - .toList - } + Compat.managedJars(War) val startWar: Initialize[Task[Unit]] = Def.task { @@ -74,7 +69,10 @@ object SbtWar extends AutoPlugin { "webapp.runner.launch.Main", "--port", warPort.value.toString(), - pkg.value.getPath() + Compat + .toFile(pkg) + .value + .getPath() ) ) containerInstance.set(Some(process)) @@ -88,10 +86,8 @@ object SbtWar extends AutoPlugin { val onLoadSetting: Initialize[State => State] = Def.setting { - (Global / onLoad).value - .compose { state: State => - state.addExitHook(stopContainerInstance(println(_))) - } + Compat.Global_onLoad.value + .compose(_.addExitHook(stopContainerInstance(println(_)))) } val forkOptions: Initialize[Task[ForkOptions]] = @@ -112,17 +108,17 @@ object SbtWar extends AutoPlugin { val runnerConfigFile: File = { - val emptyDir: File = (Compile / target).value / "empty" + val emptyDir: File = (Compat.Compile_target).value / "empty" val resourceMapString = - WebappComponentsPlugin.warContents.value + Compat.warContents.value .map { case (k, v) => s"${k}->${v}" } .mkString(",") val configurationFile: File = - (Compile / target).value / "webapp-components.properties" + (Compat.Compile_target).value / "webapp-components.properties" Files .writeString( @@ -155,7 +151,7 @@ object SbtWar extends AutoPlugin { } Seq( - Global / onLoad := onLoadSetting.value, + Compat.Global_onLoad := onLoadSetting.value, libraryDependencies ++= runnerLibraries.value, warForkOptions := forkOptions.value, warJoin := joinWar.value, diff --git a/src/main/scala/com/earldouglas/sbt/war/WarPackagePlugin.scala b/src/main/scala/com/earldouglas/sbt/war/WarPackagePlugin.scala index a2a493bc..b65b36be 100644 --- a/src/main/scala/com/earldouglas/sbt/war/WarPackagePlugin.scala +++ b/src/main/scala/com/earldouglas/sbt/war/WarPackagePlugin.scala @@ -1,6 +1,4 @@ package com.earldouglas.sbt.war - -import sbt.Def.Initialize import sbt.Keys.artifact import sbt.Keys.moduleName import sbt.Keys.{`package` => pkg} @@ -21,9 +19,8 @@ object WarPackagePlugin extends AutoPlugin { override lazy val projectSettings: Seq[Setting[_]] = { // Flip warContents around from (dst -> src) to (src -> dst) - val packageContents: Initialize[Task[Seq[(java.io.File, String)]]] = - WebappComponentsPlugin.warContents - .map(_.map(_.swap).toSeq) + val packageContents = + Compat.warContents.map(_.map(_.swap).toSeq) val packageTaskSettings: Seq[Setting[_]] = Defaults.packageTaskSettings(pkg, packageContents) @@ -32,7 +29,7 @@ object WarPackagePlugin extends AutoPlugin { pkg / artifact := Artifact(moduleName.value, "war", "war") val artifactSettings: Seq[Setting[_]] = - addArtifact(Compile / pkg / artifact, pkg) + addArtifact(Compat.Compile_pkg_artifact, pkg) Seq( packageTaskSettings, diff --git a/src/main/scala/com/earldouglas/sbt/war/WebappComponents.scala b/src/main/scala/com/earldouglas/sbt/war/WebappComponents.scala index 27ded17b..d9d95b64 100644 --- a/src/main/scala/com/earldouglas/sbt/war/WebappComponents.scala +++ b/src/main/scala/com/earldouglas/sbt/war/WebappComponents.scala @@ -20,7 +20,8 @@ object WebappComponents { * a mapping from destination to source of webapp resources */ def getResources(resourcesDir: File): Map[String, File] = { - (resourcesDir ** "*").get + (resourcesDir ** "*") + .get() .filter(_.exists()) .filter(_.isFile()) .flatMap(file => @@ -47,7 +48,7 @@ object WebappComponents { val classesMappings: Seq[(String, File)] = for { classpathDir <- classpathDirs - classFile <- (classpathDir ** "*").get + classFile <- (classpathDir ** "*").get() if classFile.exists() if classFile.isFile() relativeFile <- IO.relativizeFile(classpathDir, classFile) diff --git a/src/main/scala/com/earldouglas/sbt/war/WebappComponentsPlugin.scala b/src/main/scala/com/earldouglas/sbt/war/WebappComponentsPlugin.scala index fd8bd327..a836306e 100644 --- a/src/main/scala/com/earldouglas/sbt/war/WebappComponentsPlugin.scala +++ b/src/main/scala/com/earldouglas/sbt/war/WebappComponentsPlugin.scala @@ -2,7 +2,6 @@ package com.earldouglas.sbt.war import sbt.Def.Initialize import sbt.Def.taskKey -import sbt.Keys._ import sbt._ /** Identifies the files that compose the webapp (resources, .class @@ -52,19 +51,17 @@ object WebappComponentsPlugin extends AutoPlugin { override val projectSettings: Seq[Setting[_]] = { val warResourcesTask: Initialize[Task[Map[String, File]]] = - (Compile / sourceDirectory) + (Compat.Compile_sourceDirectory) .map(_ / "webapp") .map(WebappComponents.getResources) val warClassesTask: Initialize[Task[Map[String, File]]] = - (Runtime / fullClasspath) - .map(_.files) - .map(WebappComponents.getClasses) + Compat.classpathFiles + .map(WebappComponents.getClasses(_)) val warLibTask: Initialize[Task[Map[String, File]]] = - (Runtime / fullClasspath) - .map(_.files) - .map(WebappComponents.getLib) + Compat.classpathFiles + .map(WebappComponents.getLib(_)) Seq( warResources := warResourcesTask.value, diff --git a/src/test/scala-2/com/earldouglas/sbt/war/TestCompat.scala b/src/test/scala-2/com/earldouglas/sbt/war/TestCompat.scala new file mode 100644 index 00000000..5dc478f7 --- /dev/null +++ b/src/test/scala-2/com/earldouglas/sbt/war/TestCompat.scala @@ -0,0 +1,13 @@ +package com.earldouglas.sbt.war +import java.util.{List => JavaList} +import java.util.{Map => JavaMap} +import scala.collection.JavaConverters._ + +object TestCompat { + + def asScala[A, B](javaMap: JavaMap[A, B]): Map[A, B] = + javaMap.asScala.toMap + + def asScala[A](javaList: JavaList[A]): List[A] = + javaList.asScala.toList +} diff --git a/src/test/scala-3/com/earldouglas/sbt/war/TestCompat.scala b/src/test/scala-3/com/earldouglas/sbt/war/TestCompat.scala new file mode 100644 index 00000000..16e7dd02 --- /dev/null +++ b/src/test/scala-3/com/earldouglas/sbt/war/TestCompat.scala @@ -0,0 +1,20 @@ +package com.earldouglas.sbt.war + +import java.net.HttpURLConnection +import java.net.URI +import scala.jdk.CollectionConverters._ +import scala.io.Source +import java.util.{List => JavaList} +import java.util.{Map => JavaMap} + +object TestCompat: + + def asScala[A, B](javaMap: JavaMap[A, B]): Map[A, B] = + javaMap + .asScala + .toMap + + def asScala[A](javaList: JavaList[A]): List[A] = + javaList + .asScala + .toList diff --git a/src/test/scala/com/earldouglas/sbt/war/HttpClient.scala b/src/test/scala/com/earldouglas/sbt/war/HttpClient.scala index df16941a..30d8d6f1 100644 --- a/src/test/scala/com/earldouglas/sbt/war/HttpClient.scala +++ b/src/test/scala/com/earldouglas/sbt/war/HttpClient.scala @@ -42,12 +42,14 @@ object HttpClient { val response = Response( status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", + headers = + TestCompat + .asScala(c.getHeaderFields()) + .filter({ case (k, _) => k != null }) + .map({ case (k, v) => + (k, TestCompat.asScala(v).mkString(",")) + }) + .toMap - "Date" - "Content-Length" - "Server", body = Source.fromInputStream { if (c.getResponseCode() < 400) { c.getInputStream