问题
I would like to define a Scala val to be a String whose value is the entire contents of a text file, as read at compile time. So, after compilation, this should just behave as a string constant.
I have the feeling that this should be simple with, e.g., Scala macros, but so far I couldn't work out how to get the String value out simply.
回答1:
You could make an sbt source generation task that executes at compile time; here's a simple example copied from those docs. It wouldn't be hard to extend this as needed to just dump file contents into a object's val field.
sourceGenerators in Compile += Def.task {
val file = (sourceManaged in Compile).value / "demo" / "Test.scala"
IO.write(file, """object Test extends App { println("Hi") }""")
Seq(file)
}.taskValue
回答2:
The simple macro. This probably exists as an example somewhere. But I need the exercise.
scala> import reflect.macros.blackbox.Context
import reflect.macros.blackbox.Context
scala> import language.experimental.macros
import language.experimental.macros
scala> import scala.io._
import scala.io._
scala> :pa
// Entering paste mode (ctrl-D to finish)
class S(val c: Context) {
import c._, universe._
def smac(file: c.Expr[String]): c.Expr[String] = file.tree match {
case Literal(Constant(s: String)) =>
val res = Source.fromFile(s, "UTF-8").getLines.mkString("\n")
c.Expr[String](Literal(Constant(res)))
}}
// Exiting paste mode, now interpreting.
defined class S
scala> def f(file: String): String = macro S.smac
defined term macro f: (file: String)String
scala> f("text.txt")
res3: String = Now is the time for jumping over dogs.
Or, quasiquoting the result:
class S(val c: Context) {
import c._, universe._
def smac(file: c.Expr[String]) = file.tree match {
case Literal(Constant(s: String)) =>
val res = Source.fromFile(s, "UTF-8").getLines.mkString("\n")
q"$res"
}}
回答3:
As a workaround you can write a SBT plugin (I assume you are building your project with SBT) for this purpose.
Consider this example:
infoFiles := {
val base = (baseDirectory in Compile).value
val file = base / "project" / "BuildInfo.scala"
val content = "package projectbuildinfo\n" +
"object BuildInfo {\n" +
"val name = \"" + name.value + "\"\n" +
"val version = \"" + version.value + "\"\n" +
"val artifact = \"" + (artifactPath in (Compile, packageBin)).value.getPath.dropRight(3) + "war" + "\"\n" +
"val artifactName = \"" + (artifactPath in (Compile, packageBin)).value.getName.dropRight(3) + "war" + "\"\n" +
"}\n"
IO.write(file, content)
Seq(file)
},
sourceGenerators in Compile <+= infoFiles,
This task generates BuildInfo.scala
on every build. You can basically read your file and store it content in a Scala class file.
来源:https://stackoverflow.com/questions/33785495/what-is-a-simple-way-to-define-a-string-constant-to-be-the-contents-of-a-file-at