Skip to content

Commit

Permalink
Fix #335: Get --package working with Gradle 7 by replacing the capsul…
Browse files Browse the repository at this point in the history
…e plugin (#370)

* Fix #335: Remove the need for the  Gradle capsule plugin.
  • Loading branch information
vsajip authored Jul 1, 2022
1 parent d77cd45 commit 715bc75
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 31 deletions.
58 changes: 39 additions & 19 deletions src/main/kotlin/kscript/app/code/GradleTemplates.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ object GradleTemplates {
|""".trimMargin()
}

//Capsule: https://github.com/ngyewch/gradle-capsule-plugin
fun createGradlePackageScript(script: Script, jarArtifact: JarArtifact): String {
val kotlinOptions = createCompilerOptionsSection(script.compilerOpts)

Expand All @@ -49,42 +48,63 @@ object GradleTemplates {
) + script.dependencies

val capsuleApp = jarArtifact.execClassName
val baseName = script.scriptName

return """
import java.io.*
import java.lang.System
import java.nio.file.Files
import java.nio.file.Paths
plugins {
id("org.jetbrains.kotlin.jvm") version "$kotlinVersion"
id("it.gianluz.capsule") version "1.0.3"
application
}
repositories {
mavenLocal()
mavenCentral()
${createGradleRepositoriesSection(script.repositories).prependIndent()}
${createGradleRepositoriesSection(script.repositories).prependIndent()}
}
tasks.create<us.kirchmeier.capsule.task.FatCapsule>("simpleCapsule") {
applicationClass("$capsuleApp")
archiveFileName.set("${script.scriptName}")
// https://github.com/danthegoodman/gradle-capsule-plugin/blob/master/DOCUMENTATION.md#really-executable-capsules
reallyExecutable
capsuleManifest.apply {
applicationClass = "$capsuleApp"
application = "${script.scriptName}"
applicationScript = "exec_header.sh"
jvmArgs = listOf()
tasks.jar {
manifest {
attributes["Main-Class"] = "$capsuleApp"
}
baseName = "$baseName"
configurations["compileClasspath"].forEach { file: File ->
from(zipTree(file.absoluteFile))
}
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
tasks.register("makeScript") {
dependsOn(":jar")
doLast {
val headerDir = layout.projectDirectory.toString()
val jarFileName = layout.buildDirectory.file("libs/$baseName.jar").get().toString()
val outFileName = layout.buildDirectory.file("libs/$baseName").get().toString()
val lineSeparator = System.getProperty("line.separator").encodeToByteArray()
val headerPath = Paths.get(headerDir).resolve("exec_header.sh")
val headerBytes = Files.readAllBytes(headerPath)
val jarBytes = Files.readAllBytes(Paths.get(jarFileName))
val outFile = Paths.get(outFileName).toFile()
val fileStream = FileOutputStream(outFile)
fileStream.write(headerBytes)
fileStream.write(lineSeparator)
fileStream.write(jarBytes)
fileStream.close()
}
}
dependencies {
implementation(files("${jarArtifact.path.parent.resolve("scriplet.jar")}"))
${createGradleDependenciesSection(extendedDependencies).prependIndent()}
${createGradleDependenciesSection(extendedDependencies).prependIndent()}
}
$kotlinOptions
""".trimIndent()
""".trimStart('\n').trimIndent()
}

private fun createGradleRepositoryCredentials(repository: Repository): String {
Expand Down
27 changes: 24 additions & 3 deletions src/main/kotlin/kscript/app/creator/PackageCreator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,48 @@ import kscript.app.util.FileUtils
import kscript.app.util.Logger.infoMsg
import kscript.app.util.OsPath
import kscript.app.util.toNativeFile
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths

class PackageCreator(private val executor: Executor) {
/**
* Create and use a temporary gradle project to package the compiled script using capsule.
* See https://github.com/puniverse/capsule
*/

fun makeHeader(options: List<String>) : String {
val opts = options.joinToString(" ").trim()

return Templates.executeHeader.replace("java -jar", "java $opts -jar")
}

fun packageKscript(basePath: OsPath, script: Script, jarArtifact: JarArtifact): OsPath {
val baseName = script.scriptName

infoMsg("Packaging script '${script.scriptName}' into standalone executable...")

// create exec_header to allow for direction execution (see http://www.capsule.io/user-guide/#really-executable-capsules)
// from https://github.com/puniverse/capsule/blob/master/capsule-util/src/main/resources/capsule/execheader.sh
FileUtils.createFile(basePath.resolve("exec_header.sh"), Templates.executeHeader)
val options = mutableListOf("")

for (opt in script.kotlinOpts) {
val s = opt.value
if (s.startsWith("-J")) {
options.add(s.substring(2))
}
}
FileUtils.createFile(basePath.resolve("exec_header.sh"), makeHeader(options))
FileUtils.createFile(
basePath.resolve("build.gradle.kts"), GradleTemplates.createGradlePackageScript(script, jarArtifact)
)

executor.createPackage(basePath)

basePath.resolve("build/libs/appName").toNativeFile().setExecutable(true)
val executable = File(basePath.resolve("build/libs/$baseName").toString())
executable.setExecutable(true)

infoMsg("Finished packaging '${script.scriptName}'; executable path: ${basePath}/build/libs/")
infoMsg("Finished packaging '${script.scriptName}'; executable path: ${basePath}/build/libs/$baseName")

return basePath
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/kscript/app/resolver/CommandResolver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class CommandResolver(private val osConfig: OsConfig) {
}

fun createPackage(projectPath: OsPath): String {
return "cd '${projectPath}' && ${osConfig.gradleCommand} simpleCapsule"
return "cd '${projectPath}' && ${osConfig.gradleCommand} makeScript"
}

private fun resolveKotlinOpts(kotlinOpts: Set<KotlinOpt>) = kotlinOpts.joinToString(" ") { it.value }
Expand Down
2 changes: 1 addition & 1 deletion test/linux_suite.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ start_suite "kt_support" $REQUESTED_SUITES
start_suite "custom_interpreters" $REQUESTED_SUITES
start_suite "misc" $REQUESTED_SUITES
start_suite "bootstrap_headers" $REQUESTED_SUITES
#start_suite "packaging" $REQUESTED_SUITES
start_suite "packaging" $REQUESTED_SUITES
start_suite "idea" $REQUESTED_SUITES
4 changes: 2 additions & 2 deletions test/resources/package_example.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@file:KotlinOpts("-J-Xmx5g")
@file:KotlinOpts("-J-Xmx512m")
@file:DependsOn("log4j:log4j:1.2.14")


Expand All @@ -8,4 +8,4 @@ org.apache.log4j.Logger.getRootLogger()
// https://stackoverflow.com/questions/7611987/how-to-check-the-configured-xmx-value-for-a-running-java-application

//println("arg is " + args[0])
println("package_me_args_" + args.size + "_mem_" + Runtime.getRuntime().maxMemory())
println("package_me_args_" + args.size + "_mem_" + Runtime.getRuntime().maxMemory())
11 changes: 6 additions & 5 deletions test/suite/packaging.sh
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
## can we resolve relative imports when using tmp-scripts (see #95)
assert "rm -f ${PROJECT_DIR}/test/package_example && kscript --package ${PROJECT_DIR}/test/resources/package_example.kts &>/dev/null && ${PROJECT_DIR}/test/package_example 1" "package_me_args_1_mem_5368709120"

assert "rm -f ${PROJECT_DIR}/test/package_example && kscript --package ${PROJECT_DIR}/test/resources/package_example.kts &>/dev/null && ${PROJECT_DIR}/test/package_example 1" "package_me_args_1_mem_536870912"
rm -f "${PROJECT_DIR}/test/package_example"
## https://unix.stackexchange.com/questions/17064/how-to-print-only-last-column
assert 'rm -f kscriptlet* && cmd=$(kscript --package "println(args.size)" 2>&1 | tail -n1 | cut -f 5 -d " ") && $cmd three arg uments' "3"

assert 'rm -f scriplet* && kscript --package "println(args.size)" >/dev/null 2>&1 && ${PROJECT_DIR}/test/scriplet three arg uments' "3"
rm -f "${PROJECT_DIR}/test/scriplet"
#assert "kscript --package test/resources/package_example.kts" "foo"
#assert "./package_example 1" "package_me_args_1_mem_4772593664"da
#assert "echo 1" "package_me_args_1_mem_4772593664"
#assert_statement 'rm -f kscriptlet* && kscript --package "println(args.size)"' "foo" "bar" 0

# ensure that the jar file is executable
assert_raises "java -jar build/lib/kscript.jar" 0
# This test doesn't appear to be needed as we're not executing jars directly
#assert_raises "java -jar build/lib/kscript.jar" 0

0 comments on commit 715bc75

Please sign in to comment.