Adding JAR to SBT task runtime

I’m writing an sbt task to generate some managed sources, in order to do so I’m using fast-classpath-scanner to extract all sub-classes of the scapegoat inspection class.

I first implemented it as a normal scala project and it worked, but when trying to implement it as an SBT task I’m having a problem, and it is that the scapegoat jar is not in the classpath of the running task, so the classpath scanner is failing.

I have the dependencies of the task in the project/plugins.sbt file.

//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"
)

And I implemented the generation task in the build.sbt file:

//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

Note: The task compiles and runs perfectly, the problem is that the generated file is empty, because the scanner finds no inspection. This can be confirmed because the print("Hello world") statement inside the processMatch method is not executed (no output in the console).

Upvotes: 1

Views: 371

Answers (1)

I found that the scapegoat jar was indeed in the classpath of the task, but for some reason (that I don't understand) , probably related with the sbt classloader mechanism, the fast-classpath-scanner was failing to found them.

To anyone with a similar problem, to fix it you only need to override the scanner classloader.

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

Upvotes: 1

Related Questions