2012-07-16 88 views
14

我想編譯一個包含java源代碼生成器的項目,然後在單個項目中編譯生成的代碼。 I.e:編譯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

我也一直在摔跤這個確切的情況。我沒有爲你回答,仍然是一個新手。但也會等待答案。 – 2012-07-19 11:55:03

回答

13

因此,在深入瞭解這一點後,我想出了一個解決方案。首先,你需要將你的項目分成兩個子項目。 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 
     } 
    } 

在這種情況下,讓我在javaSource傳遞給發電機,我產生.java文件。

當我們在這裏使用sourceGenerators時,執行的任務必須返回生成的所有文件的Seq[File]以便sbt可以管理它們,這一點很重要。在這個實現中,我們的生成器將完整的路徑文件名輸出到標準輸出,並將它們保存到一個臨時文件中。

就像Scala和SBT的所有事情一樣,你可以做任何事情,只需要深入研究。

+0

偉大的文章,這爲我工作,雖然我更喜歡使用'sourceManaged in Compile'作爲輸出目錄(建議在sbt文檔中)。 – 2013-02-07 18:00:04

+0

另外,我認爲你不應該使用(並且需要)'.dependsOn(gen)',因爲當你發佈你的項目時,你會從'use'到'gen'有一個不必要的庫依賴。 – 2013-02-07 18:58:38

+0

你如何在sbt 1.0及更高版本中做Fork.ForkScala? – ChoppyTheLumberjack 2018-02-20 18:48:05

1

項目描述在加載時編譯。沒有辦法直接調用運行時生成的新代碼。除非我猜想使用某種反射來確保JVM沒有分叉,並以某種方式將這些類加載到類加載器中。

我能想到的唯一方法就是在項目定義中製作一個項目。

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

在最內部的項目定義中,您可以將外部src定義爲src文件夾。這將爲您提供真實項目可用的Generator的編譯版本。然後在正常的項目定義中添加一個導入到Generator並像你一樣使用它。

我很確定最內層的項目只會被加載和編譯一次。如果對發生器進行更改,則需要重新加載項目定義。退出和重新開放是最簡單/最笨的方式,但它可能有助於測試。稍後查找重新加載的更智能的方法,如果它有效的話。

+0

您需要創建兩個單獨的項目,一個用於生成器源,另一個用於生成的源。 – 2012-07-25 14:48:58