将 JAR 添加到 SBT 任务运行时

Adding JAR to SBT task runtime

我正在编写一个 sbt 任务来生成一些托管源,为此我正在使用 fast-classpath-scanner to extract all sub-classes of the scapegoat inspection class

我首先将它作为一个普通的 scala project 来实现并且它起作用了,但是当我试图将它作为一个 SBT 任务来实现时 我遇到了一个问题,它是替罪羊 jar 不在 运行 任务的 classpath 中,所以类路径扫描器失败了​​。

我在 project/plugins.sbt 文件中有任务的依赖项。

//adding the generation task dependencies
libraryDependencies ++= Seq(
  "com.sksamuel.scapegoat" %% "scalac-scapegoat-plugin" % "1.3.4",
  "io.github.lukehutch" % "fast-classpath-scanner" % "2.18.1"
)

并且我在build.sbt文件中实现了生成任务:

//source generator task (simplified)
sourceGenerators in Compile += Def.task {
  //_root_ is needed because there is a sbt.io package in scope
  import _root_.io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
  import _root_.io.github.lukehutch.fastclasspathscanner.matchprocessor.SubclassMatchProcessor
  import com.sksamuel.scapegoat.Inspection
  import scala.collection.mutable

  val inspectionClass = classOf[Inspection]
  val fastCPScanner = new FastClasspathScanner(inspectionClass.getPackage.getName)
  val inspections = mutable.ListBuffer.empty[Inspection]
  fastCPScanner
    .matchSubclassesOf(
      inspectionClass,
      new SubclassMatchProcessor[Inspection] {
        override def processMatch(matchingClass: Class[_ <: Inspection]): Unit = {
          inspections += matchingClass.newInstance()
          println("Hello World")
        }
      }
    ).scan()

  val lines = inspections.toList.zipWithIndex map {
    case (inspection, idx) => s"${inspections.text} -> ${idx}"
  }

  val scapegoatInspectionsFile = (sourceManaged in Compile).value / "scapegoat" / "inspections.scala"
  IO.writeLines(scapegoatInspectionsFile, lines)
  Seq(scapegoatInspectionsFile)
}.taskValue

注意:任务编译运行完美,问题是生成的文件是空的,因为scanner没有发现检查。可以确认是因为processMatch方法里面的print("Hello world")语句没有执行(控制台无输出).

我发现替罪羊罐子确实在任务的类路径中,但由于某种原因(我不明白),可能与sbt 类加载器机制,fast-classpath-scanner 找不到它们。

对于有类似问题的任何人,要修复它,您只需要覆盖扫描器类加载器。

 fastCPScanner
   .overrideClassLoaders(this.getClass.getClassLoader)
   (...)
   .scan()