【问题标题】:Scala run time code compilationScala 运行时代码编译
【发布时间】:2013-10-21 12:45:49
【问题描述】:

我正在尝试将我的 java 代码移植到纯 scala,因此在这方面的任何帮助将不胜感激。

下面的代码首先将我的业务逻辑转换为 java 代码。在这里,我使用 freemarker 模板进行基于模板的代码生成。创建文件后,我使用 java 编译器编译代码并创建一个 jar 文件,该文件保存在临时目录中

我目前正在使用提供运行时编译的javax.tools.* 包。与该方法等效的 Scala 是什么?我想使用 freemarker 模板生成纯 Scala 代码,然后运行 ​​Scala 编译来创建一个 jar 文件。

下面是我用来实现这一点的示例 Java 代码。

    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(javaFileObjects);
    StringBuilder builder = new StringBuilder();
    builder.append(service.getConfig().getProp("coreLib"));
    builder.append(";" +result.getCodeContext().getOmClasspath());
    builder.append(";" +jarBuilder.toString());
    builder.append(";" +service.getConfig().getProp("tempCodeGen"));
    String[] compileOptions = new String[]{"-d", result.getCodeContext().getOmClasspath(),"-cp",builder.toString()} ;
    Iterable<String> compilationOptionss = Arrays.asList(compileOptions);
    DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
    CompilationTask compilerTask = compiler.getTask(null, stdFileManager, diagnostics, compilationOptionss, null, compilationUnits) ;
    boolean status = compilerTask.call();

【问题讨论】:

标签: scala


【解决方案1】:

这是我自己的代码中的一些方法来编译项目并将其打包到 jar 中。它远未完善或正确评论,但希望它会指出您需要从哪里开始。我认为您不需要使用 String Builder,因为这不是性能关键:

def buildAll(name: String, projDir: String, mainClass: String = ""): Unit =
{
  import scala.tools.nsc.{Settings,Global}
  val relSrc : List[String] = List()
  val maniVersion = "None"
  def targDir: String = projDir + "/targ"
  def srcDir: String = projDir + "/src"
  def srcDirs: List[String] = srcDir :: relSrc

  import java.io._
  val sings = new scala.tools.nsc.Settings     
  new File(targDir).mkdir 
  sings.outputDirs.setSingleOutput(targDir.toString)     
  val comp = new Global(sings)      
  val crun: comp.Run  = new comp.Run
  def getList(fName: String): List[String] =
  {
     println("starting getList " + fName)
     val file = new File(fName)
     if (file.isDirectory) file.listFiles.toList.flatMap(i => getList(fName + "/" + i.getName))
     else List(fName)
  }  

  crun.compile(srcDirs.flatMap(i => getList(i))) 
  import sys.process._
  ("cp -r /sdat/projects/ScalaLibs/scala " + targDir + "/scala").!

  import java.util.jar._
  val manif = new Manifest
  val mf = manif.getMainAttributes
  mf.put(Attributes.Name.MANIFEST_VERSION, maniVersion)
  if (mainClass != "") mf.put(Attributes.Name.MAIN_CLASS, mainClass)
  val jarName = name + ".jar"
  val jarOut: JarOutputStream = new JarOutputStream(new FileOutputStream(projDir + "/" + jarName), manif)  
  AddAllToJar(targDir, jarOut)      
  jarOut.close   
}   

def addToJar(jarOut: JarOutputStream, file: File, reldir: String): Unit =
{
  val fName = reldir + file.getName         
  val fNameMod = if (file.isDirectory) fName + "/" else fName
  val entry = new JarEntry(fNameMod)
  entry.setTime(file.lastModified)         
  jarOut.putNextEntry(entry)
  if (file.isDirectory)
  {
     jarOut.closeEntry
     file.listFiles.foreach(i => addToJar(jarOut, i, fName + "/"))
  }
  else
  {         
     var buf = new Array[Byte](1024)
     val in = new FileInputStream(file)
     Stream.continually(in.read(buf)).takeWhile(_ != -1).foreach(jarOut.write(buf, 0, _))
     in.close
     jarOut.closeEntry()
  }         
}
def AddAllToJar(targDir: String, jarOut: JarOutputStream): Unit =
  new java.io.File(targDir).listFiles.foreach(i => addToJar(jarOut, i, ""))

您需要将 Scala 编译器添加到构建路径。 Scala 编译器获取源文件列表并在输出目录中设置的目录中生成已编译的类文件。不过,掌握编译器的全部功能是一项主要任务。

【讨论】:

    【解决方案2】:

    并且在使用 scala 时,您不需要使用任何类似免费标记的工具。由于 scala 版本 2.10 有字符串插值功能。

    val traitName = "MyTrait"
    val packageName = "my.pack"
    val typeParams = List("A", "B", "C")
    
    s"""
      |package ${packageName}
      |
      |trait ${traitName}[${typeParams.mkString(",")}] {
      |  ${typeParams.map(t => s"val ${t.toLowerCase()}: ${t}")}
      |}
      |
    """.stripMargin
    

    将产生:

    package my.pack
    
    trait MyTrait[A,B,C] {
      List(val a: A, val b: B, val c: C)
    }
    

    不需要依赖:)

    【讨论】:

    • 这是一个很好的方法,但是在我的情况下,模板内容存储在一个文件中,字符串插值是一个编译时特性,所以从技术上讲,除非我遗漏了什么,否则它是不可能实现的。见下面的代码 val c2 =Source.fromFile("C:/templates/datadomain.ftl").getLines.mkString datadomain.ftl 的内容 - 包 om.${data.id +"_" +data.version};
    • 请注意,使用准引号 (docs.scala-lang.org/overviews/macros/quasiquotes.html) 后,不再需要使用容易出错的字符串操作来组装 Scala 代码。
    【解决方案3】:

    如果你想在运行时编译生成的代码,最简单的解决方案是twitter eval utility

    【讨论】:

    • 读者注意:twitter eval 将被弃用,不应用于新代码。
    【解决方案4】:

    【讨论】:

    • AFAIK,Eval 不允许您生成代码,例如物理构建代码并将其捆绑为 jar,因此这不是 op 所要求的。不太确定第一个。
    • 当提供标准的“-d”选项时,工具箱会将代码转储到指定目录:github.com/scala/scala/blob/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-16
    相关资源
    最近更新 更多