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

拥有回忆 提交于 2021-02-19 06:38:04

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!