Skip to content

Commit 6a007d5

Browse files
committed
* Better cache design.
* Idea tests and packaging tests have separate test suites
1 parent 919ce1f commit 6a007d5

File tree

9 files changed

+127
-92
lines changed

9 files changed

+127
-92
lines changed

src/main/kotlin/kscript/app/KscriptHandler.kt

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@ import kscript.app.creator.*
66
import kscript.app.model.Config
77
import kscript.app.model.ScriptType
88
import kscript.app.parser.Parser
9-
import kscript.app.resolver.*
9+
import kscript.app.resolver.CommandResolver
10+
import kscript.app.resolver.DependencyResolver
11+
import kscript.app.resolver.ScriptResolver
12+
import kscript.app.resolver.SectionResolver
13+
import kscript.app.util.Executor
1014
import kscript.app.util.Logger
15+
import kscript.app.util.Logger.infoMsg
1116
import org.docopt.DocOptWrapper
17+
import java.net.URI
1218

1319
class KscriptHandler(private val config: Config, private val docopt: DocOptWrapper) {
1420

@@ -52,8 +58,13 @@ class KscriptHandler(private val config: Config, private val docopt: DocOptWrapp
5258

5359
// Create temporary dev environment
5460
if (docopt.getBoolean("idea")) {
55-
val projectPath = IdeaProjectCreator(appDir.cache).create(script, userArgs)
56-
executor.runIdea(projectPath)
61+
val path = appDir.cache.getOrCreateIdeaProject(script.digest) { basePath ->
62+
val uriLocalPathProvider = { uri: URI -> appDir.cache.getOrCreateUriItem(uri).path }
63+
IdeaProjectCreator().create(basePath, script, userArgs, uriLocalPathProvider)
64+
}
65+
66+
infoMsg("Project set up at $path")
67+
executor.runIdea(path)
5768
return
5869
}
5970

@@ -69,11 +80,17 @@ class KscriptHandler(private val config: Config, private val docopt: DocOptWrapp
6980
throw IllegalStateException("@Entry directive is just supported for kt class files")
7081
}
7182

72-
val jar = JarCreator(appDir.cache, executor).create(script, resolvedDependencies)
83+
val jar = appDir.cache.getOrCreateJar(script.digest) { basePath ->
84+
JarArtifactCreator(executor).create(basePath, script, resolvedDependencies)
85+
}
7386

7487
//if requested try to package the into a standalone binary
7588
if (docopt.getBoolean("package")) {
76-
PackageCreator(appDir.cache, executor).packageKscript(script, jar)
89+
val path = appDir.cache.getOrCreatePackage(script.digest) { basePath ->
90+
PackageCreator(executor).packageKscript(basePath, script, jar)
91+
}
92+
93+
infoMsg("Package created in: $path")
7794
return
7895
}
7996

src/main/kotlin/kscript/app/appdir/Cache.kt

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package kscript.app.appdir
22

3+
import kscript.app.creator.JarArtifact
34
import kscript.app.model.ScriptType
5+
import kscript.app.util.Logger.devMsg
46
import kscript.app.util.ScriptUtils
57
import org.apache.commons.codec.digest.DigestUtils
68
import java.net.URI
@@ -21,22 +23,30 @@ data class UriItem(
2123
)
2224

2325
class Cache(private val path: Path) {
24-
fun findOrCreateIdea(digest: String): Path {
25-
val directory = path.resolve("idea_$digest")
26-
return if (directory.exists()) directory else directory.createDirectories()
26+
fun getOrCreateIdeaProject(digest: String, creator: (Path) -> Path): Path {
27+
return directoryCache(path.resolve("idea_$digest"), creator)
2728
}
2829

29-
fun findOrCreateJar(digest: String): Path {
30-
val directory = path.resolve("jar_$digest")
31-
return if (directory.exists()) directory else directory.createDirectories()
30+
fun getOrCreatePackage(digest: String, creator: (Path) -> Path): Path {
31+
return directoryCache(path.resolve("package_$digest"), creator)
3232
}
3333

34-
fun findOrCreatePackage(digest: String): Path {
35-
val directory = path.resolve("package_$digest")
36-
return if (directory.exists()) directory else directory.createDirectories()
34+
fun getOrCreateJar(digest: String, creator: (Path) -> JarArtifact): JarArtifact {
35+
val directory = path.resolve("jar_$digest")
36+
val cachedJarArtifact = directory.resolve("jarArtifact.descriptor")
37+
38+
return if (directory.exists()) {
39+
val jarArtifactLines = cachedJarArtifact.readText().lines()
40+
JarArtifact(Paths.get(jarArtifactLines[0]), jarArtifactLines[1])
41+
} else {
42+
directory.createDirectories()
43+
val jarArtifact = creator(directory)
44+
cachedJarArtifact.writeText("${jarArtifact.path}\n${jarArtifact.execClassName}")
45+
jarArtifact
46+
}
3747
}
3848

39-
fun readUri(uri: URI): UriItem {
49+
fun getOrCreateUriItem(uri: URI): UriItem {
4050
val digest = DigestUtils.md5Hex(uri.toString())
4151

4252
if (uri.scheme == "file") {
@@ -53,7 +63,10 @@ class Cache(private val path: Path) {
5363

5464
if (descriptorFile.exists() && contentFile.exists()) {
5565
//Cache hit
56-
val descriptor = descriptorFile.readText().split(" ")
66+
val descriptor = descriptorFile.readText().lines()
67+
68+
devMsg("Descriptor: $descriptor")
69+
5770
val scriptType = ScriptType.valueOf(descriptor[0])
5871
val fileName = descriptor[1]
5972
val cachedUri = URI.create(descriptor[2])
@@ -70,9 +83,18 @@ class Cache(private val path: Path) {
7083
val fileName = ScriptUtils.extractFileName(resolvedUri)
7184
val contextUri = resolvedUri.resolve(".")
7285

73-
descriptorFile.writeText("$scriptType $fileName $resolvedUri $contextUri")
86+
descriptorFile.writeText("$scriptType\n$fileName\n$resolvedUri\n$contextUri")
7487
contentFile.writeText(content)
7588

7689
return UriItem(content, scriptType, fileName, resolvedUri, contextUri, contentFile)
7790
}
91+
92+
private fun directoryCache(path: Path, creator: (Path) -> Path): Path {
93+
return if (path.exists()) {
94+
path
95+
} else {
96+
path.createDirectories()
97+
creator(path)
98+
}
99+
}
78100
}
Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,40 @@
11
package kscript.app.creator
22

3-
import kscript.app.appdir.Cache
43
import kscript.app.code.GradleTemplates
54
import kscript.app.code.Templates
65
import kscript.app.model.Script
76
import kscript.app.util.FileUtils
87
import kscript.app.util.Logger.devMsg
98
import kscript.app.util.Logger.infoMsg
9+
import java.net.URI
1010
import java.nio.file.Path
11-
import kotlin.io.path.exists
12-
13-
class IdeaProjectCreator(private val cache: Cache) {
14-
15-
fun create(script: Script, userArgs: List<String>): Path {
16-
val projectPath = cache.findOrCreateIdea(script.digest)
17-
18-
if (hasIdeaFiles(projectPath)) {
19-
return projectPath
20-
}
2111

12+
class IdeaProjectCreator {
13+
fun create(basePath: Path, script: Script, userArgs: List<String>, uriLocalPathProvider: (URI) -> Path): Path {
2214
infoMsg("Setting up idea project...")
2315

2416
for (scriptNode in script.scriptNodes) {
2517
val sourceUri = scriptNode.sourceUri
26-
val path = projectPath.resolve("src/${scriptNode.scriptName}")
18+
val path = basePath.resolve("src/${scriptNode.scriptName}")
2719

2820
if (sourceUri == null) {
2921
FileUtils.createFile(path, scriptNode.sections.joinToString("\n") { it.code })
3022
} else {
31-
devMsg("link: $path, target: ${cache.readUri(sourceUri).path}")
32-
FileUtils.symLinkOrCopy(path, cache.readUri(sourceUri).path)
23+
val targetPath = uriLocalPathProvider(sourceUri)
24+
devMsg("link: $path, target: $targetPath")
25+
FileUtils.symLinkOrCopy(path, targetPath)
3326
}
3427
}
3528

3629
FileUtils.createFile(
37-
projectPath.resolve(".idea/runConfigurations/Main.xml"),
30+
basePath.resolve(".idea/runConfigurations/Main.xml"),
3831
Templates.runConfig(script.rootNode.scriptName, userArgs)
3932
)
4033

4134
FileUtils.createFile(
42-
projectPath.resolve("build.gradle.kts"), GradleTemplates.createGradleIdeaScript(script)
35+
basePath.resolve("build.gradle.kts"), GradleTemplates.createGradleIdeaScript(script)
4336
)
4437

45-
infoMsg("Project set up at $projectPath")
46-
47-
return projectPath
38+
return basePath
4839
}
49-
50-
private fun hasIdeaFiles(projectPath: Path) = projectPath.resolve("src").exists()
5140
}

src/main/kotlin/kscript/app/creator/JarCreator.kt renamed to src/main/kotlin/kscript/app/creator/JarArtifactCreator.kt

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
package kscript.app.creator
22

3-
import kscript.app.appdir.Cache
43
import kscript.app.code.Templates
54
import kscript.app.model.Script
65
import kscript.app.model.ScriptType
6+
import kscript.app.util.Executor
77
import kscript.app.util.FileUtils
88
import kscript.app.util.ScriptUtils.dropExtension
99
import java.nio.file.Path
10-
import kotlin.io.path.exists
10+
import kotlin.io.path.writeText
1111

1212
data class JarArtifact(val path: Path, val execClassName: String)
1313

14-
class JarCreator(private val cache: Cache, private val executor: Executor) {
14+
class JarArtifactCreator(private val executor: Executor) {
1515

16-
fun create(script: Script, resolvedDependencies: Set<Path>): JarArtifact {
16+
fun create(basePath: Path, script: Script, resolvedDependencies: Set<Path>): JarArtifact {
1717
// Capitalize first letter and get rid of dashes (since this is what kotlin compiler is doing for the wrapper to create a valid java class name)
1818
// For valid characters see https://stackoverflow.com/questions/4814040/allowed-characters-in-filename
1919
val className =
@@ -28,13 +28,11 @@ class JarCreator(private val cache: Cache, private val executor: Executor) {
2828
"""${script.packageName.value}.${script.entryPoint?.value ?: "${className}Kt"}"""
2929
}
3030

31-
val jarPath = cache.findOrCreateJar(script.digest)
32-
val jarFile = jarPath.resolve("scriplet.jar")
33-
val scriptFile = jarPath.resolve(className + script.scriptType.extension)
31+
val jarFile = basePath.resolve("scriplet.jar")
32+
val scriptFile = basePath.resolve(className + script.scriptType.extension)
33+
val execClassNameFile = basePath.resolve("scripletExecClassName.txt")
3434

35-
if (scriptFile.exists()) {
36-
return JarArtifact(jarFile, execClassName)
37-
}
35+
execClassNameFile.writeText(execClassName)
3836

3937
FileUtils.createFile(scriptFile, script.resolvedCode)
4038

@@ -44,7 +42,7 @@ class JarCreator(private val cache: Cache, private val executor: Executor) {
4442
// create main-wrapper for kts scripts
4543
if (script.scriptType == ScriptType.KTS) {
4644
val wrapper = FileUtils.createFile(
47-
jarPath.resolve("$execClassName.kt"), Templates.wrapperForScript(script.packageName, className)
45+
basePath.resolve("$execClassName.kt"), Templates.wrapperForScript(script.packageName, className)
4846
)
4947
filesToCompile.add(wrapper)
5048
}
Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,34 @@
11
package kscript.app.creator
22

3-
import kscript.app.appdir.Cache
43
import kscript.app.code.GradleTemplates
54
import kscript.app.code.Templates
65
import kscript.app.model.Script
6+
import kscript.app.util.Executor
77
import kscript.app.util.FileUtils
88
import kscript.app.util.Logger.infoMsg
9+
import java.nio.file.Path
910

10-
class PackageCreator(private val cache: Cache, private val executor: Executor) {
11+
class PackageCreator(private val executor: Executor) {
1112
/**
1213
* Create and use a temporary gradle project to package the compiled script using capsule.
1314
* See https://github.com/puniverse/capsule
1415
*/
15-
fun packageKscript(script: Script, jarArtifact: JarArtifact) {
16+
fun packageKscript(basePath: Path, script: Script, jarArtifact: JarArtifact): Path {
1617
infoMsg("Packaging script '${script.scriptName}' into standalone executable...")
1718

18-
val projectDir = cache.findOrCreatePackage(script.digest)
19-
2019
// create exec_header to allow for direction execution (see http://www.capsule.io/user-guide/#really-executable-capsules)
2120
// from https://github.com/puniverse/capsule/blob/master/capsule-util/src/main/resources/capsule/execheader.sh
22-
FileUtils.createFile(projectDir.resolve("exec_header.sh"), Templates.executeHeader)
21+
FileUtils.createFile(basePath.resolve("exec_header.sh"), Templates.executeHeader)
2322
FileUtils.createFile(
24-
projectDir.resolve("build.gradle.kts"),
25-
GradleTemplates.createGradlePackageScript(script, jarArtifact)
23+
basePath.resolve("build.gradle.kts"), GradleTemplates.createGradlePackageScript(script, jarArtifact)
2624
)
2725

28-
executor.createPackage(projectDir)
26+
executor.createPackage(basePath)
27+
28+
basePath.resolve("build/libs/appName").toFile().setExecutable(true)
2929

30-
projectDir.resolve("build/libs/appName").toFile().setExecutable(true)
30+
infoMsg("Finished packaging '${script.scriptName}'; executable path: ${basePath}/build/libs/")
3131

32-
infoMsg("Finished packaging '${script.scriptName}'; executable path: ${projectDir}/build/libs/")
32+
return basePath
3333
}
3434
}

src/main/kotlin/kscript/app/resolver/ScriptResolver.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class ScriptResolver(
4444

4545
//Is it a URL?
4646
if (ScriptUtils.isUrl(string)) {
47-
val uriItem = cache.readUri(URL(string).toURI())
47+
val uriItem = cache.getOrCreateUriItem(URL(string).toURI())
4848
val scriptText = ScriptUtils.prependPreambles(preambles, uriItem.content)
4949

5050
return createScript(
@@ -63,7 +63,7 @@ class ScriptResolver(
6363
if (file.canRead()) {
6464
if (kotlinExtensions.contains(file.extension)) {
6565
//Regular file
66-
val uriItem = cache.readUri(file.toURI())
66+
val uriItem = cache.getOrCreateUriItem(file.toURI())
6767
val scriptText = ScriptUtils.prependPreambles(preambles, uriItem.content)
6868

6969
return createScript(

src/main/kotlin/kscript/app/resolver/SectionResolver.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class SectionResolver(private val parser: Parser, private val cache: Cache, priv
6767
throw IllegalStateException("References to local files from remote scripts are disallowed.")
6868
}
6969

70-
val uriItem = cache.readUri(uri)
70+
val uriItem = cache.getOrCreateUriItem(uri)
7171

7272
val newSections = resolve(
7373
uriItem.content,

src/main/kotlin/kscript/app/creator/Executor.kt renamed to src/main/kotlin/kscript/app/util/Executor.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
package kscript.app.creator
1+
package kscript.app.util
22

3+
import kscript.app.creator.JarArtifact
34
import kscript.app.model.Config
45
import kscript.app.resolver.CommandResolver
5-
import kscript.app.util.Logger
66
import kscript.app.util.Logger.devMsg
7-
import kscript.app.util.ProcessRunner
8-
import kscript.app.util.ShellUtils
97
import java.nio.file.Path
108

119
class Executor(private val commandResolver: CommandResolver, private val config: Config) {

0 commit comments

Comments
 (0)