or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-scripting-host.mdindex.mdjsr223-integration.mdrepl-support.mdscript-caching.mdscript-compilation.mdscript-persistence.md

script-persistence.mddocs/

0

# Script Persistence

1

2

The script persistence system provides utilities for saving compiled scripts as executable JAR files or individual class files for deployment, distribution, and standalone execution.

3

4

## Capabilities

5

6

### BasicJvmScriptJarGenerator

7

8

Generates executable JAR files from compiled scripts with embedded dependencies and proper manifest configuration.

9

10

```kotlin { .api }

11

/**

12

* Generates executable JAR files from compiled scripts

13

* @param outputJar Target JAR file location

14

*/

15

open class BasicJvmScriptJarGenerator(val outputJar: File) : ScriptEvaluator {

16

17

/**

18

* Generates JAR file from compiled script

19

* @param compiledScript Compiled script to package

20

* @param scriptEvaluationConfiguration Evaluation configuration (not used for generation)

21

* @returns Success with NotEvaluated result or failure with diagnostics

22

*/

23

override suspend operator fun invoke(

24

compiledScript: CompiledScript,

25

scriptEvaluationConfiguration: ScriptEvaluationConfiguration

26

): ResultWithDiagnostics<EvaluationResult>

27

}

28

```

29

30

### BasicJvmScriptClassFilesGenerator

31

32

Generates individual class files from compiled scripts for integration into existing projects or custom deployment scenarios.

33

34

```kotlin { .api }

35

/**

36

* Generates individual class files from compiled scripts

37

* @param outputDir Target directory for class files

38

*/

39

open class BasicJvmScriptClassFilesGenerator(val outputDir: File) : ScriptEvaluator {

40

41

/**

42

* Generates class files from compiled script

43

* @param compiledScript Compiled script to extract classes from

44

* @param scriptEvaluationConfiguration Evaluation configuration (not used for generation)

45

* @returns Success with NotEvaluated result or failure with diagnostics

46

*/

47

override suspend operator fun invoke(

48

compiledScript: CompiledScript,

49

scriptEvaluationConfiguration: ScriptEvaluationConfiguration

50

): ResultWithDiagnostics<EvaluationResult>

51

}

52

```

53

54

**Usage Examples:**

55

56

```kotlin

57

import kotlin.script.experimental.jvmhost.*

58

import kotlin.script.experimental.api.*

59

import java.io.File

60

61

// Compile a script first

62

val compiler = JvmScriptCompiler()

63

val script = """

64

data class Person(val name: String, val age: Int)

65

66

val people = listOf(

67

Person("Alice", 30),

68

Person("Bob", 25)

69

)

70

71

println("People: \$people")

72

people.filter { it.age > 25 }.map { it.name }

73

""".trimIndent()

74

75

val compilationResult = compiler(

76

script.toScriptSource("people-script.kts"),

77

ScriptCompilationConfiguration {

78

dependencies(JvmDependency(kotlinStdlib))

79

}

80

)

81

82

when (compilationResult) {

83

is ResultWithDiagnostics.Success -> {

84

val compiledScript = compilationResult.value

85

86

// Generate executable JAR

87

val jarGenerator = BasicJvmScriptJarGenerator(File("output/people-script.jar"))

88

val jarResult = jarGenerator(compiledScript, ScriptEvaluationConfiguration.Default)

89

90

when (jarResult) {

91

is ResultWithDiagnostics.Success -> {

92

println("JAR generated successfully: output/people-script.jar")

93

94

// The JAR can now be executed with: java -jar people-script.jar

95

}

96

is ResultWithDiagnostics.Failure -> {

97

jarResult.reports.forEach { println("JAR generation error: ${it.message}") }

98

}

99

}

100

101

// Generate individual class files

102

val classGenerator = BasicJvmScriptClassFilesGenerator(File("output/classes"))

103

val classResult = classGenerator(compiledScript, ScriptEvaluationConfiguration.Default)

104

105

when (classResult) {

106

is ResultWithDiagnostics.Success -> {

107

println("Class files generated in: output/classes/")

108

109

// List generated class files

110

File("output/classes").walkTopDown()

111

.filter { it.isFile && it.extension == "class" }

112

.forEach { println("Generated: ${it.relativeTo(File("output/classes"))}") }

113

}

114

is ResultWithDiagnostics.Failure -> {

115

classResult.reports.forEach { println("Class generation error: ${it.message}") }

116

}

117

}

118

}

119

is ResultWithDiagnostics.Failure -> {

120

println("Compilation failed")

121

}

122

}

123

```

124

125

### Script JAR Extension Functions

126

127

Extension functions for working with compiled scripts and JAR files directly.

128

129

#### KJvmCompiledScript.saveToJar

130

131

Saves a compiled script directly to a JAR file with proper manifest and dependency information.

132

133

```kotlin { .api }

134

/**

135

* Saves compiled script to JAR file with manifest and dependencies

136

* @param outputJar Target JAR file

137

*/

138

fun KJvmCompiledScript.saveToJar(outputJar: File)

139

```

140

141

#### File.loadScriptFromJar

142

143

Loads a previously saved script from a JAR file for execution or inspection.

144

145

```kotlin { .api }

146

/**

147

* Loads compiled script from JAR file

148

* @param checkMissingDependencies Whether to validate all dependencies exist

149

* @returns Loaded compiled script or null if invalid/missing dependencies

150

*/

151

fun File.loadScriptFromJar(checkMissingDependencies: Boolean = true): CompiledScript?

152

```

153

154

**Direct JAR Operations Example:**

155

156

```kotlin

157

import kotlin.script.experimental.jvmhost.saveToJar

158

import kotlin.script.experimental.jvmhost.loadScriptFromJar

159

import kotlin.script.experimental.jvm.impl.KJvmCompiledScript

160

161

// Save compiled script directly to JAR

162

val compiledScript = compilationResult.value as KJvmCompiledScript

163

val jarFile = File("direct-save.jar")

164

compiledScript.saveToJar(jarFile)

165

166

println("Script saved to: ${jarFile.absolutePath}")

167

168

// Load script back from JAR

169

val loadedScript = jarFile.loadScriptFromJar()

170

when (loadedScript) {

171

null -> println("Failed to load script from JAR")

172

else -> {

173

println("Successfully loaded script from JAR")

174

175

// Execute loaded script

176

val evaluator = BasicJvmScriptEvaluator()

177

val evalResult = evaluator(loadedScript, ScriptEvaluationConfiguration.Default)

178

// Handle evaluation...

179

}

180

}

181

182

// Load without dependency checking for faster loading

183

val quickLoadedScript = jarFile.loadScriptFromJar(checkMissingDependencies = false)

184

```

185

186

## JAR File Structure

187

188

Generated JAR files follow a specific structure to ensure proper execution and dependency management.

189

190

### Generated JAR Layout

191

192

```text

193

script.jar

194

├── META-INF/

195

│ └── MANIFEST.MF # Executable manifest with Main-Class and Class-Path

196

├── Script_12345.class # Main script class (name includes hash)

197

├── Script_12345$InnerClass.class# Inner classes if any

198

├── Script_12345$Data.class # Data classes defined in script

199

└── script_metadata_Script_12345 # Serialized script metadata

200

```

201

202

### Manifest Configuration

203

204

The generated manifest includes:

205

206

- **Main-Class**: Points to the compiled script class

207

- **Class-Path**: Lists all dependencies with their locations

208

- **Manifest-Version**: Standard JAR manifest version

209

- **Created-By**: Identifies Kotlin as the creator

210

211

**Example Manifest:**

212

213

```text

214

Manifest-Version: 1.0

215

Created-By: JetBrains Kotlin

216

Main-Class: Script_a1b2c3d4

217

Class-Path: /path/to/kotlin-stdlib-2.2.0.jar /path/to/kotlin-reflect-2.2.0.jar

218

```

219

220

### Dependency Management

221

222

Dependencies are handled in several ways:

223

224

1. **External References**: Dependencies remain as external JAR files listed in Class-Path

225

2. **Relative Paths**: Paths are converted to URIs for proper resolution

226

3. **Missing Dependencies**: Optional validation ensures all dependencies are accessible

227

228

**Advanced JAR Generation with Custom Dependencies:**

229

230

```kotlin

231

// Create script with custom dependencies

232

val scriptWithDeps = """

233

@file:DependsOn("org.apache.commons:commons-lang3:3.12.0")

234

235

import org.apache.commons.lang3.StringUtils

236

237

val text = " hello world "

238

val result = StringUtils.capitalize(StringUtils.trim(text))

239

println("Result: \$result")

240

result

241

""".trimIndent()

242

243

val configWithMavenDeps = ScriptCompilationConfiguration {

244

dependencies(

245

JvmDependency("org.apache.commons:commons-lang3:3.12.0"),

246

JvmDependency(kotlinStdlib)

247

)

248

249

// Custom dependency resolver

250

refineConfiguration {

251

onAnnotations(DependsOn::class) { context ->

252

val dependsOnList = context.collectedData?.get(DependsOn::class)

253

if (dependsOnList != null) {

254

ScriptCompilationConfiguration(context.compilationConfiguration) {

255

dependencies.append(JvmDependency(dependsOnList.flatMap { it.artifacts }))

256

}.asSuccess()

257

} else {

258

context.compilationConfiguration.asSuccess()

259

}

260

}

261

}

262

}

263

264

val depsCompilationResult = compiler(scriptWithDeps.toScriptSource(), configWithMavenDeps)

265

when (depsCompilationResult) {

266

is ResultWithDiagnostics.Success -> {

267

val jarGenerator = BasicJvmScriptJarGenerator(File("script-with-deps.jar"))

268

jarGenerator(depsCompilationResult.value, ScriptEvaluationConfiguration.Default)

269

270

// The generated JAR will include proper Class-Path entries for Maven dependencies

271

}

272

}

273

```

274

275

## Class File Generation

276

277

Individual class file generation provides fine-grained control over script deployment and integration.

278

279

### Class File Organization

280

281

Generated class files maintain the package structure and naming conventions of the original script:

282

283

```text

284

output/classes/

285

├── Script_12345.class # Main script class

286

├── Script_12345$Person.class # Data class defined in script

287

├── Script_12345$Helper.class # Helper class or function

288

└── Script_12345$WhenMappings.class # Compiler-generated utility classes

289

```

290

291

**Custom Class Generation Example:**

292

293

```kotlin

294

class CustomScriptClassGenerator(

295

private val outputDir: File,

296

private val packageName: String? = null

297

) : ScriptEvaluator {

298

299

override suspend operator fun invoke(

300

compiledScript: CompiledScript,

301

scriptEvaluationConfiguration: ScriptEvaluationConfiguration

302

): ResultWithDiagnostics<EvaluationResult> {

303

304

return try {

305

if (compiledScript !is KJvmCompiledScript) {

306

return ResultWithDiagnostics.Failure(

307

listOf("Cannot generate classes: unsupported compiled script type".asErrorDiagnostics())

308

)

309

}

310

311

val module = (compiledScript.getCompiledModule() as? KJvmCompiledModuleInMemory)

312

?: return ResultWithDiagnostics.Failure(

313

listOf("Cannot generate classes: unsupported module type".asErrorDiagnostics())

314

)

315

316

// Create package directory structure if specified

317

val targetDir = if (packageName != null) {

318

File(outputDir, packageName.replace('.', '/'))

319

} else {

320

outputDir

321

}

322

323

if (!targetDir.exists()) {

324

targetDir.mkdirs()

325

}

326

327

// Write all class files

328

for ((path, bytes) in module.compilerOutputFiles) {

329

val classFile = File(targetDir, File(path).name)

330

classFile.writeBytes(bytes)

331

println("Generated class file: ${classFile.relativeTo(outputDir)}")

332

}

333

334

ResultWithDiagnostics.Success(

335

EvaluationResult(ResultValue.NotEvaluated, scriptEvaluationConfiguration)

336

)

337

338

} catch (e: Throwable) {

339

ResultWithDiagnostics.Failure(

340

listOf(e.asErrorDiagnostics("Cannot generate script classes: ${e.message}"))

341

)

342

}

343

}

344

}

345

346

// Use custom generator

347

val customGenerator = CustomScriptClassGenerator(

348

outputDir = File("custom-output"),

349

packageName = "com.example.scripts"

350

)

351

352

val customResult = customGenerator(compiledScript, ScriptEvaluationConfiguration.Default)

353

```

354

355

## Integration with Build Systems

356

357

Generated artifacts can be integrated into existing build systems and deployment pipelines.

358

359

### Maven Integration

360

361

```xml

362

<plugin>

363

<groupId>org.codehaus.mojo</groupId>

364

<artifactId>exec-maven-plugin</artifactId>

365

<version>3.1.0</version>

366

<configuration>

367

<mainClass>kotlin.script.experimental.jvmhost.GenerateScriptJar</mainClass>

368

<args>

369

<arg>src/main/scripts/my-script.kts</arg>

370

<arg>target/generated-scripts/my-script.jar</arg>

371

</args>

372

</configuration>

373

</plugin>

374

```

375

376

### Gradle Integration

377

378

```kotlin

379

tasks.register<JavaExec>("generateScriptJars") {

380

classpath = configurations.runtimeClasspath.get()

381

mainClass.set("kotlin.script.experimental.jvmhost.GenerateScriptJar")

382

args = listOf(

383

"src/main/scripts/",

384

"build/generated-jars/"

385

)

386

}

387

```

388

389

### Standalone Execution

390

391

Generated JAR files can be executed directly:

392

393

```bash

394

# Execute generated script JAR

395

java -jar generated-script.jar

396

397

# Execute with additional JVM options

398

java -Xmx2g -jar data-processing-script.jar

399

400

# Execute with custom classpath additions

401

java -cp "lib/*:generated-script.jar" Script_12345

402

```