How to use Scalatest to develop a compiler-plugin in Scala

后端 未结 3 514
梦谈多话
梦谈多话 2021-01-03 07:11

Actually I\'m developing a compiler plugin for Scala according to the article on http://www.scala-lang.org/node/140.

Here is the code of the plugin:

         


        
相关标签:
3条回答
  • 2021-01-03 07:48

    I think you're looking at mock objects (I like EasyMock, but there are many others) and some refactoring.

    When you refer to your makefile, I get the impression you're using good old make. If so, might I suggest you look at something like SBT, Gradle, or, as you're coming from the Ruby world, BuildR. All of them have built in support for various scala test frameworks.

    0 讨论(0)
  • 2021-01-03 07:52

    You can invoke the Scala compiler, plus plugins, programmatically with code like the following:

    import scala.tools.nsc.{Settings, Global}
    import scala.tools.nsc.io.VirtualDirectory
    import scala.tools.nsc.reporters.ConsoleReporter
    import scala.tools.nsc.util.BatchSourceFile
    
    // prepare the code you want to compile
    val code = "object Foo extends Application { println(42 / 0) }"
    val sources = List(new BatchSourceFile("<test>", code))
    
    val settings = new Settings
    // save class files to a virtual directory in memory
    settings.outputDirs.setSingleOutput(new VirtualDirectory("(memory)", None))
    
    val compiler = new Global(settings, new ConsoleReporter(settings)) {
      override protected def computeInternalPhases () {
        super.computeInternalPhases
        for (phase <- new DivByZero(this).components)
          phasesSet += phase
      }
    }
    new compiler.Run() compileSources(sources)
    

    Note that this code requires that scala-compiler.jar and scala-library.jar be on the classpath when executing the code. If you are running your tests from within something like SBT, this will unfortunately not be the case.

    To get things running from within SBT, you have to do some hoop jumping:

    val settings = new Settings
    val loader = getClass.getClassLoader.asInstanceOf[URLClassLoader]
    val entries = loader.getURLs map(_.getPath)
    // annoyingly, the Scala library is not in our classpath, so we have to add it manually
    val sclpath = entries find(_.endsWith("scala-compiler.jar")) map(
      _.replaceAll("scala-compiler.jar", "scala-library.jar"))
    settings.classpath.value = ClassPath.join((entries ++ sclpath) : _*)
    

    If you are running from within some other build environment, you might find that scala-library.jar is already on the classpath, or if you're really lucky, then everything you need is on the standard Java classpath, in which case you can replace the above with:

    val settings = new Settings
    settings.usejavacp.value = true
    

    You can print out the value of the Java classpath with System.getProperty("java.class.path") and you can of course print out entries from the above code to see the classpath used by the class loader that is loading your test code.

    0 讨论(0)
  • 2021-01-03 07:55

    I'd like to add to samskivert's answer. Overriding computeInternalPhases forces to inject the phases of the plugin into the whole phaseSet, however, the compiler doesn't treat them as part of the plugin. So for instance, if you want to pass an option to your plugin "-P:divbyzero:someoption" using:

    settings.pluginOptions.appendToValue("divbyzero:someoption")

    you will get the following compilation error:

    error: bad option: -P:divbyzero:someoption

    and that is because compiler doesn't know anything about a plugin named divbyzero.

    The more appropriate way of adding plugin would be to override loadRoughPluginsList method and add plugins there, rather than manually inject every phase of the plugin into compilation phaseSet:

    override protected def loadRoughPluginsList: List[Plugin] =
      new DivByZero(this) :: super.loadRoughPluginsList
    
    0 讨论(0)
提交回复
热议问题