16

Java ソース ジェネレーターを含むプロジェクトをコンパイルし、生成されたコードを 1 つのプロジェクト内でコンパイルしたいと考えています。すなわち: Generator.scala をコンパイルし、Generator.generate(outputDir) を実行し、outputDir をコンパイルし、jar にパッケージ化します。私はこれを試しています:

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 は、コンパイルするプロジェクトで定義された Generator を認識しません。sbtで私のやり方でそれを行うことは可能ですか?

4

2 に答える 2

13

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

    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
      }
    }

この場合、.java ファイルを生成していたのでjavaSource、ジェネレーターに渡しました。

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

すべての Scala や SBT と同様に、掘り下げるだけで何でもできます。

于 2012-07-25T14:47:37.067 に答える
1

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

私が考えられる唯一の方法は、プロジェクト定義内でプロジェクトを作成することです。

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

最も内側のプロジェクト定義では、外側の src を src フォルダーとして定義できる場合があります。これにより、実際のプロジェクトで使用できるジェネレーターのコンパイル済みバージョンが得られます。次に、通常のプロジェクト定義で、Generator にインポートを追加し、それを以前と同じように使用します。

最も内側のプロジェクトは、一度だけロードおよびコンパイルされると確信しています。Generator に変更を加える場合は、sbt にプロジェクト定義をリロードさせる必要があります。終了して再度開くのが最も簡単で愚かな方法ですが、テストに役立つ場合があります。それが機能する場合は、後でよりスマートなリロード方法を調べてください。

于 2012-07-24T23:47:52.170 に答える