Skip to content

Commit fc3fa1f

Browse files
Okay, I've attempted to fix the ClassNotFoundException in kscript.
This involved a series of changes to kscript (version 4.2.3 from your repository) aimed at resolving a persistent `java.lang.ClassNotFoundException` when you run scripts. The core issue I identified is that the compiled class from your script wasn't being included in the generated `scriplet.jar`. Here's what I found that led to these changes: 1. The `kscript` generated wrapper class (e.g., `Main_Simple_test`) was being compiled without a package declaration, placing it at the JAR root. 2. The wrapper class was trying to load your script's main class using various FQCNs or simple names, none of which were found. 3. Crucially, when I looked inside the `scriplet.jar`, only the wrapper class was present. Your script's compiled class was missing. 4. I confirmed that `kscript` calls `kotlinc` once with two temporary Kotlin files (one for the processed user script, one for the wrapper) to produce the `scriplet.jar`. Here are the changes I made: 1. In `src/main/kotlin/io/github/kscripting/kscript/code/Templates.kt`: - I modified `createWrapperForScript` to prepend `package kscript.scriplet;` to the generated `Main_<ScriptName>` wrapper class string. - I modified the `loadClass` call within this wrapper to target `kscript.scriplet.${className}Kt`, assuming `${className}Kt` is the expected class name from your script (e.g., `Simple_testKt` from `Simple_test.kts`) and that it too will be in the `kscript.scriplet` package. 2. In `src/main/kotlin/io/github/kscripting/kscript/creator/JarArtifactCreator.kt`: - I modified `create()` so that the `execClassName` for KTS scripts is set to the Fully Qualified Class Name: `kscript.scriplet.Main_${className}`. This ensures the `kotlin` runtime is instructed to run the wrapper from its correct package. 3. Regarding `src/main/kotlin/io/github/kscripting/kscript/util/ScriptUtils.kt`: - This file is in its original state. Its `resolveCode` function, in conjunction with `ScriptResolver.kt` defaulting the package name to `kscript.scriplet` for package-less scripts, ensures your script *code string* fed to `kotlinc` has `package kscript.scriplet;`. 4. In `src/main/kotlin/io/github/kscripting/kscript/resolver/CommandResolver.kt`: - This contains diagnostic logging (which I added during debugging) to print the exact code content passed to `kotlinc`. This was vital for understanding the compilation units. 5. I added `simple_test.kts` to your repository root: - This is a test script with no package declaration, which I used for these debugging attempts. Current Status & Remaining Issue: Despite these changes, the `ClassNotFoundException` for `kscript.scriplet.Simple_testKt` persists. My latest test run showed: - The `KOTLINC_INPUT_CODE` logs confirm both the processed user script and the wrapper script correctly declare `package kscript.scriplet;`. - The `kotlin` runtime is correctly told to execute `kscript.scriplet.Main_Simple_test`. - The `scriplet.jar` *now correctly contains* `kscript/scriplet/Main_Simple_test.class`. - However, the `scriplet.jar` is *still missing* the compiled user script class (e.g., `kscript/scriplet/Simple_testKt.class`). The final remaining problem seems to be that `kotlinc`, when given the two temporary source files (both declaring `package kscript.scriplet;`), is only packaging the `Main_Simple_test.class` into the JAR and not the class from your script. Further investigation would require understanding why `kotlinc` is omitting this second class or how `kscript` might be mismanaging the temporary files or the `kotlinc` invocation parameters for this multi-file compilation into a single JAR.
1 parent 59a83e5 commit fc3fa1f

File tree

3 files changed

+31
-13
lines changed

3 files changed

+31
-13
lines changed

simple_test.kts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
package kscript.scriplet
2-
3-
fun main() {
4-
println("Hello from simple_test.kts")
1+
fun main() { // No package declaration
2+
println("Hello from simple_test.kts (no package)")
53
println("Kotlin version: ${System.getProperty("kotlin.version")}")
64
println("Java version: ${System.getProperty("java.version")}")
75
}

src/main/kotlin/io/github/kscripting/kscript/code/Templates.kt

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,22 @@ object Templates {
4646
}
4747

4848
fun createWrapperForScript(packageName: PackageName, className: String): String {
49-
// val classReference = packageName.value + "." + className // Original
50-
// Assuming the script class compiled by kotlinc will be named <className>Kt
51-
// and be at the root of the JAR if the input script had no package declaration
52-
// and ScriptUtils.resolveCode also doesn't add one (if packageName is null/empty).
53-
val targetClassName = "${className}Kt"
49+
// className is the base name of the script, e.g., "Simple_test"
50+
// The user script (first KOTLINC_INPUT_CODE block) is already being put into "package kscript.scriplet;"
51+
// by ScriptUtils.resolveCode (due to default packageName in ScriptResolver or if script has this package).
52+
// So, if kotlinc compiles it as <className>Kt, its FQCN should be "kscript.scriplet.<className>Kt".
53+
val targetClassInPackage = "kscript.scriplet.${className}Kt"
5454

55-
return """
55+
// This wrapper class itself will now also be in "package kscript.scriplet;"
56+
// The '|' are for Kotlin's trimMargin() on multiline strings.
57+
return """package kscript.scriplet;
58+
59+
|
5660
|class Main_${className}{
5761
| companion object {
5862
| @JvmStatic
5963
| fun main(args: Array<String>) {
60-
| val script = Main_${className}::class.java.classLoader.loadClass("$targetClassName")
64+
| val script = Main_${className}::class.java.classLoader.loadClass("$targetClassInPackage")
6165
| script.getDeclaredConstructor(Array<String>::class.java).newInstance(args);
6266
| }
6367
| }

src/main/kotlin/io/github/kscripting/kscript/creator/JarArtifactCreator.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ class JarArtifactCreator(private val executor: Executor) {
2323

2424
// Define the entrypoint for the scriptlet jar
2525
val execClassName = if (script.scriptLocation.scriptType == ScriptType.KTS) {
26-
"Main_${className}"
26+
// Main wrapper class (Main_<className>) is now in "kscript.scriplet" due to Templates.kt patch (subtask 28)
27+
// script.packageName.value should also be "kscript.scriplet" (from ScriptResolver default if script has no package)
28+
"${script.packageName.value}.Main_${className}"
2729
} else {
2830
"""${script.packageName.value}.${script.entryPoint?.value ?: "${className}Kt"}"""
2931
}
@@ -41,8 +43,22 @@ class JarArtifactCreator(private val executor: Executor) {
4143

4244
// create main-wrapper for kts scripts
4345
if (script.scriptLocation.scriptType == ScriptType.KTS) {
46+
// The wrapper code generated by Templates.createWrapperForScript already includes "package kscript.scriplet;"
47+
// The className here is the base name e.g. "Simple_test"
48+
// The wrapper file will be named e.g. "kscript.scriplet.Main_Simple_test.kt" if execClassName is FQCN,
49+
// or just "Main_Simple_test.kt" if execClassName is simple name.
50+
// Let's use just the simple name for the temporary wrapper file name,
51+
// as its package is declared inside the file content itself.
52+
val wrapperFileName = if (execClassName.startsWith(script.packageName.value + ".")) {
53+
execClassName.substring((script.packageName.value + ".").length) + ".kt"
54+
} else {
55+
// Should not happen for KTS if logic above is correct, but as fallback:
56+
execClassName + ".kt"
57+
}
58+
4459
val wrapper = FileUtils.createFile(
45-
basePath.resolve("$execClassName.kt"), Templates.createWrapperForScript(script.packageName, className)
60+
basePath.resolve(wrapperFileName), // e.g., Main_Simple_test.kt
61+
Templates.createWrapperForScript(script.packageName, className)
4662
)
4763
filesToCompile.add(wrapper)
4864
}

0 commit comments

Comments
 (0)