Skip to content

Commit

Permalink
Cache host classpath
Browse files Browse the repository at this point in the history
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: tschuchortdev#113
  • Loading branch information
yigit committed Feb 14, 2021
1 parent 6fba64e commit bd6e6f2
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.tschuchort.compiletesting

import io.github.classgraph.ClassGraph
import okio.Buffer
import org.jetbrains.kotlin.base.kapt3.KaptOptions
import org.jetbrains.kotlin.cli.common.CLICompiler
Expand Down Expand Up @@ -97,8 +96,7 @@ abstract class AbstractKotlinCompilation<A : CommonCompilerArguments> internal c
* process' classpaths
*/
var kotlinStdLibCommonJar: File? by default {
findInHostClasspath(hostClasspaths, "kotlin-stdlib-common.jar",
kotlinDependencyRegex("kotlin-stdlib-common"))
HostEnvironment.kotlinStdLibCommonJar
}

// Directory for input source files
Expand Down Expand Up @@ -221,22 +219,7 @@ abstract class AbstractKotlinCompilation<A : CommonCompilerArguments> internal c
}
}

/** Tries to find a file matching the given [regex] in the host process' classpath */
protected fun findInHostClasspath(hostClasspaths: List<File>, simpleName: String, regex: Regex): File? {
val jarFile = hostClasspaths.firstOrNull { classpath ->
classpath.name.matches(regex)
//TODO("check that jar file actually contains the right classes")
}

if (jarFile == null)
log("Searched host classpaths for $simpleName and found no match")
else
log("Searched host classpaths for $simpleName and found ${jarFile.path}")

return jarFile
}

protected val hostClasspaths by lazy { getHostClasspaths() }
protected val hostClasspaths by lazy { HostEnvironment.classpath }

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

internal fun kotlinDependencyRegex(prefix:String): Regex {
return Regex("$prefix(-[0-9]+\\.[0-9]+(\\.[0-9]+)?)([-0-9a-zA-Z]+)?\\.jar")
}

/** Returns the files on the classloader's classpath and modulepath */
internal fun getHostClasspaths(): List<File> {
val classGraph = ClassGraph()
.enableSystemJarsAndModules()
.removeTemporaryFilesAfterScan()

val classpaths = classGraph.classpathFiles
val modules = classGraph.modules.mapNotNull { it.locationFile }

return (classpaths + modules).distinctBy(File::getAbsolutePath)
}

internal fun convertKotlinExitCode(code: ExitCode) = when(code) {
ExitCode.OK -> KotlinCompilation.ExitCode.OK
ExitCode.INTERNAL_ERROR -> KotlinCompilation.ExitCode.INTERNAL_ERROR
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.tschuchort.compiletesting

import io.github.classgraph.ClassGraph
import java.io.File

/**
* Utility object to provide everything we might discover from the host environment.
*/
internal object HostEnvironment {
val classpath by lazy {
getHostClasspaths()
}

val kotlinStdLibJar: File? by lazy {
findInClasspath(kotlinDependencyRegex("(kotlin-stdlib|kotlin-runtime)"))
}

val kotlinStdLibCommonJar: File? by lazy {
findInClasspath(kotlinDependencyRegex("kotlin-stdlib-common"))
}

val kotlinStdLibJdkJar: File? by lazy {
findInClasspath(kotlinDependencyRegex("kotlin-stdlib-jdk[0-9]+"))
}

val kotlinStdLibJsJar: File? by default {
findInClasspath(kotlinDependencyRegex("kotlin-stdlib-js"))
}

val kotlinReflectJar: File? by lazy {
findInClasspath(kotlinDependencyRegex("kotlin-reflect"))
}

val kotlinScriptRuntimeJar: File? by lazy {
findInClasspath(kotlinDependencyRegex("kotlin-script-runtime"))
}

val toolsJar: File? by lazy {
findInClasspath(Regex("tools.jar"))
}

private fun kotlinDependencyRegex(prefix: String): Regex {
return Regex("$prefix(-[0-9]+\\.[0-9]+(\\.[0-9]+)?)([-0-9a-zA-Z]+)?\\.jar")
}

/** Tries to find a file matching the given [regex] in the host process' classpath */
private fun findInClasspath(regex: Regex): File? {
val jarFile = classpath.firstOrNull { classpath ->
classpath.name.matches(regex)
//TODO("check that jar file actually contains the right classes")
}
return jarFile
}

/** Returns the files on the classloader's classpath and modulepath */
private fun getHostClasspaths(): List<File> {
val classGraph = ClassGraph()
.enableSystemJarsAndModules()
.removeTemporaryFilesAfterScan()

val classpaths = classGraph.classpathFiles
val modules = classGraph.modules.mapNotNull { it.locationFile }

return (classpaths + modules).distinctBy(File::getAbsolutePath)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,7 @@ class KotlinCompilation : AbstractKotlinCompilation<K2JVMCompilerArguments>() {
* process' classpaths
*/
var kotlinStdLibJar: File? by default {
findInHostClasspath(hostClasspaths, "kotlin-stdlib.jar",
kotlinDependencyRegex("(kotlin-stdlib|kotlin-runtime)"))
HostEnvironment.kotlinStdLibJar
}

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

/**
Expand All @@ -198,8 +196,7 @@ class KotlinCompilation : AbstractKotlinCompilation<K2JVMCompilerArguments>() {
* process' classpaths
*/
var kotlinReflectJar: File? by default {
findInHostClasspath(hostClasspaths, "kotlin-reflect.jar",
kotlinDependencyRegex("kotlin-reflect"))
HostEnvironment.kotlinReflectJar
}

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

/**
Expand All @@ -221,7 +217,7 @@ class KotlinCompilation : AbstractKotlinCompilation<K2JVMCompilerArguments>() {
var toolsJar: File? by default {
if (!isJdk9OrLater())
jdkHome?.let { findToolsJarFromJdk(it) }
?: findInHostClasspath(hostClasspaths, "tools.jar", Regex("tools.jar"))
?: HostEnvironment.toolsJar
else
null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ class KotlinJsCompilation : AbstractKotlinCompilation<K2JSCompilerArguments>() {
* process' classpaths
*/
var kotlinStdLibJsJar: File? by default {
findInHostClasspath(hostClasspaths, "kotlin-stdlib-js.jar",
kotlinDependencyRegex("kotlin-stdlib-js"))
HostEnvironment.kotlinStdLibJsJar
}

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

0 comments on commit bd6e6f2

Please sign in to comment.