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 foo
và bar
:
Some(1)
Some(2)
None
Và 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
Và Baz
chỉ có thể xem foo
và bar
:
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__: 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). –
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__: 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. –