Załóżmy Mam projektu Scala z trzech podprojektów, z plikami tak:Czytanie zasobów z makro w projekcie 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
zawiera proste makro, które wczytuje zasobu w danej ścieżce:
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!")
}
}
}
Jeśli zasób można otworzyć, countLines
zwraca liczbę linii; w przeciwnym razie jest pusty.
Pozostałe dwa pliki źródłowe Scala zadzwoń tego makra:
object Bar extends App {
println(Foo.countLines("/foo.txt"))
println(Foo.countLines("/bar.txt"))
println(Foo.countLines("/baz.txt"))
}
oraz:
object Baz extends App {
println(Foo.countLines("/foo.txt"))
println(Foo.countLines("/bar.txt"))
println(Foo.countLines("/baz.txt"))
}
Zawartość zasobów nie ma znaczenia dla celów tego pytania.
Jeśli jest to projekt Maven, mogę go łatwo skonfigurować tak, aby główny projekt agregował trzy podprojekty i baz
w zależności od bar
, która zależy od foo
. Zobacz this Gist, aby poznać szczegóły.
Z Maven wszystko działa zgodnie z oczekiwaniami. Bar
widzi zasoby dla foo
i bar
:
Some(1)
Some(2)
None
I Baz
można zobaczyć wszystkie z nich:
Some(1)
Some(2)
Some(3)
Teraz próbuję samo z 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" % _)
)
}
Ale teraz Bar
może wyświetlać tylko zasoby w foo
:
Some(1)
None
None
I Baz
widzi tylko foo
i bar
:
Some(1)
Some(2)
None
Co tu się dzieje? Ten plik budujący SBT wydaje mi się całkiem dosłownym tłumaczeniem konfiguracji Maven. Nie mam problemu z otwarciem konsoli w projekcie bar
i przeczytaniem na przykład /bar.txt
, więc dlaczego te projekty nie mogą zobaczyć swoich zasobów podczas wywoływania makra?
@ 0__: Dzięki, powinienem wspomnieć, że owszem, są tam (i są dostępne z nie- kod makra zarówno w konsoli, jak iw źródle projektu). –
Nie ekspert od makr, ale musi to być IMO klasy programu ładującego klasy. W którym miejscu jest dokładnie wykonywane 'this.getClass.getResourceAsStream'? Być może musisz użyć kontekstu lub czegoś innego jako odwołania do modułu ładującego klasy? Nie ma pojęcia ... –
@ 0__: Zakładam, że to coś takiego, ale żaden z moich machnięć nie trafił w rozwiązanie, a fakt, że działa zgodnie z oczekiwaniami w przypadku Mavena sprawia, że myślę, że powinno to być coś prostego. –