10

So far I have

import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler

  MyProjectCompiler.initialize("SampleKtFileOutput")
    .packageName("com.test.sample")
    .compile(File(someFile.path))
    .result { ktSource: String -> K2JVMCompiler()
       .exec(System.out, /** arguments here?*/) }

This manually starts the compiler, but I would like to compile the resulting String from the first compiler (MyProjectCompiler which generates kotlin source) in-memory and check the result without writing to a file.

I would like to include everything on the current classpath if possible.

4

2 回答 2

5

我发现最简单的方法是使用原始问题中的代码并使用java.io.tmpdir. 这是一个可重复使用的解决方案:

添加 kotlin 编译器作为测试依赖项:

testCompile group: 'org.jetbrains.kotlin', name: 'kotlin-compiler', version: "$kotlin_version"

编译器的包装器:

object JvmCompile {

  fun exe(input: File, output: File): Boolean = K2JVMCompiler().run {
    val args = K2JVMCompilerArguments().apply {
      freeArgs = listOf(input.absolutePath)
      loadBuiltInsFromDependencies = true
      destination = output.absolutePath
      classpath = System.getProperty("java.class.path")
          .split(System.getProperty("path.separator"))
          .filter {
            it.asFile().exists() && it.asFile().canRead()
          }.joinToString(":")
      noStdlib = true
      noReflect = true
      skipRuntimeVersionCheck = true
      reportPerf = true
    }
    output.deleteOnExit()
    execImpl(
        PrintingMessageCollector(
            System.out,
            MessageRenderer.WITHOUT_PATHS, true),
        Services.EMPTY,
        args)
  }.code == 0

}

用于从已编译的类创建对象的类加载器:

class Initializer(private val root: File) {

  val loader = URLClassLoader(
      listOf(root.toURI().toURL()).toTypedArray(),
      this::class.java.classLoader)

  @Suppress("UNCHECKED_CAST") 
  inline fun <reified T> loadCompiledObject(clazzName: String): T? 
      = loader.loadClass(clazzName).kotlin.objectInstance as T

  @Suppress("UNCHECKED_CAST") 
  inline fun <reified T> createInstance(clazzName: String): T? 
      = loader.loadClass(clazzName).kotlin.createInstance() as T

}

示例测试用例:

首先制作一个kotlin源文件

MockClasswriter("""
    |
    |package com.test
    |
    |class Example : Consumer<String> {
    |  override fun accept(value: String) {
    |    println("found: '$\value'")
    |  }
    |}
    """.trimMargin("|"))
    .writeToFile(codegenOutputFile)

确保它编译:

assertTrue(JvmCompile.exe(codegenOutputFile, compileOutputDir))

加载类作为接口实例

Initializer(compileOutputDir)
      .createInstance<Consumer<String>>("com.test.Example")
      ?.accept("Hello, world!")

输出将如预期:found: 'Hello, world!'

于 2017-09-17T17:00:05.073 回答
0

看了类的源码K2JVMCompiler,好像编译器只支持对文件的编译。org.jetbrains.kotlin.codegen.KotlinCodegenFacade深入挖掘,伪造静态方法的条目似乎过于复杂compileCorrectFiles

您最好猜测它使用文件系统来执行此操作。临时 RAM 磁盘可能适合您的需要。(例如,这是内置的 macOS )

于 2017-09-02T20:46:32.103 回答