Hi all! I am trying to evaluate a script from a KS...
# scripting
d
Hi all! I am trying to evaluate a script from a KSP processor. However, it seems the compilation configuration I use (
dependenciesFromCurrentContext
) fails to load my base script class (which is in the dependencies of my KSP processor). Should I do something particular to have all the processor's dependencies in the compilation classpath?
Copy code
jvm {
        dependenciesFromCurrentContext(wholeClasspath = true, unpackJarCollections = true)
    }
The script base class:
Copy code
package com.github.ptitjes.kmf.script

import com.google.devtools.ksp.processing.*
import com.google.devtools.ksp.symbol.*
import kotlinx.coroutines.*
import kotlin.script.experimental.annotations.*
import kotlin.script.experimental.api.*
import kotlin.script.experimental.dependencies.*
import kotlin.script.experimental.dependencies.maven.*
import kotlin.script.experimental.jvm.*

@KotlinScript(
    displayName = "Kmf script",
    fileExtension = "kmf.kts",
    compilationConfiguration = KmfScriptConfiguration::class,
    evaluationConfiguration = KmfScriptEvaluation::class,
)
open class KmfScript(
    val codeGenerator: CodeGenerator,
    val logger: KSPLogger,
    val resolver: Resolver,
    val deferredSymbols: (symbols: List<KSAnnotated>) -> Unit,
)

object KmfScriptConfiguration : ScriptCompilationConfiguration({
    defaultImports(
        "com.google.devtools.ksp.*",
        "com.google.devtools.ksp.processing.*",
        "com.google.devtools.ksp.symbol.*",
        "com.github.ptitjes.kmf.*",
    )
    defaultImports(DependsOn::class, Repository::class)

    jvm {
        dependenciesFromCurrentContext(wholeClasspath = true, unpackJarCollections = true)
    }

    // Callbacks
    refineConfiguration {
        // Process specified annotations with the provided handler
        onAnnotations(
            DependsOn::class, Repository::class, handler = ::configureMavenDepsOnAnnotations
        )
    }

    ide {
        acceptedLocations(ScriptAcceptedLocation.Everywhere)
    }
})

object KmfScriptEvaluation : ScriptEvaluationConfiguration({
    scriptsInstancesSharing(true)
})

// Handler that reconfigures the compilation on the fly
fun configureMavenDepsOnAnnotations(
    context: ScriptConfigurationRefinementContext
): ResultWithDiagnostics<ScriptCompilationConfiguration> {
    val annotations = context.collectedData?.get(ScriptCollectedData.collectedAnnotations)?.takeIf { it.isNotEmpty() }
        ?: return context.compilationConfiguration.asSuccess()

    return runBlocking {
        resolver.resolveFromScriptSourceAnnotations(annotations)
    }.onSuccess {
        context.compilationConfiguration.with {
            dependencies.append(JvmDependency(it))
        }.asSuccess()
    }
}

private val resolver = CompoundDependenciesResolver(FileSystemDependenciesResolver(), MavenDependenciesResolver())
The processor class:
Copy code
package com.github.ptitjes.kmf.processing

import com.github.ptitjes.kmf.script.*
import com.google.devtools.ksp.processing.*
import com.google.devtools.ksp.symbol.*
import java.io.*
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.*
import kotlin.script.experimental.jvmhost.*

class KmfSymbolProcessor(
    private val scripFile: File,
    private val codeGenerator: CodeGenerator,
    private val logger: KSPLogger,
) : SymbolProcessor {
    override fun process(resolver: Resolver): List<KSAnnotated> {
        var deferredSymbols: List<KSAnnotated>? = null
        val diagnostics = evalFile(scripFile, codeGenerator, logger, resolver) { deferredSymbols = it }

        diagnostics.reports.forEach { diagnostic ->
            if (diagnostic.isError()) logger.error(diagnostic.render())
            else if (diagnostic.severity > ScriptDiagnostic.Severity.WARNING) logger.warn(diagnostic.render())
            else <http://logger.info|logger.info>(diagnostic.render())
        }

        diagnostics.onFailure {
            error("Kmf processor failed due to script issues")
        }

        return deferredSymbols ?: listOf()
    }
}

fun evalFile(
    scriptFile: File,
    codeGenerator: CodeGenerator,
    logger: KSPLogger,
    resolver: Resolver,
    deferredSymbols: (symbols: List<KSAnnotated>) -> Unit,
): ResultWithDiagnostics<EvaluationResult> {
    val compilationConfiguration = createJvmCompilationConfigurationFromTemplate<KmfScript>()
    val evaluationConfiguration = createJvmEvaluationConfigurationFromTemplate<KmfScript> {
        constructorArgs(codeGenerator, logger, resolver, deferredSymbols)
    }

    return BasicJvmScriptingHost().eval(
        scriptFile.toScriptSource(),
        compilationConfiguration,
        evaluationConfiguration
    )
}
i
dependenciesFromCurrentContext
is rather hacky helper, that tries to extract the classpath from the current thread classloader. There is no guaranteed way to do it, so I can imagine many situations, where it may fail to extract some parts of the classpath. So, if it doesn't work for you, you need to pass missing parts of the cp using regular
dependencies
key manually.