@@ -21,88 +21,110 @@ import okio.buffer
2121import okio.sink
2222import kotlin.reflect.KClass
2323import org.jetbrains.kotlin.cli.common.ExitCode
24- import org.jetbrains.kotlin.cli.common.arguments.InternalArgument
2524import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
2625import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
2726import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
2827import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
2928import org.jetbrains.kotlin.config.Services
3029import java.io.*
31- import java.net.URLClassLoader
32- import java.net.URLDecoder
3330import java.util.zip.ZipEntry
3431import java.util.zip.ZipOutputStream
3532import javax.lang.model.SourceVersion
3633
3734@Suppress(" unused" , " MemberVisibilityCanBePrivate" )
38- class KotlinCompilation (
39- /* * Working directory for the compilation */
35+ open class KotlinCompilation (
36+ /* * Working directory for the compilation */
4037 val workingDir : File ,
41- /* * Arbitrary arguments to be passed to kotlinc */
42- val args : List <String > = emptyList(),
43- /* * Arbitrary arguments to be passed to kapt */
38+ /* * Free arguments to be passed to kotlinc */
39+ val freeArgs : List <String > = emptyList(),
40+ /* * Arbitrary arguments to be passed to kapt */
4441 val kaptArgs : Map <String , String > = emptyMap(),
45- /* *
42+ /* *
4643 * Paths to directories or .jar files that contain classes
4744 * to be made available in the compilation (i.e. added to
4845 * the classpath)
4946 */
5047 classpaths : List <File > = emptyList(),
51- /* * Source files to be compiled */
48+ /* * Source files to be compiled */
5249 val sources : List <SourceFile > = emptyList(),
53- /* * Services to be passed to kapt */
50+ /* * Services to be passed to kapt */
5451 val services : List <Service <* , * >> = emptyList(),
55- /* *
52+ /* *
5653 * Path to the JDK to be used
5754 *
5855 * null if no JDK is to be used (option -no-jdk)
5956 * */
6057 val jdkHome : File ? = null ,
61- /* * Search path for Kotlin runtime libraries */
62- val kotlinHome : File ? = null ,
63- /* *
64- * Don't look for kotlin-stdlib.jar, kotlin-script-runtime.jar
65- * and kotlin-reflect.jar in the [kotlinHome] directory. They
66- * need to be provided by [classpaths] or will be unavailable.
67- * */
68- val noStdLib : Boolean = kotlinHome == null ,
69- /* *
70- * Don't look for kotlin-reflect.jar in the [kotlinHome] directory.
71- * It has to be provided by [classpaths] or will be unavailable.
72- *
73- * Setting it to false has no effect when [noStdLib] is true.
58+ /* *
59+ * Path to the kotlin-stdlib.jar
60+ * If none is given, it will be searched for in the host
61+ * process' classpaths
62+ */
63+ val kotlinStdLibJar : File ? = findKtStdLib(
64+ log = { if(verbose) systemOut.log(it) }
65+ ),
66+ /* *
67+ * Path to the kotlin-stdlib-jdk*.jar
68+ * If none is given, it will be searched for in the host
69+ * process' classpaths
70+ */
71+ val kotlinStdLibJdkJar : File ? = findKtStdLibJdk(
72+ log = { if(verbose) systemOut.log(it) }
73+ ),
74+ /* *
75+ * Path to the kotlin-reflect.jar
76+ * If none is given, it will be searched for in the host
77+ * process' classpaths
78+ */
79+ val kotlinReflectJar : File ? = findKtReflect(
80+ log = { if(verbose) systemOut.log(it) }
81+ ),
82+ /* *
83+ * Path to the kotlin-script-runtime.jar
84+ * If none is given, it will be searched for in the host
85+ * process' classpaths
86+ */
87+ val kotlinScriptRuntimeJar : File ? = findKtScriptRt(
88+ log = { if(verbose) systemOut.log(it) }
89+ ),
90+ /* *
91+ * Path to the kotlin-stdlib-common.jar
92+ * If none is given, it will be searched for in the host
93+ * process' classpaths
7494 */
75- val noReflect : Boolean = noStdLib,
76- /* *
95+ val kotlinStdLibCommonJar : File ? = findKtStdLibCommon(
96+ log = { if(verbose) systemOut.log(it) }
97+ ),
98+ /* *
7799 * Path to the tools.jar file needed for kapt when using a JDK 8.
78100 *
79101 * Note: Using a tools.jar file with a JDK 9 or later leads to an
80102 * internal compiler error!
81103 */
82- val toolsJar : File ? = findToolsJarInHostClasspath (
104+ val toolsJar : File ? = findToolsInHostClasspath (
83105 log = { if(verbose) systemOut.log(it) }
84106 ),
85- /* *
107+ /* *
86108 * Path to the kotlin-annotation-processing-embeddable*.jar that
87109 * contains kapt3.
88110 *
89111 * Only needed when [services] is not empty.
90112 */
91- val kapt3Jar : File ? = findKapt3JarInHostClasspath (
113+ val kapt3Jar : File ? = findKapt3 (
92114 log = { if(verbose) systemOut.log(it) }
93115 ),
94- /* * Inherit classpath from calling process */
116+ /* * Inherit classpath from calling process */
95117 val inheritClassPath : Boolean = false ,
96- val jvmTarget : String? = null ,
97- val correctErrorTypes : Boolean = true ,
98- val skipRuntimeVersionCheck : Boolean = false ,
99- val verbose : Boolean = false ,
100- val suppressWarnings : Boolean = false ,
101- val allWarningsAsErrors : Boolean = false ,
102- val reportOutputFiles : Boolean = false ,
103- val reportPerformance : Boolean = false ,
104- val loadBuiltInsFromDependencies : Boolean = false ,
105- /* *
118+ val jvmTarget : String? = null ,
119+ val correctErrorTypes : Boolean = true ,
120+ val skipRuntimeVersionCheck : Boolean = false ,
121+ val verbose : Boolean = false ,
122+ val suppressWarnings : Boolean = false ,
123+ val allWarningsAsErrors : Boolean = false ,
124+ val reportOutputFiles : Boolean = false ,
125+ val reportPerformance : Boolean = false ,
126+ val loadBuiltInsFromDependencies : Boolean = false ,
127+ /* *
106128 * Helpful information (if [verbose] = true) and the compiler
107129 * system output will be written to this stream
108130 */
@@ -159,14 +181,17 @@ class KotlinCompilation(
159181 val allClasspaths: List <String > = mutableListOf<String >().apply {
160182 addAll(classpaths.map(File ::getAbsolutePath))
161183
184+ addAll(listOfNotNull(kotlinStdLibJar, kotlinReflectJar, kotlinScriptRuntimeJar)
185+ .map(File ::getAbsolutePath))
186+
162187 if (inheritClassPath) {
163- val hostClasspaths = getClasspaths ().map(File ::getAbsolutePath)
188+ val hostClasspaths = getHostClasspaths ().map(File ::getAbsolutePath)
164189 addAll(hostClasspaths)
165190
166191 if (verbose)
167192 systemOut.log(" Inheriting classpaths: " + hostClasspaths.joinToString(File .pathSeparator))
168193 }
169- }
194+ }.distinct()
170195
171196 /* * Returns arguments necessary to enable and configure kapt3. */
172197 private fun annotationProcessorArgs () = object {
@@ -195,66 +220,63 @@ class KotlinCompilation(
195220 )
196221 }
197222
198- /* * Runs the compilation task */
199- fun run (): Result {
200- // write given sources to working directory
201- sources.forEach { it.writeTo(sourcesDir) }
202-
203- // setup arguments for the compiler call
204- val k2jvmArgs = K2JVMCompilerArguments ().also { k2JvmArgs ->
205- k2JvmArgs.freeArgs = sourcesDir.listFiles().map { it.absolutePath } + args
206-
207- // only add kapt stuff if there are services that may use it
208- if (services.isNotEmpty()) {
209- val annotationProcArgs = annotationProcessorArgs()
210-
211- k2JvmArgs.pluginOptions = if (k2JvmArgs.pluginOptions != null )
212- k2JvmArgs.pluginOptions!! .plus(annotationProcArgs.pluginOptions)
213- else
214- annotationProcArgs.pluginOptions
223+ // setup arguments for the compiler call
224+ protected open fun parseK2JVMArgs () = K2JVMCompilerArguments ().also { k2JvmArgs ->
225+ k2JvmArgs.freeArgs = sourcesDir.listFiles().map { it.absolutePath } + freeArgs
215226
216- k2JvmArgs.pluginClasspaths = if (k2JvmArgs.pluginClasspaths != null )
217- k2JvmArgs.pluginClasspaths!! .plus(annotationProcArgs.pluginClassPaths)
218- else
219- annotationProcArgs.pluginClassPaths
220- }
221- else if (verbose) {
222- systemOut.log(" No services were given. Not including kapt in the compiler's plugins." )
223- }
224-
225- k2JvmArgs.destination = classesDir.absolutePath
226- k2JvmArgs.classpath = allClasspaths.joinToString(separator = File .pathSeparator)
227-
228- if (jdkHome != null ) {
229- k2JvmArgs.jdkHome = jdkHome.absolutePath
230- }
231- else {
232- if (verbose)
233- systemOut.log(" Using option -no-jdk. Kotlinc won't look for a JDK." )
227+ // only add kapt stuff if there are services that may use it
228+ if (services.isNotEmpty()) {
229+ val annotationProcArgs = annotationProcessorArgs()
234230
235- k2JvmArgs.noJdk = true
236- }
231+ k2JvmArgs.pluginOptions = if (k2JvmArgs.pluginOptions != null )
232+ k2JvmArgs.pluginOptions!! .plus(annotationProcArgs.pluginOptions)
233+ else
234+ annotationProcArgs.pluginOptions
237235
238- k2JvmArgs.noStdlib = noStdLib
239- k2JvmArgs.noReflect = noReflect
236+ k2JvmArgs.pluginClasspaths = if (k2JvmArgs.pluginClasspaths != null )
237+ k2JvmArgs.pluginClasspaths!! .plus(annotationProcArgs.pluginClassPaths)
238+ else
239+ annotationProcArgs.pluginClassPaths
240+ }
241+ else if (verbose) {
242+ systemOut.log(" No services were given. Not including kapt in the compiler's plugins." )
243+ }
240244
241- kotlinHome?.let { k2JvmArgs.kotlinHome = it.absolutePath }
245+ k2JvmArgs.destination = classesDir.absolutePath
246+ k2JvmArgs.classpath = allClasspaths.joinToString(separator = File .pathSeparator)
242247
243- jvmTarget?.let { k2JvmArgs.jvmTarget = it }
248+ if (jdkHome != null ) {
249+ k2JvmArgs.jdkHome = jdkHome.absolutePath
250+ }
251+ else {
252+ if (verbose)
253+ systemOut.log(" Using option -no-jdk. Kotlinc won't look for a JDK." )
244254
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
255+ k2JvmArgs.noJdk = true
256+ }
253257
258+ // the compiler should never look for stdlib or reflect in the
259+ // kotlinHome directory (which is null anyway). We will put them
260+ // in the classpath manually if they're needed
261+ k2JvmArgs.noStdlib = true
262+ k2JvmArgs.noReflect = true
263+
264+ jvmTarget?.let { k2JvmArgs.jvmTarget = it }
265+
266+ k2JvmArgs.verbose = verbose
267+ k2JvmArgs.skipRuntimeVersionCheck = skipRuntimeVersionCheck
268+ k2JvmArgs.suppressWarnings = suppressWarnings
269+ k2JvmArgs.allWarningsAsErrors = allWarningsAsErrors
270+ k2JvmArgs.reportOutputFiles = reportOutputFiles
271+ k2JvmArgs.reportPerf = reportPerformance
272+ k2JvmArgs.reportOutputFiles = reportOutputFiles
273+ k2JvmArgs.loadBuiltInsFromDependencies = loadBuiltInsFromDependencies
274+ }
254275
255- k2JvmArgs.additionalJavaModules= arrayOf()
256- k2JvmArgs.javaModulePath= " "
257- }
276+ /* * Runs the compilation task */
277+ fun run (): Result {
278+ // write given sources to working directory
279+ sources.forEach { it.writeTo(sourcesDir) }
258280
259281 val compilerSystemOutBuffer = Buffer () // Buffer for capturing compiler's logging output
260282
@@ -264,7 +286,7 @@ class KotlinCompilation(
264286 TeeOutputStream (systemOut, compilerSystemOutBuffer.outputStream())),
265287 MessageRenderer .WITHOUT_PATHS , true ),
266288 Services .EMPTY ,
267- k2jvmArgs
289+ parseK2JVMArgs()
268290 )
269291
270292 // check for known errors that are hard to debug for the user
@@ -285,7 +307,7 @@ class KotlinCompilation(
285307
286308
287309 /* *
288- * Base64 encodes a mapping of annotation processor args for kapt, as specified by
310+ * Base64 encodes a mapping of annotation processor freeArgs for kapt, as specified by
289311 * https://kotlinlang.org/docs/reference/kapt.html#apjavac-options-encoding
290312 */
291313 private fun encodeOptionsForKapt (options : Map <String , String >): String {
@@ -301,38 +323,58 @@ class KotlinCompilation(
301323 }
302324
303325 companion object {
304- /* * Tries to find the kapt 3 jar in the host classpath */
305- fun findKapt3JarInHostClasspath (log : ((String ) -> Unit )? = null): File ? {
306- val jarFile = getClasspaths().firstOrNull { classpath ->
307- classpath.name.matches(Regex (" kotlin-annotation-processing-embeddable.*?\\ .jar" ))
308- // TODO("check that jar file actually contains the right classes")
309- }
310-
311- if (jarFile == null && log != null )
312- 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 " )
315-
316- return jarFile
317- }
326+ /* * Tries to find a file matching the given [regex] in the host process' classpath */
327+ fun findInHostClasspath (shortName : String , regex : Regex , log : ((String ) -> Unit )? = null): File ? {
328+ val jarFile = getHostClasspaths().firstOrNull { classpath ->
329+ classpath.name.matches(regex)
330+ // TODO("check that jar file actually contains the right classes")
331+ }
318332
319- /* * Tries to find the tools.jar needed for kapt in the host classpath */
320- fun findToolsJarInHostClasspath (log : ((String ) -> Unit )? = null): File ? {
321- val jarFile = getClasspaths().firstOrNull { classpath ->
322- classpath.name.matches(Regex (" tools.jar" ))
323- // TODO("check that jar file actually contains the right classes")
324- }
333+ if (jarFile == null && log != null )
334+ log(" Searched classpath for $shortName but didn't find anything." )
335+ else if (log != null )
336+ log(" Searched classpath for $shortName and found: $jarFile " )
325337
326- if (jarFile == null && log != null )
327- 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 " )
338+ return jarFile
339+ }
330340
331- return jarFile
332- }
341+ /* * Tries to find the kotlin-stdlib.jar in the host process' classpath */
342+ fun findKtStdLib (log : ((String ) -> Unit )? = null)
343+ = findInHostClasspath(" kotlin-stdlib.jar" ,
344+ Regex (" kotlin-stdlib(-[0-9]+\\ .[0-9]+\\ .[0-9]+)\\ .jar" ), log)
345+
346+ /* * Tries to find the kotlin-stdlib-jdk*.jar in the host process' classpath */
347+ fun findKtStdLibJdk (log : ((String ) -> Unit )? = null)
348+ = findInHostClasspath(" kotlin-stdlib-jdk*.jar" ,
349+ Regex (" kotlin-stdlib-jdk[0-9]+(-[0-9]+\\ .[0-9]+\\ .[0-9]+)\\ .jar" ), log)
350+
351+ /* * Tries to find the kotlin-stdlib-common.jar in the host process' classpath */
352+ fun findKtStdLibCommon (log : ((String ) -> Unit )? = null)
353+ = findInHostClasspath(" kotlin-stdlib-common.jar" ,
354+ Regex (" kotlin-stdlib-common(-[0-9]+\\ .[0-9]+\\ .[0-9]+)\\ .jar" ), log)
355+
356+ /* * Tries to find the kotlin-reflect.jar in the host process' classpath */
357+ fun findKtReflect (log : ((String ) -> Unit )? = null)
358+ = findInHostClasspath(" kotlin-reflect.jar" ,
359+ Regex (" kotlin-reflect(-[0-9]+\\ .[0-9]+\\ .[0-9]+)\\ .jar" ), log)
360+
361+ /* * Tries to find the kotlin-script-runtime.jar in the host process' classpath */
362+ fun findKtScriptRt (log : ((String ) -> Unit )? = null)
363+ = findInHostClasspath(" kotlin-script-runtime.jar" ,
364+ Regex (" kotlin-script-runtime(-[0-9]+\\ .[0-9]+\\ .[0-9]+)\\ .jar" ), log)
365+
366+ /* * Tries to find the kapt 3 jar in the host process' classpath */
367+ fun findKapt3 (log : ((String ) -> Unit )? = null)
368+ = findInHostClasspath(" kotlin-annotation-processing(-embeddable).jar" ,
369+ Regex (" kotlin-annotation-processing(-embeddable)?(-[0-9]+\\ .[0-9]+\\ .[0-9]+)?\\ .jar" ), log)
370+
371+
372+ /* * Tries to find the tools.jar needed for kapt in the host process' classpath */
373+ fun findToolsInHostClasspath (log : ((String ) -> Unit )? = null)
374+ = findInHostClasspath(" tools.jar" , Regex (" tools.jar" ), log)
333375
334376 /* * Returns the files on the classloader's classpath and modulepath */
335- fun getClasspaths (): List <File > {
377+ fun getHostClasspaths (): List <File > {
336378 val classGraph = ClassGraph ()
337379 .enableSystemJarsAndModules()
338380 .removeTemporaryFilesAfterScan()
0 commit comments