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.
Yes, when targeting the JVM, Kotlin is compiled to JVM *. class files, which is a bytecode format that can later be either interpreted by a JVM, or compiled to the machine code by the JVM during the program run (JIT), or even compiled ahead-of-time (AOT) down to the machine code.
No, kotlinc compiles only Kotlin files ( . kt ). A mixed-language project requires combining both kotlinc and javac .
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!'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With