1515 */
1616package com.tschuchort.compiletest
1717
18+ import io.github.classgraph.ClassGraph
1819import okio.Buffer
1920import okio.buffer
2021import okio.sink
2122import kotlin.reflect.KClass
2223import org.jetbrains.kotlin.cli.common.ExitCode
24+ import org.jetbrains.kotlin.cli.common.arguments.InternalArgument
2325import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
2426import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
2527import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
@@ -71,19 +73,13 @@ class KotlinCompilation(
7173 * Setting it to false has no effect when [noStdLib] is true.
7274 */
7375 val noReflect : Boolean = noStdLib,
74- /* *
75- * The classloader that should be used for the discovery of
76- * the host process' classpath
77- */
78- val hostClassLoader : ClassLoader = this : :class.java.classLoader,
7976 /* *
8077 * Path to the tools.jar file needed for kapt when using a JDK 8.
8178 *
8279 * Note: Using a tools.jar file with a JDK 9 or later leads to an
8380 * internal compiler error!
8481 */
8582 val toolsJar : File ? = findToolsJarInHostClasspath(
86- hostClassLoader = hostClassLoader,
8783 log = { if(verbose) systemOut.log(it) }
8884 ),
8985 /* *
@@ -93,7 +89,6 @@ class KotlinCompilation(
9389 * Only needed when [services] is not empty.
9490 */
9591 val kapt3Jar : File ? = findKapt3JarInHostClasspath(
96- hostClassLoader = hostClassLoader,
9792 log = { if(verbose) systemOut.log(it) }
9893 ),
9994 /* * Inherit classpath from calling process */
@@ -165,8 +160,11 @@ class KotlinCompilation(
165160 addAll(classpaths.map(File ::getAbsolutePath))
166161
167162 if (inheritClassPath) {
168- val hostClasspaths = getClasspaths(hostClassLoader ).map(File ::getAbsolutePath)
163+ val hostClasspaths = getClasspaths().map(File ::getAbsolutePath)
169164 addAll(hostClasspaths)
165+
166+ if (verbose)
167+ systemOut.log(" Inheriting classpaths: " + hostClasspaths.joinToString(File .pathSeparator))
170168 }
171169 }
172170
@@ -199,59 +197,66 @@ class KotlinCompilation(
199197
200198 /* * Runs the compilation task */
201199 fun run (): Result {
202- sources.forEach {
203- it.writeTo(sourcesDir)
204- }
200+ // write given sources to working directory
201+ sources.forEach { it.writeTo(sourcesDir) }
205202
206- val k2jvmArgs = K2JVMCompilerArguments ().apply {
207- freeArgs = sourcesDir.listFiles().map { it.absolutePath } + args
203+ // setup arguments for the compiler call
204+ val k2jvmArgs = K2JVMCompilerArguments ().also { k2JvmArgs ->
205+ k2JvmArgs.freeArgs = sourcesDir.listFiles().map { it.absolutePath } + args
208206
209207 // only add kapt stuff if there are services that may use it
210208 if (services.isNotEmpty()) {
211209 val annotationProcArgs = annotationProcessorArgs()
212210
213- pluginOptions = if (pluginOptions != null )
214- pluginOptions!! .plus(annotationProcArgs.pluginOptions)
211+ k2JvmArgs. pluginOptions = if (k2JvmArgs. pluginOptions != null )
212+ k2JvmArgs. pluginOptions!! .plus(annotationProcArgs.pluginOptions)
215213 else
216214 annotationProcArgs.pluginOptions
217215
218- pluginClasspaths = if (pluginClasspaths != null )
219- pluginClasspaths!! .plus(annotationProcArgs.pluginClassPaths)
216+ k2JvmArgs. pluginClasspaths = if (k2JvmArgs. pluginClasspaths != null )
217+ k2JvmArgs. pluginClasspaths!! .plus(annotationProcArgs.pluginClassPaths)
220218 else
221219 annotationProcArgs.pluginClassPaths
222220 }
223221 else if (verbose) {
224222 systemOut.log(" No services were given. Not including kapt in the compiler's plugins." )
225223 }
226224
227- destination = classesDir.absolutePath
228- classpath = allClasspaths.joinToString(separator = File .pathSeparator)
225+ k2JvmArgs. destination = classesDir.absolutePath
226+ k2JvmArgs. classpath = allClasspaths.joinToString(separator = File .pathSeparator)
229227
230- if (this @KotlinCompilation. jdkHome != null ) {
231- jdkHome = this @KotlinCompilation. jdkHome.absolutePath
228+ if (jdkHome != null ) {
229+ k2JvmArgs. jdkHome = jdkHome.absolutePath
232230 }
233231 else {
234- noJdk = true
232+ if (verbose)
233+ systemOut.log(" Using option -no-jdk. Kotlinc won't look for a JDK." )
234+
235+ k2JvmArgs.noJdk = true
235236 }
236237
237- noStdlib = this @KotlinCompilation.noStdLib
238- noReflect = this @KotlinCompilation.noReflect
238+ k2JvmArgs.noStdlib = noStdLib
239+ k2JvmArgs.noReflect = noReflect
240+
241+ kotlinHome?.let { k2JvmArgs.kotlinHome = it.absolutePath }
239242
240- this @KotlinCompilation.kotlinHome ?.let { kotlinHome = it.absolutePath }
243+ jvmTarget ?.let { k2JvmArgs.jvmTarget = it }
241244
242- this @KotlinCompilation.jvmTarget?.let { jvmTarget = it }
245+ k2JvmArgs.verbose = verbose
246+ k2JvmArgs.skipRuntimeVersionCheck = skipRuntimeVersionCheck
247+ k2JvmArgs.suppressWarnings = suppressWarnings
248+ k2JvmArgs.allWarningsAsErrors = allWarningsAsErrors
249+ k2JvmArgs.reportOutputFiles = reportOutputFiles
250+ k2JvmArgs.reportPerf = reportPerformance
251+ k2JvmArgs.reportOutputFiles = reportOutputFiles
252+ k2JvmArgs.loadBuiltInsFromDependencies = loadBuiltInsFromDependencies
243253
244- verbose = this @KotlinCompilation.verbose
245- skipRuntimeVersionCheck = this @KotlinCompilation.skipRuntimeVersionCheck
246- suppressWarnings = this @KotlinCompilation.suppressWarnings
247- allWarningsAsErrors = this @KotlinCompilation.allWarningsAsErrors
248- reportOutputFiles = this @KotlinCompilation.reportOutputFiles
249- reportPerf = this @KotlinCompilation.reportPerformance
250- reportOutputFiles = this @KotlinCompilation.reportOutputFiles
251- loadBuiltInsFromDependencies = this @KotlinCompilation.loadBuiltInsFromDependencies
254+
255+ k2JvmArgs.additionalJavaModules= arrayOf()
256+ k2JvmArgs.javaModulePath= " "
252257 }
253258
254- val compilerSystemOutBuffer = Buffer ()
259+ val compilerSystemOutBuffer = Buffer () // Buffer for capturing compiler's logging output
255260
256261 val exitCode = K2JVMCompiler ().execImpl(
257262 PrintingMessageCollector (
@@ -262,6 +267,7 @@ class KotlinCompilation(
262267 k2jvmArgs
263268 )
264269
270+ // check for known errors that are hard to debug for the user
265271 if (exitCode == ExitCode .INTERNAL_ERROR && compilerSystemOutBuffer.readUtf8()
266272 .contains(" No enum constant com.sun.tools.javac.main.Option.BOOT_CLASS_PATH" )) {
267273
@@ -296,62 +302,45 @@ class KotlinCompilation(
296302
297303 companion object {
298304 /* * Tries to find the kapt 3 jar in the host classpath */
299- fun findKapt3JarInHostClasspath (hostClassLoader : ClassLoader ,
300- log : ((String ) -> Unit )? = null): File ? {
301-
302- val jarFile = getClasspaths(hostClassLoader).firstOrNull { classpath ->
305+ fun findKapt3JarInHostClasspath (log : ((String ) -> Unit )? = null): File ? {
306+ val jarFile = getClasspaths().firstOrNull { classpath ->
303307 classpath.name.matches(Regex (" kotlin-annotation-processing-embeddable.*?\\ .jar" ))
304308 // TODO("check that jar file actually contains the right classes")
305309 }
306310
307311 if (jarFile == null && log != null )
308312 log(" Searched classpath for kotlin-annotation-processing-embeddable*.jar but didn't find anything." )
313+ else if (log != null )
314+ log(" Searched classpath for kapt3 jar and found: $jarFile " )
309315
310316 return jarFile
311317 }
312318
313319 /* * Tries to find the tools.jar needed for kapt in the host classpath */
314- fun findToolsJarInHostClasspath (hostClassLoader : ClassLoader ,
315- log : ((String ) -> Unit )? = null): File ? {
316-
317- val jarFile = getClasspaths(hostClassLoader).firstOrNull { classpath ->
320+ fun findToolsJarInHostClasspath (log : ((String ) -> Unit )? = null): File ? {
321+ val jarFile = getClasspaths().firstOrNull { classpath ->
318322 classpath.name.matches(Regex (" tools.jar" ))
319323 // TODO("check that jar file actually contains the right classes")
320324 }
321325
322326 if (jarFile == null && log != null )
323327 log(" Searched classpath for tools.jar but didn't find anything." )
328+ else if (log != null )
329+ log(" Searched classpath for tools.jar and found: $jarFile " )
324330
325331 return jarFile
326332 }
327333
328- /* * Returns the files on the classloader's classpath. */
329- fun getClasspaths (classLoader : ClassLoader ): List <File > {
330- if (classLoader is URLClassLoader ) {
331- return classLoader.urLs.map { url ->
332- if (url.protocol != " file" )
333- throw UnsupportedOperationException (" unable to handle classpath element $url " )
334+ /* * Returns the files on the classloader's classpath and modulepath */
335+ fun getClasspaths (): List <File > {
336+ val classGraph = ClassGraph ()
337+ .enableSystemJarsAndModules()
338+ .removeTemporaryFilesAfterScan()
334339
335- // paths may contain percent-encoded characters like %20 for space
336- File (URLDecoder .decode(url.path, " UTF-8" ))
337- }
338- }
339- else {
340- val jarsOrZips = classLoader.getResources(" META-INF" ).toList().mapNotNull {
341- if (it.path.matches(Regex (" file:/.*?(\\ .jar|\\ .zip)!/META-INF" )))
342- it.path.removeSurrounding(" file:/" , " !/META-INF" )
343- else
344- null
345- }
346-
347- val dirs = classLoader.getResources(" " ).toList().map { it.path }
348- val wildcards = classLoader.getResources(" *" ).toList().map { it.path }
349-
350- return (jarsOrZips + dirs + wildcards).map {
351- // paths may contain percent-encoded characters like %20 for space
352- File (URLDecoder .decode(it, " UTF-8" ))
353- }
354- }
340+ val classpaths = classGraph.classpathFiles
341+ val modules = classGraph.modules.mapNotNull { it.locationFile }
342+
343+ return (classpaths + modules).distinctBy(File ::getAbsolutePath)
355344 }
356345
357346 /* * Finds the tools.jar given a path to a JDK 8 or earlier */
0 commit comments