2012-07-16 4 views
14

Javaソースジェネレータを含むプロジェクトをコンパイルしてから、生成されたコードを1つのプロジェクト内でコンパイルしたいとします。 Ie:Generator.scalaをコンパイルし、Generator.generate(outputDir)を実行し、outputDirをコンパイルしてjarファイルにパッケージ化します。 私はこれをしようとしている:SBTはプロジェクト定義ジェネレータを使用してコードを生成します

sourceGenerators in Compile <+= sourceManaged in Compile map { out => 
    Generator.generate(out/"generated") 
} 

をしかし、SBTは、基本的に

[error] Build.scala:1: object example is not a member of package org 
[error] import org.example.Generator 

文句を言い、SBTは、ジェネレータは、それがコンパイルされたプロジェクトで定義されて見えません。 sbtで私のやり方をすることは可能ですか?

+0

私もこの正確なシナリオに取り組んでいます。私はあなたのための答えはまだ、まだ初心者初心者はありません。しかし、答えも待っています。 –

答えて

13

これを少し掘り下げた後、私は解決策を思いつきました。まず、プロジェクトを2つのサブプロジェクトに分割する必要があります。 genには、ジェネレータコードを含むすべてのソースが含まれています。 usegenに依存し、ジェネレータを使用します。この場合

import sbt._ 
    import Keys._ 
    import java.io.{ File ⇒ JFile, FileOutputStream } 

    object OverallBuild extends Build { 

     lazy val root = Project(id = "overall", base = file(".")).aggregate(gen, use) 

     lazy val gen = Project(id = "generate", base = file("gen")) 

     val myCodeGenerator = TaskKey[Seq[File]]("mycode-generate", "Generate My Awesome Code") 

     lazy val use = Project(id = "use", base = file("use"), 
     settings = Defaults.defaultSettings ++ Seq(

      sourceGenerators in Compile <+= (myCodeGenerator in Compile), 

      myCodeGenerator in Compile <<= 
      (javaSource in Compile, dependencyClasspath in Runtime in gen) map { 

       (javaSource, cp) ⇒ runMyCodeGenerator(javaSource, cp.files) 

      })).dependsOn(gen) 

     def runMyCodeGenerator(javaSource: File, cp: Seq[File]): Seq[File] = { 
     val mainClass = "com.yourcompany.myCodeGenerator" 
     val tmp = JFile.createTempFile("sources", ".txt") 
     val os = new FileOutputStream(tmp) 

     try { 
      val i = new Fork.ForkScala(mainClass).fork(None, Nil, cp, 
      Seq(javaSource.toString), 
      None, 
      false, 
      CustomOutput(os)).exitValue() 

      if (i != 0) { 
      error("Trouble with code generator") 
      } 
     } finally { 
      os.close() 
     } 
     scala.io.Source.fromFile(tmp).getLines.map(f ⇒ file(f)).toList 
     } 
    } 

私は発電機にjavaSourceに渡されたので、私は.javaファイルを生成していました。

sourceGeneratorsを使用する場合、実行されたタスクは、生成されたすべてのファイルのSeq[File]を返して、sbtがそれらを管理できるようにする必要があります。この実装では、ジェネレータがフルパスファイル名を標準出力に出力し、それらを一時ファイルに保存します。

スカラと確かにSBTのように、あなたは何かをすることができ、それを掘り下げるだけでいいです。

+0

偉大な投稿ですが、これは私のために働いていましたが、 'sourceManaged in Compile'を出力ディレクトリとして使うことが好きです(sbtのドキュメントで推奨されています)。 –

+0

また、 '.dependsOn(gen)'を使う必要はないと思います。なぜなら、プロジェクトを公開するとき、 'use'から' gen'までの不要なライブラリ依存関係があるからです。 –

+0

sbt 1.0以降ではどのようにFork.ForkScalaをやりますか? – ChoppyTheLumberjack

1

プロジェクトの読み込み時にプロジェクトの説明がコンパイルされます。実行時に生成された新しいコードを直接呼び出す方法はありません。私は、JVMのフォークがなく、どういうわけかそれらのクラスがクラスローダーにロードされていることを確認して、何らかのリフレクションを使用していると思わない限り。

私が考えることができる唯一の方法は、プロジェクトをプロジェクト定義の中に作ることです。

root 
- src 
- project/ 
    - Build.scala // normal project definition 
    - project/ 
    - Build.scala // inner most 

最も内側のプロジェクト定義では、外側のsrcをsrcフォルダとして定義できます。これにより、実際のプロジェクトで使用できるGeneratorのコンパイル版が得られます。次に、通常のプロジェクト定義で、ジェネレータにインポートを追加し、実行中のように使用します。

私はかなり内側のプロジェクトが一度だけロードされ、コンパイルされることを確信しています。ジェネレータに変更を加えた場合は、プロジェクト定義をリロードする必要があります。退出と再開はそれを行う最も単純で愚かな方法ですが、それはテストに役立つかもしれません。後でそれがうまくいくと、読み込みのスマートな方法を調べる。

+0

2つの別々のプロジェクトを作成する必要があります.1つはジェネレータソース用で、もう1つは 'generated'ソース用です。 –

関連する問題