2011-01-17 8 views
4

実際、私はhttp://www.scala-lang.org/node/140の記事に従ってScalaのコンパイラプラグインを開発しています。ここでScalatestを使ってScalaでコンパイラプラグインを開発するには

は、プラグインのコードです:

package localhost 

import scala.tools.nsc 
import nsc.Global 
import nsc.Phase 
import nsc.plugins.Plugin 
import nsc.plugins.PluginComponent 

class DivByZero(val global: Global) extends Plugin { 
    import global._ 

    val name = "divbyzero" 
    val description = "checks for division by zero" 
    val components = List[PluginComponent](Component) 

    private object Component extends PluginComponent { 
    val global: DivByZero.this.global.type = DivByZero.this.global 
    val runsAfter = "refchecks" 
    // Using the Scala Compiler 2.8.x the runsAfter should be written as below 
    // val runsAfter = List[String]("refchecks"); 
    val phaseName = DivByZero.this.name 
    def newPhase(_prev: Phase) = new DivByZeroPhase(_prev)  

    class DivByZeroPhase(prev: Phase) extends StdPhase(prev) { 
     override def name = DivByZero.this.name 
     def apply(unit: CompilationUnit) { 
     for (tree @ Apply(Select(rcvr, nme.DIV), List(Literal(Constant(0)))) <- unit.body; 
      if rcvr.tpe <:< definitions.IntClass.tpe) 
      { 
      unit.error(tree.pos, "definitely division by zero") 
      } 
     } 
    } 
    } 
} 

私は物事をやっているが挙げ、すべてをコンパイルしてからのjarファイルを作成し、いくつかのmakefileを書きました。次に、プラグインjarファイルをtestfileで次のコマンドでロードします。

scalac -Xplugin:myplugin.jar test.scala 

出力を確認してください。私はルビーからtddとbddのやり方を知っていたので、この方法が嫌いです。 Scalatest http://www.scalatest.org/をインストールしました。 jarファイルやクラスdivbyzeroをテストすることは可能でしょうか?私は、プラグインがファイルで実行されるときに最初にロードされることを知っています。私は自分の頭の中で非常に有線であり、jarファイルを作成せずにプラグインクラスを直接テストすることができるかどうかわかりません(またはjarファイルのいくつかの関数とクラスをテストすることも可能です)。

誰も私を助けることができない場合は、私は古き良き時代のようにお時間を

感謝の開発に保つことができると私が好き(私はあなたがモックオブジェクトを見ていると思います マティアス

+0

、することができますIDEのクラスパスに追加してテストファイルから呼び出すことはできません。 – Raphael

+0

私はeclipseやnetbeansでこれを扱っていませんでした。 jarファイルがあるすべての新しいコンパイルでそれらを伝えなければなりません。それが私がmakefileを書いた理由です。 –

答えて

1

を助けますEasyMockがありますが、他にも多数あります)といくつかのリファクタリングがあります。

あなたのメイクファイルを参照すると、古いメイクを使用しているという印象を受けます。もしそうなら、あなたはSBTGradleのようなものを見るか、Rubyの世界から来ているように、BuildRを見ることをお勧めします。それらのすべては、さまざまなスケーラテストフレームワークのサポートを組み込んでいます。

+0

私は実際にRakefileを書いています。 –

+0

@Matthias Guenther:これはレイクの質問になるだろう。これはクールだ。私は、Rakeから派生したBuildRへの移行を提案し、Rubyをその言語として使用するので、あなたはそれを受け入れなければなりません。 – sblundy

+0

@sblundyあなたが思うに、これはモックオブジェクトを追加することを可能にするか、またはBuildRを使う利点は何ですか? –

17

あなたは、プログラムで、次のようなコードで、Scalaのコンパイラに加え、プラグインを呼び出すことができます。このコードは、コードを実行するときscala-compiler.jarscala-library.jarがクラスパス上にあることが必要ということ

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) 

注意を。あなたがSBTのようなものの中からあなたのテストを走らせているなら、残念ながらそうではありません。

物事がSBT内から実行されている取得するには、いくつかのフープジャンプをしなければならない。

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) : _*) 

あなたには、いくつかの他のビルド環境内から実行している場合は、scala-library.jarは、クラスパスにすでにあることを見つけるかもしれない、またはあなたがSystem.getProperty("java.class.path")とJavaクラスパスの値をプリントアウトすることができますし、あなたができる

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

:あなたは本当に幸運なら、あなたが必要その後、すべてはあなたが上記を置き換えることができ、その場合には、標準のJavaクラスパス上にありますの上記のコードからentriesを印刷して、テストコードをロードしているクラスローダーが使用するクラスパスを確認してください。

+0

優秀な書き込み、ありがとう! –

+0

偉大なポスト - 本当に依存関係分析を行うためのプラグインを書くとき私を助けた。出力ではなく、仮想のものよりも、実際のディレクトリにある必要がありますが、その後、私はあなたが 'settings.d.valueを使うべきだと思う=「パス・ツー・ディレクトリとして、文字列」' の代わり 'settings.outputDirs.setSingleOutput ...' 前者は後者を呼び出すだけでなく、 'outdir'メソッドの設定を一貫させます。 – cage433

5

私はsamskivertの答えに追加したいと思います。 computeInternalPhasesを無効にすると、プラグインのフェーズがphaseSet全体に挿入されますが、コンパイラはこれらをプラグインの一部として処理しません。だから、例えば、あなたが使用してプラグイン"-P:divbyzero:someoption"にオプションを渡したい場合:「

error: bad option: -P:divbyzero:someoption

と、コンパイラはdoesnのため、それは次のとおりです。

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

を次のコンパイルエラーを取得しますdivbyzeroという名前のプラグインについて何か知っている。

プラグインを追加するより適切な方法は、loadRoughPluginsListメソッドをオーバーライドして、そこにプラグインを追加するのではなく、手動でコンパイルphaseSetへのプラグインのすべてのフェーズを注入することです:Scalaのコンパイラは、jarファイルとして提供されますので

override protected def loadRoughPluginsList: List[Plugin] = 
    new DivByZero(this) :: super.loadRoughPluginsList 
+0

非常に良い –

関連する問題