fix: Resolve ClassNotFoundException for .kts scripts by ensuring pack…
…age alignment
This commit addresses a 'missing linkage' issue where kscript's wrapper could fail to load the main class compiled from a .kts script, resulting in a ClassNotFoundException.
Problem Analysis:
- For
.ktsscripts without an explicitpackagedeclaration, kscript internally assigns a default package (e.g.,kscript.scriplet). - A wrapper class (e.g.,
Main_ScriptName.kt) is generated to provide a standardmainmethod entry point. This wrapper attempts to load the compiled.ktsscript's class using reflection, qualified with the assigned package name (e.g.,kscript.scriplet.ScriptName). - However, the original
.ktsfile content (without an explicit package statement) was written to a temporary file and compiled bykotlinc.kotlincwould place such a class in the default (unnamed) package. - This mismatch (wrapper expecting
kscript.scriplet.ScriptName, but class actually beingScriptNamein the default package) caused theClassNotFoundException.
Solution Implemented:
The JarArtifactCreator.create() method has been modified. Before a .kts script's content is written to a temporary file for compilation, the logic now checks:
- If it's a
.ktsfile. - If kscript has determined a package name for it (either parsed or defaulted).
- If the script content itself does not already start with a
packagedeclaration.
If these conditions are met, the determined package declaration (e.g., package kscript.scriplet;) is prepended to the script content. This ensures that kotlinc compiles the .kts script's class into the same package that the wrapper expects, resolving the ClassNotFoundException.
Further Considerations for Full Robustness (Future Work):
While this commit fixes a critical classloading issue for .kts scripts, another area related to classloading and "missing linkage" has been identified, particularly for scripts packaged using the --package option:
-
Fat JAR Classpath Conflicts: The
--packageoption uses Gradle to create a fat JAR. The current Gradle template usesDuplicatesStrategy.INCLUDE. This can lead to runtime issues (e.g.,NoSuchMethodError, services not loading) if dependencies have conflicting class versions orMETA-INF/servicesfiles, as only one version of a conflicting file will be included, potentially the wrong one. -
Recommendation: For more robust packaged scripts, the Gradle
template should be updated to use a dedicated fat JAR plugin like
com.github.johnrengelman.shadow, which offers better strategies for dependency conflict resolution and resource merging.
This fix provides a significant improvement in the reliable execution of .kts files. Further work on the packaging mechanism can enhance robustness for distributed scripts.