Dynamic compilation with scala 2.12 - in a `sbt test` session

See below for updated question.

Hello,

You might want to post the NPE?

“”“println()”"" is not a valid compilation unit. Try something like
""“object MyObject { println() }”""

 Best, Oliver

I’ve refocused my question (also available on SO : https://stackoverflow.com/questions/44217163/dynamic-compilation-with-scala-2-12-from-sbt-test)

I want to test a tool I write which transforms some scala source code, I want to check that the transformed code compiles from a sbt test (using scalatest).

I’d like to call programmatically the scala compiler on a String with my source (all is in a standalone file).

I made some progress since the initial question. This code works in my IDE (IntelliJ) but not from a sbt test session

import java.io.File
import scala.reflect.internal.util.BatchSourceFile
import scala.tools.nsc.{GenericRunnerSettings, Global}

object Compilation {
  val settings = new GenericRunnerSettings(System.out.println _)
//  val sbtClasspath = System.getProperty("sbt-classpath")
//  val s = File.pathSeparator
//  val classPath = s".${s}$sbtClasspath"
//  settings.classpath.append(classPath)
  settings.usejavacp.value = true
  val global = new Global(settings)

  def compiles(code: String): Boolean = {
    val r = new global.Run
    r.compileSources(List(new BatchSourceFile("<partest>", code)))
    val errors = global.reporter.hasErrors
    if (errors) r.reporting.summarizeErrors()
    !errors
  }
}

Here is my build.sbt

name := "CodinGame-Scala-Kit"
version := "0.1.0"
scalaVersion := "2.12.2"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.1" % "test"
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.12.2" % "test"

enablePlugins(JmhPlugin)

//val sbtcp = taskKey[Unit]("sbt-classpath")
//
//sbtcp := {
//  val files: Seq[File] = (fullClasspath in Compile).value.files
//  val sbtClasspath: String = files.map(x => x.getAbsolutePath).mkString(java.io.File.pathSeparator)
//  println("Set SBT classpath to 'sbt-classpath' environment variable")
//  println(sbtClasspath)
//  System.setProperty("sbt-classpath", sbtClasspath)
//}
//
//compile <<= (compile in Compile).dependsOn(sbtcp)

The initial error is

scala.reflect.internal.MissingRequirementError: object scala.annotation.Annotation in compiler mirror not found.
[info]   at scala.reflect.internal.MissingRequirementError$.signal(MissingRequirementError.scala:17)
[info]   at scala.reflect.internal.MissingRequirementError$.notFound(MissingRequirementError.scala:18)
[info]   at scala.reflect.internal.Mirrors$RootsBase.$anonfun$getModuleOrClass$4(Mirrors.scala:54)
[info]   at scala.reflect.internal.Mirrors$RootsBase.getModuleOrClass(Mirrors.scala:54)
[info]   at scala.reflect.internal.Mirrors$RootsBase.getModuleOrClass(Mirrors.scala:45)
[info]   at scala.reflect.internal.Mirrors$RootsBase.getModuleOrClass(Mirrors.scala:66)
[info]   at scala.reflect.internal.Mirrors$RootsBase.getClassByName(Mirrors.scala:101)
[info]   at scala.reflect.internal.Mirrors$RootsBase.getRequiredClass(Mirrors.scala:104)
[info]   at scala.reflect.internal.Mirrors$RootsBase.requiredClass(Mirrors.scala:107)
[info]   at scala.reflect.internal.Definitions$DefinitionsClass.AnnotationClass$lzycompute(Definitions.scala:1141)

I have added the lines commented in build.sbt and in the Compilation object following this answer. It changes nothing in the IDE.

I now have this error on sbt test (compile correctly prints the classpath) :

[info]   scala.reflect.internal.Symbols$CyclicReference: illegal cyclic reference involving class Object
[info]   at scala.reflect.internal.Symbols$Symbol.$anonfun$info$3(Symbols.scala:1509)
[info]   at scala.Function0.apply$mcV$sp(Function0.scala:34)
[info]   at scala.reflect.internal.Symbols$Symbol.lock(Symbols.scala:564)
[info]   at scala.reflect.internal.Symbols$Symbol.info(Symbols.scala:1507)
[info]   at scala.reflect.internal.Symbols$Symbol.initialize(Symbols.scala:1669)
[info]   at scala.reflect.internal.Definitions$DefinitionsClass.init(Definitions.scala:1446)
[info]   at scala.tools.nsc.Global$Run.<init>(Global.scala:1149)
[info]   at com.truelaurel.tests.Compilation$.compiles(Compilation.scala:18)
[info]   at com.truelaurel.codingame.bundler.BundlerTest.$anonfun$new$1(BundlerTest.scala:22)
[info]   at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
[info]   ...
[info] - should keep scala imports *** FAILED ***
[info]   java.lang.AssertionError: assertion failed: parser: AnyRef
[info]         with DelayedInit {
[info]   def $init$(): Unit
[info]   val executionStart: Long
[info]   protected def args: Array[String]
[info]   def _args(): Array[String]
[info]   def _args_=(x$1: Array[String]): Unit
[info]   val initCode: scala.collection.mutable.ListBuffer[() => Unit]
[info]   override def delayedInit(body: () => Unit): Unit
[info]   def main(args: Array[String]): Unit
[info] }, fields: Object
[info]         with DelayedInit {
[info]   def $init$(): Unit
[info]   val executionStart(): Long
[info]   protected def args(): Array[String]
[info]   def _args(): Array[String]
[info]   def _args_=(x$1: Array[String]): Unit
[info]   val initCode(): scala.collection.mutable.ListBuffer[() => Unit]
[info]   override def delayedInit(body: () => Unit): Unit
[info]   def main(args: Array[String]): Unit
[info] }, namer: Object
[info]         with DelayedInit {
[info]   def $init$(): Unit
[info]   val executionStart: Long
[info]   protected def args: Array[String]
[info]   private def _args: Array[String]
[info]   private def _args_=(x$1: Array[String]): Unit
[info]   private val initCode: scala.collection.mutable.ListBuffer[() => Unit]
[info]   override def delayedInit(body: => Unit): Unit
[info]   def main(args: Array[String]): Unit
[info]   protected[this] def scala$App$_setter_$executionStart_=(x$1: Long): Unit
[info]   protected[this] def initCode_=(x$1: scala.collection.mutable.ListBuffer[() => Unit]): Unit
[info] }
[info]   at scala.reflect.internal.Symbols$TypeHistory.<init>(Symbols.scala:3651)
[info]   at scala.reflect.internal.Symbols$Symbol.rawInfo(Symbols.scala:1616)
[info]   at scala.tools.nsc.typechecker.Typers$Typer.isStale(Typers.scala:514)
[info]   at scala.tools.nsc.typechecker.Typers$Typer.reallyExists(Typers.scala:506)
[info]   at scala.tools.nsc.typechecker.Typers$Typer.qualifies$1(Typers.scala:5004)
[info]   at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typed1$62(Typers.scala:5030)
[info]   at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typed1$62$adapted(Typers.scala:5030)
[info]   at scala.reflect.internal.Symbols$Symbol.filter(Symbols.scala:1952)
[info]   at scala.tools.nsc.typechecker.Contexts$Context.lookupSymbol(Contexts.scala:1086)
[info]   at scala.tools.nsc.typechecker.Typers$Typer.typedIdent$2(Typers.scala:5030)
[info]   ...

running scripts has always been a huge pain for me, across versions - here’s what I use now on 2.11 with 0.13.something. It’s usually a class loader issue with SBT…

Hope it still works for 2.12 -

this check determines if it’s running in sbt or stand-alone

  if(razie.wiki.Services.config.isLocalhost) {

there’s a lot of code in there piled up over 8 years since 2.8, that’s perhaps not required anymore…