2012-07-16 24 views
14

Tôi muốn biên dịch một dự án có chứa trình tạo mã nguồn java và sau đó biên dịch mã được tạo trong một dự án. I.e: biên dịch Generator.scala, chạy Generator.generate (outputDir), biên dịch outputDir, gói thành một cái lọ. Tôi đang cố gắng này:SBT tạo mã bằng cách sử dụng trình tạo dự án được xác định

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

nhưng SBT phàn nàn

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

Về cơ bản, SBT không thấy Generator được định nghĩa trong dự án nó biên dịch. Có thể thực hiện theo cách của tôi với sbt không?

+0

Tôi cũng đã đấu vật với kịch bản chính xác này. Tôi không có câu trả lời cho bạn, vẫn là một newbie sbt. Nhưng sẽ chờ đợi một câu trả lời là tốt. –

Trả lời

13

Vì vậy, sau khi tìm hiểu về điều này một chút, tôi đã đưa ra một giải pháp. Trước tiên, bạn cần phải phá vỡ dự án của bạn thành hai dự án phụ. gen có tất cả các nguồn bao gồm mã máy phát điện của bạn. use tùy thuộc vào gen và sử dụng trình tạo.

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

Trong trường hợp này, tôi đã tạo các tệp .java để tôi truyền vào máy phát điện javaSource. Điều quan trọng là không phải khi sử dụng các bộ phát nguồn như chúng ta đang ở đây, tác vụ được thực thi phải trả về một Seq[File] của tất cả các tệp được tạo để sbt có thể quản lý chúng. Trong quá trình thực hiện này, trình tạo của chúng tôi xuất ra các tên tệp đường dẫn đầy đủ để tiêu chuẩn và chúng tôi lưu chúng vào một tệp tạm thời.

Như với tất cả mọi thứ Scala và chắc chắn SBT, bạn có thể làm bất cứ điều gì, chỉ cần đào sâu vào nó.

+0

Bài viết tuyệt vời, điều này làm việc cho tôi, mặc dù tôi thích sử dụng 'sourceManaged in Compile' làm thư mục đầu ra (như được khuyến nghị trong các tài liệu sbt). –

+0

Ngoài ra, tôi nghĩ bạn không nên sử dụng (và cần) '.dependsOn (gen)', bởi vì khi bạn xuất bản dự án của bạn, bạn sẽ có một sự phụ thuộc thư viện không cần thiết từ 'use' thành' gen'. –

+0

Làm thế nào để bạn thực hiện Fork.ForkScala trong sbt 1.0 trở lên? – ChoppyTheLumberjack

1

Mô tả dự án được biên dịch khi tải nó. Không có cách nào để gọi trực tiếp mã mới được tạo ra khi chạy. Trừ khi tôi đoán sử dụng một số loại phản chiếu nào đó để đảm bảo không có sự can thiệp nào của JVM và bằng cách nào đó có các lớp đó được nạp vào trình nạp lớp.

Cách duy nhất tôi có thể nghĩ là làm một dự án bên trong định nghĩa dự án của bạn.

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

Trong định nghĩa dự án bên trong nhất bạn có thể xác định src bên ngoài làm thư mục src. Điều đó sẽ giúp bạn có được một phiên bản biên dịch của Máy phát điện có sẵn cho dự án thực. Sau đó, trong định nghĩa dự án bình thường thêm một nhập khẩu vào máy phát điện và sử dụng nó như bạn đang làm.

Tôi khá chắc chắn rằng dự án bên trong nhất sẽ chỉ được tải và biên dịch một lần. Bạn sẽ cần phải có sbt tải lại định nghĩa dự án nếu bạn thay đổi Generator. Thoát và mở lại là cách đơn giản/ngu ngốc nhất để thực hiện nhưng nó có thể giúp kiểm tra. Tìm kiếm các cách tải lại thông minh hơn sau này nếu nó hoạt động.

+0

Bạn cần tạo hai dự án riêng biệt, một cho nguồn máy phát và một cho nguồn 'created'. –

Các vấn đề liên quan