简体   繁体   中英

What is a simple way to define a string constant to be the contents of a file at compile-time (in Scala)?

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, eg, Scala macros, but so far I couldn't work out how to get the String value out simply.

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

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"
}}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM