How to create a Scala presentation compiler inside Ammonite REPL? - scala

I want to create a Scala presentation compiler in Ammonite REPL, however I always got the error of Missing dependency 'object scala in compiler mirror'.
I have tried the workaround mentioned in object scala in compiler mirror not found - running Scala compiler programatically . Unfortunately it does not work.
How to make it work?
Welcome to the Ammonite Repl 1.0.0
(Scala 2.12.2 Java 1.8.0_131)
If you like Ammonite, please support our development at www.patreon.com/lihaoyi
# import scala.tools.nsc.Settings
import scala.tools.nsc.Settings
# import scala.tools.nsc.interactive.Global
import scala.tools.nsc.interactive.Global
# import scala.tools.nsc.reporters.ConsoleReporter
import scala.tools.nsc.reporters.ConsoleReporter
# val settings = new Settings()
settings: Settings = Settings {
-d = .
}
# settings.usejavacp.value = true
# val reporter = new ConsoleReporter(settings)
reporter: ConsoleReporter = scala.tools.nsc.reporters.ConsoleReporter#4a24170b
# val compiler = new Global(settings, reporter)
error: error while loading Object, Missing dependency 'object scala in compiler mirror', required by /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/rt.jar(java/lang/Object.class)
scala.reflect.internal.MissingRequirementError: object scala in compiler mirror not found.
scala.reflect.internal.MissingRequirementError$.signal(MissingRequirementError.scala:17)
scala.reflect.internal.MissingRequirementError$.notFound(MissingRequirementError.scala:18)
scala.reflect.internal.Mirrors$RootsBase.$anonfun$getModuleOrClass$4(Mirrors.scala:54)
scala.reflect.internal.Mirrors$RootsBase.getModuleOrClass(Mirrors.scala:54)
scala.reflect.internal.Mirrors$RootsBase.getModuleOrClass(Mirrors.scala:66)
scala.reflect.internal.Mirrors$RootsBase.getPackage(Mirrors.scala:172)
scala.reflect.internal.Definitions$DefinitionsClass.ScalaPackage$lzycompute(Definitions.scala:169)
scala.reflect.internal.Definitions$DefinitionsClass.ScalaPackage(Definitions.scala:169)
scala.reflect.internal.Definitions$DefinitionsClass.ScalaPackageClass$lzycompute(Definitions.scala:170)
scala.reflect.internal.Definitions$DefinitionsClass.ScalaPackageClass(Definitions.scala:170)
scala.reflect.internal.Definitions$DefinitionsClass.init(Definitions.scala:1447)
scala.tools.nsc.Global$Run.<init>(Global.scala:1149)
scala.tools.nsc.interactive.Global$TyperRun.<init>(Global.scala:1308)
scala.tools.nsc.interactive.Global.newTyperRun(Global.scala:1331)
scala.tools.nsc.interactive.Global.<init>(Global.scala:286)
ammonite.$sess.cmd6$.<init>(cmd6.sc:1)
ammonite.$sess.cmd6$.<clinit>(cmd6.sc)
The same code works in official Scala REPL.
Welcome to Scala 2.12.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_131).
Type in expressions for evaluation. Or try :help.
scala> import scala.tools.nsc.Settings
import scala.tools.nsc.Settings
scala> import scala.tools.nsc.interactive.Global
import scala.tools.nsc.interactive.Global
scala> import scala.tools.nsc.reporters.ConsoleReporter
import scala.tools.nsc.reporters.ConsoleReporter
scala> val settings = new Settings()
settings: scala.tools.nsc.Settings =
Settings {
-d = .
}
scala> settings.usejavacp.value = true
settings.usejavacp.value: Boolean = true
scala> val reporter = new ConsoleReporter(settings)
reporter: scala.tools.nsc.reporters.ConsoleReporter = scala.tools.nsc.reporters.ConsoleReporter#7eeb38b2
scala> val compiler = new Global(settings, reporter)
compiler: scala.tools.nsc.interactive.Global = scala.tools.nsc.interactive.Global#3b6a4b91

Related

initialCommands in consoleProject is not executing one or more imports

Given these two inconspicuous lines of code…
import scala.sys.process._
def less(s: String) = ("code -" #< new java.io.ByteArrayInputStream(s.getBytes)).!!
…defined like this…
lazy val consoleSupportSettings: Seq[Setting[_]] = Seq(
initialCommands in consoleProject := """import scala.sys.process._
|def less(s: String) = ("code -" #< new java.io.ByteArrayInputStream(s.getBytes)).!!""".stripMargin
)
…which eventually is added to the root project…
lazy val root =
Project(id = "root", base = file("."))
.settings(consoleSupportSettings)
scala seems to not really(*) execute the line import scala.sys.process._: When I fire up sbt and hop into consoleProject I am greeted by…
> consoleProject
[info] Starting scala interpreter...
[info]
<console>:19: error: value #< is not a member of String
def less(s: String) = ("code -" #< new java.io.ByteArrayInputStream(s.getBytes)).!!
^
[success] Total time: 1 s, completed Apr 29, 2021 10:58:09 AM
success indeed. NOT.
However, if I remove the setting…
> set initialCommands in consoleProject := ""
[info] Defining root/*:consoleProject::initialCommands
[info] The new value will be used by root/*:consoleProject
[info] Reapplying settings...
…
> consoleProject
…and then manually enter above code it works:
…
Welcome to Scala version 2.10.4 (OpenJDK 64-Bit Server VM, Java 1.8.0_272).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import scala.sys.process._
import scala.sys.process._
scala> def less(s: String) = ("code -" #< new java.io.ByteArrayInputStream(s.getBytes)).!!
less: (s: String)String
Why is that and how do I fix this?
Worthy of note is that the sbt version is 0.13.8, so not exactly hot off the press.
(*) fwiw I replaced import scala.sys.process._ with import scala.sys.processasdf._ and it, as expected, would complain that processasdf was not a member of scala.sys, so it's not as if the import was actually ignored.
Whilst I still don't know why, it turns out that having the import inside the def rather than globally does the job:
initialCommands in consoleProject := """
|def less(s: String) = {
| import scala.sys.process._
| ("code -" #< new java.io.ByteArrayInputStream(s.getBytes)).!!
|}
|""".stripMargin

sbt: execute initialCommands silently

Is it possible to execute initialCommands in the console task silently, i.e. as if
:silent
val $session = new foo.bar.Session()
import $session._
import $session.lib._
:silent
Putting these commands in initialCommands doesn't work, though, because :<command> commands apparently cannot be used in initialCommands:
Welcome to Scala 2.12.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_102).
Type in expressions for evaluation. Or try :help.
scala> <console>:2: error: illegal start of definition
:silent
^
Interpreter encountered errors during initialization!
[error] (Thread-1) java.lang.InterruptedException
java.lang.InterruptedException
at java.util.concurrent.SynchronousQueue.put(SynchronousQueue.java:879)
at scala.tools.nsc.interpreter.SplashLoop.run(InteractiveReader.scala:77)
at java.lang.Thread.run(Thread.java:745)
Unfortunately, as of 0.13.13, sbt runs the initialCommands early, while it's creating the interpreter, and before the console has a chance to bind the interpreter as $intp.
This is close:
$ sbt -Dscala.repl.maxprintstring=-1
[info] Set current project to sbt-test (in build file:/home/apm/tmp/sbt-test/)
> console
[info] Starting scala interpreter...
[info]
Welcome to Scala 2.12.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_111).
Type in expressions for evaluation. Or try :help.
scala> ...
scala> Future(42)
...
scala> $intp.isettings.max
maxAutoprintCompletion maxPrintString
scala> $intp.isettings.maxPrintString = 1000
$intp.isettings.maxPrintString: Int = 1000
scala> "hi"*1000
res0: String = hihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihihi...
scala> Future(42)
res1: scala.concurrent.Future[Int] = Future(Success(42))
It's a misfeature that setting maxPrintString to zero doesn't truncate everything, including the ellipsis, which is always residual.
I'm unaware of an sbt option to do that. In the lack of a better solution, you could hide all your setup in nice looking import as follows:
object console {
object setup {
val bar = foo.bar
bar.init()
}
}
Edit 1:
Note that this is equivalent to the code original code you wrote: it put a thing in scope called bar, which points to foo.bar. You can also use the same technique with types to group whatever imports you need into a single one. This is the mechanism used Predef to magically get scala.collection.immutable.Set (both the type and the value) in scope.
Edit 2:
I guess your technique can't achieve that with a single import.
It still works. Suppose Session is defined as follows:
trait Session {
val v
def f
lazy val l
object o {}
type T
}
then
val $session = new foo.bar.Session()
import $session._
becomes
object console {
object setup {
val $session = new foo.bar.Session()
val v = $session.v
def f = $session.f
lazy val l = $session.l
val o = $session.o
type T = $session.T
}
}
You can apply this transformation recursively for lib._ and whatever other imports you have until you've built the exact same scope.

Setting configuration properties to access in REPL

Given:
src/test/scala/net/Main.scala
package net
import com.typesafe.config.ConfigFactory
object Main extends App {
override def main(args: Array[String]) {
val bar = ConfigFactory.load().getString("app.bar")
val bippy = ConfigFactory.load().getString("app.bippy")
println(s"bar: $bar | bippy : $bippy")
}
}
src/test/resources/application.conf
app {
bar = ${?BAR}
bippy = ${?BIPPY}
}
I attempted to set the BAR and BIPPY environment variables in sbt:
>set envVars := Map("BAR" -> "bar!", "BIPPY" -> "bippy!")
Then, I opened the REPL in test mode:
>test:console
scala> import net.Main
import net.Main
scala> Main.main(Array())
com.typesafe.config.ConfigException$Missing: No configuration setting
found for key 'app.bar'
How can I set these properties for the REPL?
Pass your configuration file using the -Dconfig.file system property
[localhost]$ sbt -Dconfig.file=src/test/resources/application.conf
[info] Loading global plugins from ~/.sbt/0.13/plugins
[info] Loading project definition from ~/my/project
[info] Set current project to my-project (in build file:~/my/project/)
> console
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.11.6 (OpenJDK 64-Bit Server VM, Java 1.8.0_72-internal).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import com.typesafe.config._
import com.typesafe.config._
scala> val config = ConfigFactory.load()
config: com.typesafe.config.Config = Config(SimpleConfigObject({"test": "success"})
scala> val value = config.getString("test")
value: String = test

Why does custom scaladoc task throw MissingRequirementError: object scala.annotation.Annotation in compiler mirror not found?

I hit a MissingRequirementError when I try to invoke scaladoc from within an sbt task.
Using any version of sbt 0.13.x, start with this build.sbt:
val scaladoc = taskKey[Unit]("run scaladoc")
scaladoc := {
import scala.tools.nsc._
val settings = new doc.Settings(error => print(error))
settings.usejavacp.value = true
val docFactory = new doc.DocFactory(new reporters.ConsoleReporter(settings), settings)
val universe = docFactory.makeUniverse(Left((sources in Compile).value.map(_.absolutePath).toList))
}
Then run sbt scaladoc, and behold (during makeUniverse):
[info] Set current project to test (in build file:...)
scala.reflect.internal.MissingRequirementError: object scala.annotation.Annotation in compiler mirror not found.
at scala.reflect.internal.MissingRequirementError$.signal(MissingRequirementError.scala:16)
at scala.reflect.internal.MissingRequirementError$.notFound(MissingRequirementError.scala:17)
at scala.reflect.internal.Mirrors$RootsBase.getModuleOrClass(Mirrors.scala:48)
What is wrong here? I've already tried fork := true and different combinations of sbt/scala versions to no avail.
It seems you need to provide scala-library (and indeed, any other dependencies) directly to the DocFactory.
scaladoc := {
import scala.tools.nsc._
val settings = new doc.Settings(error => print(error))
val dependencyPaths = (update in Compile).value
.select().map(_.absolutePath).mkString(java.io.File.pathSeparator)
settings.classpath.append(dependencyPaths)
settings.bootclasspath.append(dependencyPaths)
val docFactory = new doc.DocFactory(new reporters.ConsoleReporter(settings), settings)
val universe = docFactory.makeUniverse(Left((sources in Compile).value.map(_.absolutePath).toList))
}

REPL using IMain, Akka and sbt: get import working

I'm trying to get an interactive shell into my Scala application. I'm using the following system:
Scala 2.10.0
sbt 0.12.2
Akka 2.1.0
sbt-lwjgl-plugin 3.1.4
and the following non-working code:
import akka.actor.Actor
import scala.tools.nsc.Settings
import scala.tools.nsc.interpreter.IMain
class TestActor extends Actor {
def receive => {
case _ => {
val settings = new Settings
settings.usejavacp.value = true
settings embeddedDefaults ActorSystem.getClass.getClassLoader
val repl = new IMain(settings)
repl.interpret("import java._") // working
repl.interpret("import scala._") // working
repl.interpret("import akka._") // not working
repl.interpret("import other.java.class.Bar") // not working
}
}
}
Sbt is set to fork := true. I've tried several settings and class path configurations, but didn't find a working configuration. Can someone give me a hint/solution for this problem?
Have you tried to re-import all classpath with absolute path?
val settings = new Settings
settings.usejavacp.value = true
val classLoader = Thread.currentThread.getContextClassLoader
classLoader.asInstanceOf[URLClassLoader].getURLs.map(url => new File(url.toURI).getAbsolutePath).foreach {
jarPath =>
println(s"adding into Scala SDK classpath : ${jarPath}")
settings.bootclasspath.append(jarPath)
settings.classpath.append(jarPath)
}