Skip to content

Commit 4ef88b2

Browse files
yigittschuchortdev
authored andcommitted
Cache host classpath
This PR adds caching for host classpath to avoid calling ClassGraph for every compilation. In my local tests, it saves between 50 to 80ms per compilation on my laptop, which is not necessarily much but adds up when you run hundreds of them. I've also cached common jars we find from there. It does not necessarily help with performance but rather as a cleanup to keep all of them in 1 place. Test: existing tests Issue: google#113
1 parent 5d69d6d commit 4ef88b2

File tree

4 files changed

+74
-46
lines changed

4 files changed

+74
-46
lines changed

core/src/main/kotlin/com/tschuchort/compiletesting/AbstractKotlinCompilation.kt

+2-35
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.tschuchort.compiletesting
22

3-
import io.github.classgraph.ClassGraph
43
import okio.Buffer
54
import org.jetbrains.kotlin.base.kapt3.KaptOptions
65
import org.jetbrains.kotlin.cli.common.CLICompiler
@@ -97,8 +96,7 @@ abstract class AbstractKotlinCompilation<A : CommonCompilerArguments> internal c
9796
* process' classpaths
9897
*/
9998
var kotlinStdLibCommonJar: File? by default {
100-
findInHostClasspath(hostClasspaths, "kotlin-stdlib-common.jar",
101-
kotlinDependencyRegex("kotlin-stdlib-common"))
99+
HostEnvironment.kotlinStdLibCommonJar
102100
}
103101

104102
// Directory for input source files
@@ -221,22 +219,7 @@ abstract class AbstractKotlinCompilation<A : CommonCompilerArguments> internal c
221219
}
222220
}
223221

224-
/** Tries to find a file matching the given [regex] in the host process' classpath */
225-
protected fun findInHostClasspath(hostClasspaths: List<File>, simpleName: String, regex: Regex): File? {
226-
val jarFile = hostClasspaths.firstOrNull { classpath ->
227-
classpath.name.matches(regex)
228-
//TODO("check that jar file actually contains the right classes")
229-
}
230-
231-
if (jarFile == null)
232-
log("Searched host classpaths for $simpleName and found no match")
233-
else
234-
log("Searched host classpaths for $simpleName and found ${jarFile.path}")
235-
236-
return jarFile
237-
}
238-
239-
protected val hostClasspaths by lazy { getHostClasspaths() }
222+
protected val hostClasspaths by lazy { HostEnvironment.classpath }
240223

241224
/* This internal buffer and stream is used so it can be easily converted to a string
242225
that is put into the [Result] object, in addition to printing immediately to the user's
@@ -266,22 +249,6 @@ abstract class AbstractKotlinCompilation<A : CommonCompilerArguments> internal c
266249
internal val internalMessageStreamAccess: PrintStream get() = internalMessageStream
267250
}
268251

269-
internal fun kotlinDependencyRegex(prefix:String): Regex {
270-
return Regex("$prefix(-[0-9]+\\.[0-9]+(\\.[0-9]+)?)([-0-9a-zA-Z]+)?\\.jar")
271-
}
272-
273-
/** Returns the files on the classloader's classpath and modulepath */
274-
internal fun getHostClasspaths(): List<File> {
275-
val classGraph = ClassGraph()
276-
.enableSystemJarsAndModules()
277-
.removeTemporaryFilesAfterScan()
278-
279-
val classpaths = classGraph.classpathFiles
280-
val modules = classGraph.modules.mapNotNull { it.locationFile }
281-
282-
return (classpaths + modules).distinctBy(File::getAbsolutePath)
283-
}
284-
285252
internal fun convertKotlinExitCode(code: ExitCode) = when(code) {
286253
ExitCode.OK -> KotlinCompilation.ExitCode.OK
287254
ExitCode.INTERNAL_ERROR -> KotlinCompilation.ExitCode.INTERNAL_ERROR
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.tschuchort.compiletesting
2+
3+
import io.github.classgraph.ClassGraph
4+
import java.io.File
5+
6+
/**
7+
* Utility object to provide everything we might discover from the host environment.
8+
*/
9+
internal object HostEnvironment {
10+
val classpath by lazy {
11+
getHostClasspaths()
12+
}
13+
14+
val kotlinStdLibJar: File? by lazy {
15+
findInClasspath(kotlinDependencyRegex("(kotlin-stdlib|kotlin-runtime)"))
16+
}
17+
18+
val kotlinStdLibCommonJar: File? by lazy {
19+
findInClasspath(kotlinDependencyRegex("kotlin-stdlib-common"))
20+
}
21+
22+
val kotlinStdLibJdkJar: File? by lazy {
23+
findInClasspath(kotlinDependencyRegex("kotlin-stdlib-jdk[0-9]+"))
24+
}
25+
26+
val kotlinStdLibJsJar: File? by default {
27+
findInClasspath(kotlinDependencyRegex("kotlin-stdlib-js"))
28+
}
29+
30+
val kotlinReflectJar: File? by lazy {
31+
findInClasspath(kotlinDependencyRegex("kotlin-reflect"))
32+
}
33+
34+
val kotlinScriptRuntimeJar: File? by lazy {
35+
findInClasspath(kotlinDependencyRegex("kotlin-script-runtime"))
36+
}
37+
38+
val toolsJar: File? by lazy {
39+
findInClasspath(Regex("tools.jar"))
40+
}
41+
42+
private fun kotlinDependencyRegex(prefix: String): Regex {
43+
return Regex("$prefix(-[0-9]+\\.[0-9]+(\\.[0-9]+)?)([-0-9a-zA-Z]+)?\\.jar")
44+
}
45+
46+
/** Tries to find a file matching the given [regex] in the host process' classpath */
47+
private fun findInClasspath(regex: Regex): File? {
48+
val jarFile = classpath.firstOrNull { classpath ->
49+
classpath.name.matches(regex)
50+
//TODO("check that jar file actually contains the right classes")
51+
}
52+
return jarFile
53+
}
54+
55+
/** Returns the files on the classloader's classpath and modulepath */
56+
private fun getHostClasspaths(): List<File> {
57+
val classGraph = ClassGraph()
58+
.enableSystemJarsAndModules()
59+
.removeTemporaryFilesAfterScan()
60+
61+
val classpaths = classGraph.classpathFiles
62+
val modules = classGraph.modules.mapNotNull { it.locationFile }
63+
64+
return (classpaths + modules).distinctBy(File::getAbsolutePath)
65+
}
66+
}

core/src/main/kotlin/com/tschuchort/compiletesting/KotlinCompilation.kt

+5-9
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,7 @@ class KotlinCompilation : AbstractKotlinCompilation<K2JVMCompilerArguments>() {
178178
* process' classpaths
179179
*/
180180
var kotlinStdLibJar: File? by default {
181-
findInHostClasspath(hostClasspaths, "kotlin-stdlib.jar",
182-
kotlinDependencyRegex("(kotlin-stdlib|kotlin-runtime)"))
181+
HostEnvironment.kotlinStdLibJar
183182
}
184183

185184
/**
@@ -188,8 +187,7 @@ class KotlinCompilation : AbstractKotlinCompilation<K2JVMCompilerArguments>() {
188187
* process' classpaths
189188
*/
190189
var kotlinStdLibJdkJar: File? by default {
191-
findInHostClasspath(hostClasspaths, "kotlin-stdlib-jdk*.jar",
192-
kotlinDependencyRegex("kotlin-stdlib-jdk[0-9]+"))
190+
HostEnvironment.kotlinStdLibJdkJar
193191
}
194192

195193
/**
@@ -198,8 +196,7 @@ class KotlinCompilation : AbstractKotlinCompilation<K2JVMCompilerArguments>() {
198196
* process' classpaths
199197
*/
200198
var kotlinReflectJar: File? by default {
201-
findInHostClasspath(hostClasspaths, "kotlin-reflect.jar",
202-
kotlinDependencyRegex("kotlin-reflect"))
199+
HostEnvironment.kotlinReflectJar
203200
}
204201

205202
/**
@@ -208,8 +205,7 @@ class KotlinCompilation : AbstractKotlinCompilation<K2JVMCompilerArguments>() {
208205
* process' classpaths
209206
*/
210207
var kotlinScriptRuntimeJar: File? by default {
211-
findInHostClasspath(hostClasspaths, "kotlin-script-runtime.jar",
212-
kotlinDependencyRegex("kotlin-script-runtime"))
208+
HostEnvironment.kotlinScriptRuntimeJar
213209
}
214210

215211
/**
@@ -221,7 +217,7 @@ class KotlinCompilation : AbstractKotlinCompilation<K2JVMCompilerArguments>() {
221217
var toolsJar: File? by default {
222218
if (!isJdk9OrLater())
223219
jdkHome?.let { findToolsJarFromJdk(it) }
224-
?: findInHostClasspath(hostClasspaths, "tools.jar", Regex("tools.jar"))
220+
?: HostEnvironment.toolsJar
225221
else
226222
null
227223
}

core/src/main/kotlin/com/tschuchort/compiletesting/KotlinJsCompilation.kt

+1-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ class KotlinJsCompilation : AbstractKotlinCompilation<K2JSCompilerArguments>() {
4242
* process' classpaths
4343
*/
4444
var kotlinStdLibJsJar: File? by default {
45-
findInHostClasspath(hostClasspaths, "kotlin-stdlib-js.jar",
46-
kotlinDependencyRegex("kotlin-stdlib-js"))
45+
HostEnvironment.kotlinStdLibJsJar
4746
}
4847

4948
// *.class files, Jars and resources (non-temporary) that are created by the

0 commit comments

Comments
 (0)