2013-06-16 40 views
6

Giả sử tôi có một dự án với ba tiểu dự án Scala, với các tập tin như thế này:nguồn Reading từ một macro trong một dự án SBT

foo/src/main/scala/Foo.scala 
foo/src/main/resources/foo.txt 

bar/src/main/scala/Bar.scala 
bar/src/main/resources/bar.txt 

baz/src/main/scala/Baz.scala 
baz/src/main/resources/baz.txt 

Foo.scala chứa một macro đơn giản mà đọc một tài nguyên tại một con đường nhất định:

import scala.language.experimental.macros 
import scala.reflect.macros.Context 

object Foo { 
    def countLines(path: String): Option[Int] = macro countLines_impl 

    def countLines_impl(c: Context)(path: c.Expr[String]) = { 
    import c.universe._ 

    path.tree match { 
     case Literal(Constant(s: String)) => Option(
     this.getClass.getResourceAsStream(s) 
    ).fold(reify(None: Option[Int])) { stream => 
     val count = c.literal(io.Source.fromInputStream(stream).getLines.size) 
     reify(Some(count.splice)) 
     } 
     case _ => c.abort(c.enclosingPosition, "Need a literal path!") 
    } 
    } 
} 

Nếu tài nguyên có thể được mở, countLines trả về số dòng; nếu không thì nó trống.

Hai file nguồn Scala khác chỉ cần gọi macro này:

object Bar extends App { 
    println(Foo.countLines("/foo.txt")) 
    println(Foo.countLines("/bar.txt")) 
    println(Foo.countLines("/baz.txt")) 
} 

Và:

object Baz extends App { 
    println(Foo.countLines("/foo.txt")) 
    println(Foo.countLines("/bar.txt")) 
    println(Foo.countLines("/baz.txt")) 
} 

Nội dung của các nguồn lực không thực sự quan trọng cho các mục đích của câu hỏi này.

Nếu đây là dự án Maven, tôi có thể dễ dàng định cấu hình để dự án gốc tổng hợp ba tiểu dự án và baz tùy thuộc vào bar, phụ thuộc vào foo. Xem this Gist để biết chi tiết đẫm máu.

Với mọi thứ Maven hoạt động như mong đợi. Bar có thể thấy các nguồn lực cho foobar:

Some(1) 
Some(2) 
None 

Baz có thể xem tất cả trong số họ:

Some(1) 
Some(2) 
Some(3) 

Bây giờ tôi cố gắng điều tương tự với SBT:

import sbt._ 
import Keys._ 

object MyProject extends Build { 
    lazy val root: Project = Project(
    id = "root", base = file("."), 
    settings = commonSettings 
).aggregate(foo, bar, baz) 

    lazy val foo: Project = Project(
    id = "foo", base = file("foo"), 
    settings = commonSettings 
) 

    lazy val bar: Project = Project(
    id = "bar", base = file("bar"), 
    settings = commonSettings, 
    dependencies = Seq(foo) 
) 

    lazy val baz: Project = Project(
    id = "baz", base = file("baz"), 
    settings = commonSettings, 
    dependencies = Seq(bar) 
) 

    def commonSettings = Defaults.defaultSettings ++ Seq(
    scalaVersion := "2.10.2", 
    libraryDependencies <+= scalaVersion("org.scala-lang" % "scala-compiler" % _) 
) 
} 

Nhưng bây giờ Bar chỉ có thể xem các tài nguyên trong foo:

Some(1) 
None 
None 

Baz chỉ có thể xem foobar:

Some(1) 
Some(2) 
None 

gì đang xảy ra ở đây? Tập tin xây dựng SBT này dường như là một bản dịch khá đẹp về cấu hình Maven. Tôi không có vấn đề gì khi mở một giao diện điều khiển trong dự án bar và đọc /bar.txt, vậy tại sao các dự án này không thể xem tài nguyên của riêng họ khi gọi macro?

+0

@ 0__: Cảm ơn, tôi có nên nói rằng-vâng, họ đang có (và có sẵn từ phi mã macro cả trong bảng điều khiển và trong nguồn dự án). –

+0

Không phải là chuyên gia về macro, nhưng nó phải là một trình tải lớp học IMO. Tại điểm nào chính xác là 'this.getClass.getResourceAsStream' được thực hiện? Có lẽ bạn cần sử dụng ngữ cảnh hoặc thứ gì đó khác làm tham chiếu bộ nạp lớp? Không có đầu mối ... –

+0

@ 0__: Tôi giả định nó giống như vậy, nhưng không có lỗi nào của tôi đánh một giải pháp, và thực tế là nó hoạt động như mong đợi trong trường hợp Maven khiến tôi nghĩ rằng nó phải là một cái gì đó đơn giản. –

Trả lời

5

SBT không thêm tài nguyên của dự án hiện tại vào đường dẫn lớp xây dựng. Có lẽ vì nó hiếm khi cần thiết.

Bạn chỉ cần một ống (các nguồn lực) vào khác (classpath):

def commonSettings = Defaults.defaultSettings ++ Seq(
    scalaVersion := "2.10.2", 
    libraryDependencies <+= scalaVersion("org.scala-lang" % "scala-compiler" % _), 
    // add resources of the current project into the build classpath 
    unmanagedClasspath in Compile <++= unmanagedResources in Compile 
) 

Trong dự án mà bạn sẽ chỉ cần điều đó cho barbaz.

CẬP NHẬT: Vì sbt 0.13, <+=<++= là không cần thiết (và không có giấy tờ) nhờ cú pháp vĩ mô mới dựa trên:

libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value 
unmanagedClasspath in Compile ++= (unmanagedResources in Compile).value 
Các vấn đề liên quan