Reading resources from a macro in an SBT project

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?

+6
source share
1 answer

SBT does not add the resources of the current project to the assembly class path. Probably because it is rarely necessary.

You just need to connect one (resources) to another (class path):

 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 ) 

In this project you will only need for bar and baz .

UPDATE:. Since sbt 0.13, <+= and <++= not needed (and undocumented) thanks to the new macro-based syntax:

 libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value unmanagedClasspath in Compile ++= (unmanagedResources in Compile).value 
+5
source

Source: https://habr.com/ru/post/947405/


All Articles