Implement main method generation in scripts and runnable jar saving

refactor necessary parts on the way
This commit is contained in:
Ilya Chernikov
2019-04-09 08:28:53 +02:00
parent c896b4873b
commit 67ad3773de
14 changed files with 355 additions and 81 deletions
@@ -48,6 +48,6 @@ inline fun <reified T : Any> createJvmEvaluationConfigurationFromTemplate(
): ScriptEvaluationConfiguration = createEvaluationConfigurationFromTemplate(
KotlinType(T::class),
hostConfiguration,
ScriptCompilationConfiguration::class,
ScriptEvaluationConfiguration::class,
body
)
@@ -352,7 +352,6 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
sourceDependencies: List<ScriptsCompilationDependencies.SourceDependencies>,
getScriptConfiguration: (KtFile) -> ScriptCompilationConfiguration
): KJvmCompiledScript<Any> {
val module = makeCompiledModule(generationState)
val scriptDependenciesStack = ArrayDeque<KtScript>()
fun makeOtherScripts(script: KtScript): List<KJvmCompiledScript<*>> {
@@ -371,7 +370,7 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
getScriptConfiguration(sourceFile),
it.fqName.asString(),
makeOtherScripts(it),
module
null
)
}
} ?: emptyList()
@@ -380,6 +379,7 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
return otherScripts
}
val module = makeCompiledModule(generationState)
return KJvmCompiledScript(
script.locationId,
getScriptConfiguration(ktScript.containingKtFile),
@@ -5,15 +5,16 @@
package kotlin.script.experimental.jvmhost
import java.io.File
import java.io.FileOutputStream
import org.jetbrains.kotlin.utils.KotlinPaths
import java.io.*
import java.util.jar.JarEntry
import java.util.jar.JarOutputStream
import java.util.jar.Manifest
import kotlin.script.experimental.api.*
import kotlin.script.experimental.jvm.JvmDependency
import kotlin.script.experimental.jvm.impl.*
import kotlin.script.experimental.jvm.util.scriptCompilationClasspathFromContext
import kotlin.script.experimental.jvmhost.impl.KJvmCompiledModuleInMemory
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
// TODO: generate execution code (main)
@@ -45,6 +46,41 @@ open class BasicJvmScriptClassFilesGenerator(val outputDir: File) : ScriptEvalua
}
}
fun KJvmCompiledScript<*>.saveToJar(outputJar: File) {
val module = (compiledModule as? KJvmCompiledModuleInMemory)
?: throw IllegalArgumentException("Unsupported module type $compiledModule")
val dependenciesFromScript = compilationConfiguration[ScriptCompilationConfiguration.dependencies]
?.filterIsInstance<JvmDependency>()
?.flatMap { it.classpath }
.orEmpty()
val dependenciesForMain = scriptCompilationClasspathFromContext(
KotlinPaths.Jar.ScriptingLib.baseName, KotlinPaths.Jar.ScriptingJvmLib.baseName, KotlinPaths.Jar.CoroutinesCore.baseName,
classLoader = this::class.java.classLoader,
wholeClasspath = false
)
val dependencies = (dependenciesFromScript + dependenciesForMain).distinct()
FileOutputStream(outputJar).use { fileStream ->
val manifest = Manifest()
manifest.mainAttributes.apply {
putValue("Manifest-Version", "1.0")
putValue("Created-By", "JetBrains Kotlin")
if (dependencies.isNotEmpty()) {
// TODO: implement options for various cases - paths as is (now), absolute paths (local execution only), names only (most likely as a hint only), fat jar
putValue("Class-Path", dependencies.joinToString(" "))
}
putValue("Main-Class", scriptClassFQName)
}
// TODO: fat jar/dependencies
val jarStream = JarOutputStream(fileStream, manifest)
jarStream.putNextEntry(JarEntry(scriptMetadataPath(scriptClassFQName)))
jarStream.write(copyWithoutModule().toBytes())
for ((path, bytes) in module.compilerOutputFiles) {
jarStream.putNextEntry(JarEntry(path))
jarStream.write(bytes)
}
jarStream.finish()
}
}
open class BasicJvmScriptJarGenerator(val outputJar: File) : ScriptEvaluator {
@@ -55,29 +91,7 @@ open class BasicJvmScriptJarGenerator(val outputJar: File) : ScriptEvaluator {
try {
if (compiledScript !is KJvmCompiledScript<*>)
return failure("Cannot generate jar: unsupported compiled script type $compiledScript")
val module = (compiledScript.compiledModule as? KJvmCompiledModuleInMemory)
?: return failure("Cannot generate jar: unsupported module type ${compiledScript.compiledModule}")
val dependencies = compiledScript.compilationConfiguration[ScriptCompilationConfiguration.dependencies]
?.filterIsInstance<JvmDependency>()
?.flatMap { it.classpath }
.orEmpty()
FileOutputStream(outputJar).use { fileStream ->
val manifest = Manifest()
manifest.mainAttributes.apply {
putValue("Manifest-Version", "1.0")
putValue("Created-By", "JetBrains Kotlin")
if (dependencies.isNotEmpty()) {
putValue("Class-Path", dependencies.joinToString(" ") { it.name })
}
}
// TODO: fat jar/dependencies
val jarStream = JarOutputStream(fileStream, manifest)
for ((path, bytes) in module.compilerOutputFiles) {
jarStream.putNextEntry(JarEntry(path))
jarStream.write(bytes)
}
jarStream.finish()
}
compiledScript.saveToJar(outputJar)
return ResultWithDiagnostics.Success(EvaluationResult(ResultValue.Unit, scriptEvaluationConfiguration))
} catch (e: Throwable) {
return ResultWithDiagnostics.Failure(