Implement additional compiler arguments support in scripts
This commit is contained in:
+229
-87
@@ -11,17 +11,21 @@ import com.intellij.openapi.vfs.CharsetToolkit
|
|||||||
import com.intellij.psi.PsiFileFactory
|
import com.intellij.psi.PsiFileFactory
|
||||||
import com.intellij.psi.impl.PsiFileFactoryImpl
|
import com.intellij.psi.impl.PsiFileFactoryImpl
|
||||||
import com.intellij.testFramework.LightVirtualFile
|
import com.intellij.testFramework.LightVirtualFile
|
||||||
|
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||||
|
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||||
|
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
|
||||||
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
|
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
|
||||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||||
|
import org.jetbrains.kotlin.cli.common.setupCommonArguments
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.*
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.*
|
import org.jetbrains.kotlin.cli.jvm.compiler.*
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
|
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
||||||
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
|
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
||||||
import org.jetbrains.kotlin.codegen.CompilationErrorHandler
|
import org.jetbrains.kotlin.codegen.CompilationErrorHandler
|
||||||
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
||||||
@@ -34,13 +38,12 @@ import org.jetbrains.kotlin.name.NameUtils
|
|||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.KtScript
|
import org.jetbrains.kotlin.psi.KtScript
|
||||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||||
import org.jetbrains.kotlin.script.util.KotlinJars
|
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||||
import java.io.File
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
import kotlin.reflect.full.starProjectedType
|
import kotlin.reflect.full.starProjectedType
|
||||||
|
import kotlin.script.dependencies.ScriptContents
|
||||||
import kotlin.script.experimental.api.*
|
import kotlin.script.experimental.api.*
|
||||||
import kotlin.script.experimental.dependencies.DependenciesResolver
|
import kotlin.script.experimental.dependencies.DependenciesResolver
|
||||||
import kotlin.script.experimental.host.FileScriptSource
|
import kotlin.script.experimental.host.FileScriptSource
|
||||||
@@ -51,6 +54,7 @@ import kotlin.script.experimental.jvm.JvmDependency
|
|||||||
import kotlin.script.experimental.jvm.impl.BridgeDependenciesResolver
|
import kotlin.script.experimental.jvm.impl.BridgeDependenciesResolver
|
||||||
import kotlin.script.experimental.jvm.jdkHome
|
import kotlin.script.experimental.jvm.jdkHome
|
||||||
import kotlin.script.experimental.jvm.jvm
|
import kotlin.script.experimental.jvm.jvm
|
||||||
|
import kotlin.script.experimental.jvm.util.KotlinJars
|
||||||
import kotlin.script.experimental.jvm.withUpdatedClasspath
|
import kotlin.script.experimental.jvm.withUpdatedClasspath
|
||||||
import kotlin.script.experimental.jvmhost.KJvmCompilerProxy
|
import kotlin.script.experimental.jvmhost.KJvmCompilerProxy
|
||||||
import kotlin.script.experimental.util.getOrError
|
import kotlin.script.experimental.util.getOrError
|
||||||
@@ -67,94 +71,222 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
|
|||||||
ResultWithDiagnostics.Failure(*messageCollector.diagnostics.toTypedArray(), *diagnostics)
|
ResultWithDiagnostics.Failure(*messageCollector.diagnostics.toTypedArray(), *diagnostics)
|
||||||
|
|
||||||
fun failure(message: String): ResultWithDiagnostics.Failure =
|
fun failure(message: String): ResultWithDiagnostics.Failure =
|
||||||
ResultWithDiagnostics.Failure(
|
failure(message.asErrorDiagnostics(path = script.locationId))
|
||||||
*messageCollector.diagnostics.toTypedArray(),
|
|
||||||
message.asErrorDiagnostics(path = script.locationId)
|
val disposable = Disposer.newDisposable()
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setIdeaIoUseFallback()
|
setIdeaIoUseFallback()
|
||||||
|
|
||||||
var environment: KotlinCoreEnvironment? = null
|
// TODO: refactor/cleanup when the internal resolving API will allow easier info passing between resolver and compiler
|
||||||
var updatedConfiguration = scriptCompilationConfiguration
|
|
||||||
|
|
||||||
fun updateClasspath(classpath: List<File>) {
|
val kotlinCompilerConfiguration = createInitialCompilerConfiguration(scriptCompilationConfiguration, messageCollector)
|
||||||
environment!!.updateClasspath(classpath.map(::JvmClasspathRoot))
|
|
||||||
|
|
||||||
updatedConfiguration = updatedConfiguration.withUpdatedClasspath(classpath)
|
val initialScriptCompilationConfiguration =
|
||||||
}
|
scriptCompilationConfiguration.withUpdatesFromCompilerConfiguration(kotlinCompilerConfiguration)
|
||||||
|
|
||||||
val disposable = Disposer.newDisposable()
|
val sourcesWithRefinementsState = SourcesWithRefinedConfigurations(script)
|
||||||
val kotlinCompilerConfiguration = org.jetbrains.kotlin.config.CompilerConfiguration().apply {
|
|
||||||
add(
|
|
||||||
JVMConfigurationKeys.SCRIPT_DEFINITIONS,
|
|
||||||
BridgeScriptDefinition(scriptCompilationConfiguration, hostConfiguration, ::updateClasspath)
|
|
||||||
)
|
|
||||||
put<MessageCollector>(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
|
|
||||||
put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
|
|
||||||
|
|
||||||
var isModularJava = false
|
kotlinCompilerConfiguration.add(
|
||||||
(updatedConfiguration.getNoDefault(ScriptCompilationConfiguration.jvm.jdkHome)
|
JVMConfigurationKeys.SCRIPT_DEFINITIONS,
|
||||||
?: hostConfiguration[ScriptingHostConfiguration.jvm.jdkHome])?.let {
|
makeScriptDefinition(initialScriptCompilationConfiguration, script, sourcesWithRefinementsState)
|
||||||
put(JVMConfigurationKeys.JDK_HOME, it)
|
|
||||||
isModularJava = CoreJrtFileSystem.isModularJdk(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedConfiguration[ScriptCompilationConfiguration.dependencies]?.let { dependencies ->
|
|
||||||
addJvmClasspathRoots(
|
|
||||||
dependencies.flatMap {
|
|
||||||
(it as JvmDependency).classpath
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: implement logic similar to compiler's -no-stdlib (and -no-reflect?)
|
|
||||||
val standardLibs = arrayListOf("kotlin.stdlib" to KotlinJars.stdlib)
|
|
||||||
KotlinJars.scriptRuntimeOrNull?.let { standardLibs.add("kotlin.script.runtime" to it) }
|
|
||||||
|
|
||||||
for ((moduleName, file) in standardLibs) {
|
|
||||||
if (isModularJava) {
|
|
||||||
add(CLIConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(file))
|
|
||||||
add(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES, moduleName)
|
|
||||||
} else {
|
|
||||||
add(CLIConfigurationKeys.CONTENT_ROOTS, JvmClasspathRoot(file))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updatedConfiguration = updatedConfiguration.withUpdatedClasspath(standardLibs.map { it.second })
|
|
||||||
|
|
||||||
put(CommonConfigurationKeys.MODULE_NAME, "kotlin-script") // TODO" take meaningful and valid name from somewhere
|
|
||||||
languageVersionSettings = LanguageVersionSettingsImpl(
|
|
||||||
LanguageVersion.LATEST_STABLE, ApiVersion.LATEST_STABLE, mapOf(AnalysisFlags.skipMetadataVersionCheck to true)
|
|
||||||
)
|
|
||||||
|
|
||||||
put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.JVM_1_8)
|
|
||||||
}
|
|
||||||
environment = KotlinCoreEnvironment.createForProduction(
|
|
||||||
disposable,
|
|
||||||
kotlinCompilerConfiguration,
|
|
||||||
EnvironmentConfigFiles.JVM_CONFIG_FILES
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val analyzerWithCompilerReport = AnalyzerWithCompilerReport(messageCollector, environment.configuration.languageVersionSettings)
|
val environment = KotlinCoreEnvironment.createForProduction(
|
||||||
|
disposable, kotlinCompilerConfiguration, EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||||
|
)
|
||||||
|
|
||||||
val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl
|
val mainKtFile = getMainKtFile(script, initialScriptCompilationConfiguration, environment)
|
||||||
val scriptText = getMergedScriptText(script, updatedConfiguration)
|
|
||||||
val scriptFileName = script.name ?: "script.${updatedConfiguration[ScriptCompilationConfiguration.fileExtension]}"
|
|
||||||
|
|
||||||
val virtualFile = ScriptLightVirtualFile(scriptFileName, (script as? FileScriptSource)?.file?.path, scriptText)
|
|
||||||
|
|
||||||
val psiFile: KtFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile?
|
|
||||||
?: return failure("Unable to make PSI file from script")
|
?: return failure("Unable to make PSI file from script")
|
||||||
|
val ktScript = mainKtFile.declarations.firstIsInstanceOrNull<KtScript>()
|
||||||
val ktScript = psiFile.declarations.firstIsInstanceOrNull<KtScript>()
|
|
||||||
?: return failure("Not a script file")
|
?: return failure("Not a script file")
|
||||||
|
|
||||||
val sourceFiles = arrayListOf(psiFile)
|
val sourceFiles = arrayListOf(mainKtFile)
|
||||||
val (classpath, newSources, sourceDependencies) =
|
val (classpath, newSources, sourceDependencies) =
|
||||||
collectScriptsCompilationDependencies(kotlinCompilerConfiguration, environment.project, sourceFiles)
|
collectScriptsCompilationDependencies(kotlinCompilerConfiguration, environment.project, sourceFiles)
|
||||||
|
|
||||||
|
// TODO: consider removing, it is probably redundant: the actual index update is performed with environment.updateClasspath
|
||||||
kotlinCompilerConfiguration.addJvmClasspathRoots(classpath)
|
kotlinCompilerConfiguration.addJvmClasspathRoots(classpath)
|
||||||
|
environment.updateClasspath(classpath.map(::JvmClasspathRoot))
|
||||||
|
|
||||||
sourceFiles.addAll(newSources)
|
sourceFiles.addAll(newSources)
|
||||||
|
|
||||||
|
// collectScriptsCompilationDependencies calls resolver for every file, so at this point all updated configurations are collected
|
||||||
|
environment.configuration.updateWithRefinedConfigurations(
|
||||||
|
initialScriptCompilationConfiguration, sourcesWithRefinementsState.refinedConfigurations
|
||||||
|
)
|
||||||
|
|
||||||
|
val analysisResult = analyze(sourceFiles, environment)
|
||||||
|
|
||||||
|
if (!analysisResult.shouldGenerateCode) return failure("no code to generate")
|
||||||
|
if (analysisResult.isError() || messageCollector.hasErrors()) return failure()
|
||||||
|
|
||||||
|
val generationState = generate(analysisResult, sourceFiles, kotlinCompilerConfiguration)
|
||||||
|
|
||||||
|
val compiledScript =
|
||||||
|
makeCompiledScript(generationState, script, ktScript, sourceDependencies) { ktFile ->
|
||||||
|
sourcesWithRefinementsState.refinedConfigurations.entries.find { ktFile.name == it.key.name }?.value
|
||||||
|
?: initialScriptCompilationConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultWithDiagnostics.Success(compiledScript, messageCollector.diagnostics)
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
return failure(ex.asDiagnostics(path = script.locationId))
|
||||||
|
} finally {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SourcesWithRefinedConfigurations(rootScript: SourceCode) {
|
||||||
|
val knownSources = hashSetOf(rootScript)
|
||||||
|
val refinedConfigurations = hashMapOf<SourceCode, ScriptCompilationConfiguration>()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeScriptDefinition(
|
||||||
|
scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||||
|
mainScript: SourceCode,
|
||||||
|
sourcsesWithConfigurationsState: SourcesWithRefinedConfigurations
|
||||||
|
): BridgeScriptDefinition =
|
||||||
|
BridgeScriptDefinition(
|
||||||
|
scriptCompilationConfiguration,
|
||||||
|
hostConfiguration,
|
||||||
|
{ script, updatedConfiguration ->
|
||||||
|
sourcsesWithConfigurationsState.refinedConfigurations[script] = updatedConfiguration
|
||||||
|
updatedConfiguration[ScriptCompilationConfiguration.importScripts]?.let {
|
||||||
|
sourcsesWithConfigurationsState.knownSources.addAll(it)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ scriptContents ->
|
||||||
|
val name = scriptContents.file?.name
|
||||||
|
sourcsesWithConfigurationsState.knownSources.find {
|
||||||
|
// TODO: consider using merged text (likely should be cached)
|
||||||
|
// on the other hand it may become obsolete when scripting internals will be redesigned properly
|
||||||
|
(name != null && name == it.scriptFileName(
|
||||||
|
mainScript,
|
||||||
|
scriptCompilationConfiguration
|
||||||
|
)) || it.text == scriptContents.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun ScriptCompilationConfiguration.withUpdatesFromCompilerConfiguration(kotlinCompilerConfiguration: CompilerConfiguration) =
|
||||||
|
withUpdatedClasspath(kotlinCompilerConfiguration.jvmClasspathRoots)
|
||||||
|
|
||||||
|
private fun createInitialCompilerConfiguration(
|
||||||
|
scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||||
|
messageCollector: MessageCollector
|
||||||
|
): CompilerConfiguration {
|
||||||
|
|
||||||
|
val baseArguments = K2JVMCompilerArguments()
|
||||||
|
parseCommandLineArguments(
|
||||||
|
scriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions] ?: emptyList(),
|
||||||
|
baseArguments
|
||||||
|
)
|
||||||
|
|
||||||
|
return org.jetbrains.kotlin.config.CompilerConfiguration().apply {
|
||||||
|
|
||||||
|
// default value differs from the argument'ss default (see #KT-29405 and #KT-29319)
|
||||||
|
put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.JVM_1_8)
|
||||||
|
|
||||||
|
put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
|
||||||
|
setupCommonArguments(baseArguments)
|
||||||
|
|
||||||
|
setupJvmSpecificArguments(baseArguments)
|
||||||
|
|
||||||
|
val jdkHomeFromConfigurations = scriptCompilationConfiguration.getNoDefault(ScriptCompilationConfiguration.jvm.jdkHome)
|
||||||
|
?: hostConfiguration[ScriptingHostConfiguration.jvm.jdkHome]
|
||||||
|
if (jdkHomeFromConfigurations != null) {
|
||||||
|
messageCollector.report(CompilerMessageSeverity.LOGGING, "Using JDK home directory $jdkHomeFromConfigurations")
|
||||||
|
put(JVMConfigurationKeys.JDK_HOME, jdkHomeFromConfigurations)
|
||||||
|
} else {
|
||||||
|
configureJdkHome(baseArguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
|
||||||
|
|
||||||
|
val isModularJava = isModularJava()
|
||||||
|
|
||||||
|
scriptCompilationConfiguration[ScriptCompilationConfiguration.dependencies]?.let { dependencies ->
|
||||||
|
addJvmClasspathRoots(
|
||||||
|
dependencies.flatMap {
|
||||||
|
(it as JvmDependency).classpath
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
configureExplicitContentRoots(baseArguments)
|
||||||
|
|
||||||
|
if (!baseArguments.noStdlib) {
|
||||||
|
addModularRootIfNotNull(isModularJava, "kotlin.stdlib", KotlinJars.stdlib)
|
||||||
|
addModularRootIfNotNull(isModularJava, "kotlin.script.runtime", KotlinJars.scriptRuntimeOrNull)
|
||||||
|
}
|
||||||
|
// see comments about logic in CompilerConfiguration.configureStandardLibs
|
||||||
|
if (!baseArguments.noReflect && !baseArguments.noStdlib) {
|
||||||
|
addModularRootIfNotNull(isModularJava, "kotlin.reflect", KotlinJars.reflectOrNull)
|
||||||
|
}
|
||||||
|
|
||||||
|
put(CommonConfigurationKeys.MODULE_NAME, baseArguments.moduleName ?: "kotlin-script")
|
||||||
|
|
||||||
|
configureAdvancedJvmOptions(baseArguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private fun SourceCode.scriptFileName(
|
||||||
|
mainScript: SourceCode,
|
||||||
|
scriptCompilationConfiguration: ScriptCompilationConfiguration
|
||||||
|
): String =
|
||||||
|
when {
|
||||||
|
name != null -> name!!
|
||||||
|
mainScript == this -> "script.${scriptCompilationConfiguration[ScriptCompilationConfiguration.fileExtension]}"
|
||||||
|
else -> throw Exception("Unexpected script without name: $this")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMainKtFile(
|
||||||
|
mainScript: SourceCode,
|
||||||
|
scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||||
|
environment: KotlinCoreEnvironment
|
||||||
|
): KtFile? {
|
||||||
|
val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl
|
||||||
|
val scriptText = getMergedScriptText(mainScript, scriptCompilationConfiguration)
|
||||||
|
val virtualFile = ScriptLightVirtualFile(
|
||||||
|
mainScript.scriptFileName(mainScript, scriptCompilationConfiguration),
|
||||||
|
(mainScript as? FileScriptSource)?.file?.path,
|
||||||
|
scriptText
|
||||||
|
)
|
||||||
|
return psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile?
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CompilerConfiguration.updateWithRefinedConfigurations(
|
||||||
|
initialScriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||||
|
refinedScriptCompilationConfigurations: HashMap<SourceCode, ScriptCompilationConfiguration>
|
||||||
|
) {
|
||||||
|
val updatedCompilerOptions = refinedScriptCompilationConfigurations.flatMap {
|
||||||
|
it.value[ScriptCompilationConfiguration.compilerOptions] ?: emptyList()
|
||||||
|
}
|
||||||
|
if (updatedCompilerOptions.isNotEmpty() &&
|
||||||
|
updatedCompilerOptions != initialScriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions]
|
||||||
|
) {
|
||||||
|
|
||||||
|
val updatedArguments = K2JVMCompilerArguments()
|
||||||
|
parseCommandLineArguments(updatedCompilerOptions, updatedArguments)
|
||||||
|
|
||||||
|
setupCommonArguments(updatedArguments)
|
||||||
|
|
||||||
|
setupJvmSpecificArguments(updatedArguments)
|
||||||
|
|
||||||
|
configureAdvancedJvmOptions(updatedArguments)
|
||||||
|
|
||||||
|
// TODO: report warnings for all ignored options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun analyze(sourceFiles: Collection<KtFile>, environment: KotlinCoreEnvironment): AnalysisResult {
|
||||||
|
val messageCollector = environment.configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY]!!
|
||||||
|
|
||||||
|
val analyzerWithCompilerReport = AnalyzerWithCompilerReport(messageCollector, environment.configuration.languageVersionSettings)
|
||||||
|
|
||||||
analyzerWithCompilerReport.analyzeAndReport(sourceFiles) {
|
analyzerWithCompilerReport.analyzeAndReport(sourceFiles) {
|
||||||
val project = environment.project
|
val project = environment.project
|
||||||
TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
|
TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
|
||||||
@@ -165,13 +297,14 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
|
|||||||
environment::createPackagePartProvider
|
environment::createPackagePartProvider
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val analysisResult = analyzerWithCompilerReport.analysisResult
|
return analyzerWithCompilerReport.analysisResult
|
||||||
|
}
|
||||||
if (!analysisResult.shouldGenerateCode) return failure("no code to generate")
|
|
||||||
if (analysisResult.isError() || messageCollector.hasErrors()) return failure()
|
|
||||||
|
|
||||||
|
private fun generate(
|
||||||
|
analysisResult: AnalysisResult, sourceFiles: List<KtFile>, kotlinCompilerConfiguration: CompilerConfiguration
|
||||||
|
): GenerationState {
|
||||||
val generationState = GenerationState.Builder(
|
val generationState = GenerationState.Builder(
|
||||||
psiFile.project,
|
sourceFiles.first().project,
|
||||||
ClassBuilderFactories.BINARIES,
|
ClassBuilderFactories.BINARIES,
|
||||||
analysisResult.moduleDescriptor,
|
analysisResult.moduleDescriptor,
|
||||||
analysisResult.bindingContext,
|
analysisResult.bindingContext,
|
||||||
@@ -180,7 +313,16 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
|
|||||||
).build()
|
).build()
|
||||||
|
|
||||||
KotlinCodegenFacade.compileCorrectFiles(generationState, CompilationErrorHandler.THROW_EXCEPTION)
|
KotlinCodegenFacade.compileCorrectFiles(generationState, CompilationErrorHandler.THROW_EXCEPTION)
|
||||||
|
return generationState
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeCompiledScript(
|
||||||
|
generationState: GenerationState,
|
||||||
|
script: SourceCode,
|
||||||
|
ktScript: KtScript,
|
||||||
|
sourceDependencies: List<ScriptsCompilationDependencies.SourceDependencies>,
|
||||||
|
getScriptConfiguration: (KtFile) -> ScriptCompilationConfiguration
|
||||||
|
): KJvmCompiledScript<Any> {
|
||||||
val scriptDependenciesStack = ArrayDeque<KtScript>()
|
val scriptDependenciesStack = ArrayDeque<KtScript>()
|
||||||
|
|
||||||
fun makeOtherScripts(script: KtScript): List<KJvmCompiledScript<*>> {
|
fun makeOtherScripts(script: KtScript): List<KJvmCompiledScript<*>> {
|
||||||
@@ -195,7 +337,10 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
|
|||||||
sourceDependencies.find { it.scriptFile == containingKtFile }?.sourceDependencies?.mapNotNull { sourceFile ->
|
sourceDependencies.find { it.scriptFile == containingKtFile }?.sourceDependencies?.mapNotNull { sourceFile ->
|
||||||
sourceFile.declarations.firstIsInstanceOrNull<KtScript>()?.let {
|
sourceFile.declarations.firstIsInstanceOrNull<KtScript>()?.let {
|
||||||
KJvmCompiledScript<Any>(
|
KJvmCompiledScript<Any>(
|
||||||
containingKtFile.virtualFile?.path, updatedConfiguration, it.fqName.asString(), makeOtherScripts(it)
|
containingKtFile.virtualFile?.path,
|
||||||
|
getScriptConfiguration(sourceFile),
|
||||||
|
it.fqName.asString(),
|
||||||
|
makeOtherScripts(it)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
@@ -204,17 +349,13 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
|
|||||||
return otherScripts
|
return otherScripts
|
||||||
}
|
}
|
||||||
|
|
||||||
val compiledScript = KJvmCompiledScript<Any>(
|
return KJvmCompiledScript(
|
||||||
script.locationId,
|
script.locationId,
|
||||||
updatedConfiguration,
|
getScriptConfiguration(ktScript.containingKtFile),
|
||||||
ktScript.fqName.asString(),
|
ktScript.fqName.asString(),
|
||||||
makeOtherScripts(ktScript),
|
makeOtherScripts(ktScript),
|
||||||
KJvmCompiledModule(generationState)
|
KJvmCompiledModule(generationState)
|
||||||
)
|
)
|
||||||
|
|
||||||
return ResultWithDiagnostics.Success(compiledScript, messageCollector.diagnostics)
|
|
||||||
} catch (ex: Throwable) {
|
|
||||||
return failure(ex.asDiagnostics(path = script.locationId))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,7 +401,8 @@ internal class ScriptDiagnosticsMessageCollector : MessageCollector {
|
|||||||
internal class BridgeScriptDefinition(
|
internal class BridgeScriptDefinition(
|
||||||
val scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
val scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||||
val hostConfiguration: ScriptingHostConfiguration,
|
val hostConfiguration: ScriptingHostConfiguration,
|
||||||
updateClasspath: (List<File>) -> Unit
|
updateConfiguration: (SourceCode, ScriptCompilationConfiguration) -> Unit,
|
||||||
|
getScriptSource: (ScriptContents) -> SourceCode?
|
||||||
) : KotlinScriptDefinition(Any::class) {
|
) : KotlinScriptDefinition(Any::class) {
|
||||||
|
|
||||||
val baseClass: KClass<*> by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
val baseClass: KClass<*> by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||||
@@ -308,7 +450,7 @@ internal class BridgeScriptDefinition(
|
|||||||
.orEmpty()
|
.orEmpty()
|
||||||
|
|
||||||
override val dependencyResolver: DependenciesResolver =
|
override val dependencyResolver: DependenciesResolver =
|
||||||
BridgeDependenciesResolver(scriptCompilationConfiguration, updateClasspath)
|
BridgeDependenciesResolver(scriptCompilationConfiguration, updateConfiguration, getScriptSource)
|
||||||
|
|
||||||
private val scriptingClassGetter by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
private val scriptingClassGetter by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||||
hostConfiguration[ScriptingHostConfiguration.getScriptingClass]
|
hostConfiguration[ScriptingHostConfiguration.getScriptingClass]
|
||||||
|
|||||||
+49
-7
@@ -33,7 +33,7 @@ class ScriptingHostTest : TestCase() {
|
|||||||
fun testSimpleUsage() {
|
fun testSimpleUsage() {
|
||||||
val greeting = "Hello from script!"
|
val greeting = "Hello from script!"
|
||||||
val output = captureOut {
|
val output = captureOut {
|
||||||
evalScript("println(\"$greeting\")")
|
evalScript("println(\"$greeting\")").throwOnFailure()
|
||||||
}
|
}
|
||||||
Assert.assertEquals(greeting, output)
|
Assert.assertEquals(greeting, output)
|
||||||
}
|
}
|
||||||
@@ -137,6 +137,41 @@ class ScriptingHostTest : TestCase() {
|
|||||||
assertEquals("/script.kts", report?.sourcePath)
|
assertEquals("/script.kts", report?.sourcePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCompileOptionsLanguageVersion() {
|
||||||
|
val script = "typealias MyInt = Int\nval x: MyInt = 3"
|
||||||
|
val compilationConfiguration1 = createJvmCompilationConfigurationFromTemplate<SimpleScriptTemplate> {
|
||||||
|
compilerOptions("-language-version", "1.0")
|
||||||
|
}
|
||||||
|
val res = BasicJvmScriptingHost().eval(script.toScriptSource(), compilationConfiguration1, null)
|
||||||
|
assertTrue(res is ResultWithDiagnostics.Failure)
|
||||||
|
res.reports.find { it.message.startsWith("The feature \"type aliases\" is only available since language version 1.1") }
|
||||||
|
?: fail("Error report about language version not found. Reported:\n ${res.reports.joinToString("\n ") { it.message }}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCompileOptionsNoStdlib() {
|
||||||
|
val script = "println(\"Hi\")"
|
||||||
|
|
||||||
|
val res1 = evalScriptWithConfiguration(script) {
|
||||||
|
compilerOptions("-no-stdlib")
|
||||||
|
}
|
||||||
|
assertTrue(res1 is ResultWithDiagnostics.Failure)
|
||||||
|
res1.reports.find { it.message.startsWith("Unresolved reference: println") }
|
||||||
|
?: fail("Expected unresolved reference report. Reported:\n ${res1.reports.joinToString("\n ") { it.message }}")
|
||||||
|
|
||||||
|
val res2 = evalScriptWithConfiguration(script) {
|
||||||
|
refineConfiguration {
|
||||||
|
beforeCompiling { ctx ->
|
||||||
|
ScriptCompilationConfiguration(ctx.compilationConfiguration) {
|
||||||
|
compilerOptions("-no-stdlib")
|
||||||
|
}.asSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// -no-stdlib in refined configuration has no effect
|
||||||
|
assertTrue(res2 is ResultWithDiagnostics.Success)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMemoryCache() {
|
fun testMemoryCache() {
|
||||||
@@ -147,7 +182,7 @@ class ScriptingHostTest : TestCase() {
|
|||||||
val host = BasicJvmScriptingHost(compiler = compiler, evaluator = evaluator)
|
val host = BasicJvmScriptingHost(compiler = compiler, evaluator = evaluator)
|
||||||
Assert.assertTrue(cache.data.isEmpty())
|
Assert.assertTrue(cache.data.isEmpty())
|
||||||
|
|
||||||
val output = captureOut { evalScript(script, host) }
|
val output = captureOut { evalScript(script, host).throwOnFailure() }
|
||||||
Assert.assertEquals("x = 1", output)
|
Assert.assertEquals("x = 1", output)
|
||||||
|
|
||||||
Assert.assertEquals(1, cache.data.size)
|
Assert.assertEquals(1, cache.data.size)
|
||||||
@@ -157,7 +192,7 @@ class ScriptingHostTest : TestCase() {
|
|||||||
Assert.assertEquals(output, output2)
|
Assert.assertEquals(output, output2)
|
||||||
|
|
||||||
// TODO: check if cached script is actually used
|
// TODO: check if cached script is actually used
|
||||||
val output3 = captureOut { evalScript(script, host) }.trim()
|
val output3 = captureOut { evalScript(script, host).throwOnFailure() }.trim()
|
||||||
Assert.assertEquals(output, output3)
|
Assert.assertEquals(output, output3)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +240,7 @@ class ScriptingHostTest : TestCase() {
|
|||||||
Assert.assertEquals(output, output2)
|
Assert.assertEquals(output, output2)
|
||||||
|
|
||||||
// TODO: check if cached script is actually used
|
// TODO: check if cached script is actually used
|
||||||
val output3 = captureOut { evalScript(script, host) }.trim()
|
val output3 = captureOut { evalScript(script, host).throwOnFailure() }.trim()
|
||||||
Assert.assertEquals(output, output3)
|
Assert.assertEquals(output, output3)
|
||||||
} finally {
|
} finally {
|
||||||
cacheDir.deleteRecursively()
|
cacheDir.deleteRecursively()
|
||||||
@@ -255,9 +290,16 @@ fun ResultWithDiagnostics<*>.throwOnFailure(): ResultWithDiagnostics<*> = apply
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun evalScript(script: String, host: BasicScriptingHost = BasicJvmScriptingHost()) {
|
private fun evalScript(script: String, host: BasicScriptingHost = BasicJvmScriptingHost()): ResultWithDiagnostics<*> =
|
||||||
val compilationConfiguration = createJvmCompilationConfigurationFromTemplate<SimpleScriptTemplate>()
|
evalScriptWithConfiguration(script, host)
|
||||||
host.eval(script.toScriptSource(), compilationConfiguration, null).throwOnFailure()
|
|
||||||
|
private fun evalScriptWithConfiguration(
|
||||||
|
script: String,
|
||||||
|
host: BasicScriptingHost = BasicJvmScriptingHost(),
|
||||||
|
body: ScriptCompilationConfiguration.Builder.() -> Unit = {}
|
||||||
|
): ResultWithDiagnostics<EvaluationResult> {
|
||||||
|
val compilationConfiguration = createJvmCompilationConfigurationFromTemplate<SimpleScriptTemplate>(body = body)
|
||||||
|
return host.eval(script.toScriptSource(), compilationConfiguration, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+16
-18
@@ -22,7 +22,8 @@ import kotlin.script.experimental.jvm.compat.mapToLegacyScriptReportSeverity
|
|||||||
|
|
||||||
class BridgeDependenciesResolver(
|
class BridgeDependenciesResolver(
|
||||||
val scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
val scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||||
val onClasspathUpdated: (List<File>) -> Unit = {}
|
val onConfigurationUpdated: (SourceCode, ScriptCompilationConfiguration) -> Unit = { _, _ -> },
|
||||||
|
val getScriptSource: (ScriptContents) -> SourceCode? = { null }
|
||||||
) : AsyncDependenciesResolver {
|
) : AsyncDependenciesResolver {
|
||||||
|
|
||||||
override fun resolve(scriptContents: ScriptContents, environment: Environment): DependenciesResolver.ResolveResult =
|
override fun resolve(scriptContents: ScriptContents, environment: Environment): DependenciesResolver.ResolveResult =
|
||||||
@@ -40,9 +41,6 @@ class BridgeDependenciesResolver(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val oldClasspath =
|
|
||||||
scriptCompilationConfiguration[ScriptCompilationConfiguration.dependencies].toClassPathOrEmpty()
|
|
||||||
|
|
||||||
val defaultImports = scriptCompilationConfiguration[ScriptCompilationConfiguration.defaultImports]?.toList() ?: emptyList()
|
val defaultImports = scriptCompilationConfiguration[ScriptCompilationConfiguration.defaultImports]?.toList() ?: emptyList()
|
||||||
|
|
||||||
fun ScriptCompilationConfiguration.toDependencies(classpath: List<File>): ScriptDependencies =
|
fun ScriptCompilationConfiguration.toDependencies(classpath: List<File>): ScriptDependencies =
|
||||||
@@ -53,13 +51,15 @@ class BridgeDependenciesResolver(
|
|||||||
scripts = this[ScriptCompilationConfiguration.importScripts].toFilesOrEmpty()
|
scripts = this[ScriptCompilationConfiguration.importScripts].toFilesOrEmpty()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val script = getScriptSource(scriptContents) ?: scriptContents.toScriptSource()
|
||||||
|
|
||||||
val refineResults = scriptCompilationConfiguration.refineWith(
|
val refineResults = scriptCompilationConfiguration.refineWith(
|
||||||
scriptCompilationConfiguration[ScriptCompilationConfiguration.refineConfigurationOnAnnotations]?.handler,
|
scriptCompilationConfiguration[ScriptCompilationConfiguration.refineConfigurationOnAnnotations]?.handler,
|
||||||
scriptContents, processedScriptData
|
processedScriptData, script
|
||||||
).onSuccess {
|
).onSuccess {
|
||||||
it.refineWith(
|
it.refineWith(
|
||||||
scriptCompilationConfiguration[ScriptCompilationConfiguration.refineConfigurationBeforeCompiling]?.handler,
|
scriptCompilationConfiguration[ScriptCompilationConfiguration.refineConfigurationBeforeCompiling]?.handler,
|
||||||
scriptContents, processedScriptData
|
processedScriptData, script
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,11 +72,12 @@ class BridgeDependenciesResolver(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (refinedConfiguration != scriptCompilationConfiguration) {
|
||||||
|
onConfigurationUpdated(script, refinedConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
val newClasspath = refinedConfiguration[ScriptCompilationConfiguration.dependencies]
|
val newClasspath = refinedConfiguration[ScriptCompilationConfiguration.dependencies]
|
||||||
?.flatMap { (it as JvmDependency).classpath } ?: emptyList()
|
?.flatMap { (it as JvmDependency).classpath } ?: emptyList()
|
||||||
if (newClasspath != oldClasspath) {
|
|
||||||
onClasspathUpdated(newClasspath)
|
|
||||||
}
|
|
||||||
|
|
||||||
return DependenciesResolver.ResolveResult.Success(
|
return DependenciesResolver.ResolveResult.Success(
|
||||||
// TODO: consider returning only increment from the initial config
|
// TODO: consider returning only increment from the initial config
|
||||||
@@ -109,12 +110,9 @@ internal fun List<SourceCode>?.toFilesOrEmpty() = this?.map {
|
|||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
|
|
||||||
fun ScriptCompilationConfiguration.refineWith(
|
fun ScriptCompilationConfiguration.refineWith(
|
||||||
handler: RefineScriptCompilationConfigurationHandler?, scriptContents: ScriptContents, processedScriptData: ScriptCollectedData
|
handler: RefineScriptCompilationConfigurationHandler?,
|
||||||
): ResultWithDiagnostics<ScriptCompilationConfiguration> {
|
processedScriptData: ScriptCollectedData,
|
||||||
|
script: SourceCode
|
||||||
if (handler == null) return this.asSuccess()
|
): ResultWithDiagnostics<ScriptCompilationConfiguration> =
|
||||||
|
if (handler == null) this.asSuccess()
|
||||||
return handler(
|
else handler(ScriptConfigurationRefinementContext(script, this, processedScriptData))
|
||||||
ScriptConfigurationRefinementContext(scriptContents.toScriptSource(), this, processedScriptData)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -77,6 +77,19 @@ fun getResourcePathForClass(aClass: Class<*>): File {
|
|||||||
return File(resourceRoot).absoluteFile
|
return File(resourceRoot).absoluteFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun tryGetResourcePathForClassByName(name: String, classLoader: ClassLoader): File? = try {
|
||||||
|
classLoader.loadClass(name)?.let {
|
||||||
|
val path = "/" + name.replace('.', '/') + ".class"
|
||||||
|
getResourceRoot(it, path)
|
||||||
|
}?.let {
|
||||||
|
File(it).absoluteFile
|
||||||
|
}
|
||||||
|
} catch (_: ClassNotFoundException) {
|
||||||
|
null
|
||||||
|
} catch (_: NoClassDefFoundError) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
internal fun URL.toFile() =
|
internal fun URL.toFile() =
|
||||||
try {
|
try {
|
||||||
File(toURI().schemeSpecificPart)
|
File(toURI().schemeSpecificPart)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import java.net.URLClassLoader
|
|||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.script.experimental.jvm.impl.getResourcePathForClass
|
import kotlin.script.experimental.jvm.impl.getResourcePathForClass
|
||||||
import kotlin.script.experimental.jvm.impl.toFile
|
import kotlin.script.experimental.jvm.impl.toFile
|
||||||
|
import kotlin.script.experimental.jvm.impl.tryGetResourcePathForClassByName
|
||||||
import kotlin.script.templates.standard.ScriptTemplateWithArgs
|
import kotlin.script.templates.standard.ScriptTemplateWithArgs
|
||||||
|
|
||||||
// TODO: consider moving all these utilites to the build-common or some other shared compiler API module
|
// TODO: consider moving all these utilites to the build-common or some other shared compiler API module
|
||||||
@@ -29,6 +30,7 @@ internal const val KOTLIN_SCRIPT_CLASSPATH_PROPERTY = "kotlin.script.classpath"
|
|||||||
internal const val KOTLIN_COMPILER_CLASSPATH_PROPERTY = "kotlin.compiler.classpath"
|
internal const val KOTLIN_COMPILER_CLASSPATH_PROPERTY = "kotlin.compiler.classpath"
|
||||||
internal const val KOTLIN_COMPILER_JAR_PROPERTY = "kotlin.compiler.jar"
|
internal const val KOTLIN_COMPILER_JAR_PROPERTY = "kotlin.compiler.jar"
|
||||||
internal const val KOTLIN_STDLIB_JAR_PROPERTY = "kotlin.java.stdlib.jar"
|
internal const val KOTLIN_STDLIB_JAR_PROPERTY = "kotlin.java.stdlib.jar"
|
||||||
|
internal const val KOTLIN_REFLECT_JAR_PROPERTY = "kotlin.java.reflect.jar"
|
||||||
// obsolete name, but maybe still used in the wild
|
// obsolete name, but maybe still used in the wild
|
||||||
// TODO: consider removing
|
// TODO: consider removing
|
||||||
internal const val KOTLIN_RUNTIME_JAR_PROPERTY = "kotlin.java.runtime.jar"
|
internal const val KOTLIN_RUNTIME_JAR_PROPERTY = "kotlin.java.runtime.jar"
|
||||||
@@ -189,6 +191,11 @@ object KotlinJars {
|
|||||||
?: explicitCompilerClasspath?.firstOrNull { it.matchMaybeVersionedFile(jarName) }?.takeIf(File::exists)
|
?: explicitCompilerClasspath?.firstOrNull { it.matchMaybeVersionedFile(jarName) }?.takeIf(File::exists)
|
||||||
?: getResourcePathForClass(markerClass.java).takeIf(File::exists)
|
?: getResourcePathForClass(markerClass.java).takeIf(File::exists)
|
||||||
|
|
||||||
|
fun getLib(propertyName: String, jarName: String, markerClassName: String): File? =
|
||||||
|
System.getProperty(propertyName)?.let(::File)?.takeIf(File::exists)
|
||||||
|
?: explicitCompilerClasspath?.firstOrNull { it.matchMaybeVersionedFile(jarName) }?.takeIf(File::exists)
|
||||||
|
?: tryGetResourcePathForClassByName(markerClassName, Thread.currentThread().contextClassLoader)?.takeIf(File::exists)
|
||||||
|
|
||||||
val stdlibOrNull: File? by lazy {
|
val stdlibOrNull: File? by lazy {
|
||||||
System.getProperty(KOTLIN_STDLIB_JAR_PROPERTY)?.let(::File)?.takeIf(File::exists)
|
System.getProperty(KOTLIN_STDLIB_JAR_PROPERTY)?.let(::File)?.takeIf(File::exists)
|
||||||
?: getLib(
|
?: getLib(
|
||||||
@@ -203,6 +210,14 @@ object KotlinJars {
|
|||||||
?: throw Exception("Unable to find kotlin stdlib, please specify it explicitly via \"$KOTLIN_STDLIB_JAR_PROPERTY\" property")
|
?: throw Exception("Unable to find kotlin stdlib, please specify it explicitly via \"$KOTLIN_STDLIB_JAR_PROPERTY\" property")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val reflectOrNull: File? by lazy {
|
||||||
|
getLib(
|
||||||
|
KOTLIN_REFLECT_JAR_PROPERTY,
|
||||||
|
KOTLIN_JAVA_REFLECT_JAR,
|
||||||
|
"kotlin.reflect.full.IllegalCallableAccessException"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val scriptRuntimeOrNull: File? by lazy {
|
val scriptRuntimeOrNull: File? by lazy {
|
||||||
getLib(
|
getLib(
|
||||||
KOTLIN_SCRIPT_RUNTIME_JAR_PROPERTY,
|
KOTLIN_SCRIPT_RUNTIME_JAR_PROPERTY,
|
||||||
|
|||||||
Reference in New Issue
Block a user