Suppose I have a Scala project with three subprojects, with these files:
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 contains a simple macro that reads a resource at a given path:
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!") } } }
If the resource can be opened, countLines returns the number of rows; otherwise it is empty.
Two other Scala source files simply call this macro:
object Bar extends App { println(Foo.countLines("/foo.txt")) println(Foo.countLines("/bar.txt")) println(Foo.countLines("/baz.txt")) }
and
object Baz extends App { println(Foo.countLines("/foo.txt")) println(Foo.countLines("/bar.txt")) println(Foo.countLines("/baz.txt")) }
The content of the resources is not particularly relevant for the purpose of this question.
If this is a Maven project, I can easily configure it so that the root project aggregates three subprojects, and baz depends on bar , which depends on foo . See this Gist for gory details.
With Maven, everything works as expected. bar can see resources for foo and bar :
Some(1) Some(2) None
And baz can see all of them:
Some(1) Some(2) Some(3)
Now I try the same with 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" % _) ) }
But now bar can only see resources in foo :
Some(1) None None
And baz can only see foo and bar :
Some(1) Some(2) None
What's going on here? This SBT build file seems like a pretty literal translation of the Maven configuration. I have no problem opening the console in the bar project and reading /bar.txt , for example, so why don't these projects see their own resources when calling the macro?