2013-06-16 12 views
6

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

@ 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). –

+0

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

@ 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. –

Odpowiedz

5

SBT nie dodaje zasobów bieżącego projektu do ścieżki klasy kompilacji. Prawdopodobnie dlatego, że jest rzadko potrzebny.

Trzeba tylko jedna rura (zasoby) do drugiej (ścieżki klasy):

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 
) 

w tym projekcie byś tylko trzeba, że ​​dla bar i baz.

AKTUALIZACJA: Od sbt 0.13, <+= i <++= są niepotrzebne (i nielegalni) dzięki nowej makro oparte na składni:

libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value 
unmanagedClasspath in Compile ++= (unmanagedResources in Compile).value 
Powiązane problemy