Reputation: 1225
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.
Upvotes: 10
Views: 2114
Reputation: 1225
I found the easiest way to do it is to use something like the code in the original question and use java.io.tmpdir
. Here's a re-usable solution:
Add the kotlin compiler as a test dependency:
testCompile group: 'org.jetbrains.kotlin', name: 'kotlin-compiler', version: "$kotlin_version"
Wrapper for the compiler:
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
}
Classloader for creating objects from the compiled classes:
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
}
Example test case:
First make a kotlin source file
MockClasswriter("""
|
|package com.test
|
|class Example : Consumer<String> {
| override fun accept(value: String) {
| println("found: '$\value'")
| }
|}
""".trimMargin("|"))
.writeToFile(codegenOutputFile)
Make sure it compiles:
assertTrue(JvmCompile.exe(codegenOutputFile, compileOutputDir))
Load the class as interface instance
Initializer(compileOutputDir)
.createInstance<Consumer<String>>("com.test.Example")
?.accept("Hello, world!")
The output will be as expected: found: 'Hello, world!'
Upvotes: 5
Reputation: 2155
Reading the source of the K2JVMCompiler
class, it seems that the compiler only supports compilation for files. Digging deeper, it seems overcomplicated to fake the entries of org.jetbrains.kotlin.codegen.KotlinCodegenFacade
static method compileCorrectFiles
.
Your best guess it to use a file system to do this. A temporary RAM disk may suit your needs. (This is built-in macOS for example)
Upvotes: 0