@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
2727import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
2828import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
2929import org.jetbrains.kotlin.config.Services
30+ import org.jetbrains.kotlin.incremental.isJavaFile
3031import java.io.*
3132import java.lang.RuntimeException
3233import java.util.zip.ZipEntry
@@ -156,20 +157,23 @@ data class KotlinCompilation(
156157 // Directory for input source files
157158 private val sourcesDir get() = File (workingDir, " sources" )
158159
160+ // Base directory for kapt stuff
161+ private val kaptDir get() = File (workingDir, " kapt" )
162+
159163 // *.class files, Jars and resources (non-temporary) that are created by the
160164 // compilation will land here
161165 val classesDir get() = File (workingDir, " classes" )
162166
163167 // Java annotation processors that are compile by kapt will put their generated files here
164- val kaptSourceDir get() = File (workingDir , " kapt/ sources" )
168+ val kaptSourceDir get() = File (kaptDir , " sources" )
165169
166170 // Output directory for Kotlin source files generated by kapt
167171 val kaptKotlinGeneratedDir get() = kaptArgs[OPTION_KAPT_KOTLIN_GENERATED ]
168172 ?.let { path ->
169173 require(File (path).isDirectory) { " $OPTION_KAPT_KOTLIN_GENERATED must be a directory" }
170174 File (path)
171175 }
172- ? : File (workingDir , " kapt/ kotlinGenerated" )
176+ ? : File (kaptDir , " kotlinGenerated" )
173177
174178
175179 /* *
@@ -258,8 +262,9 @@ data class KotlinCompilation(
258262 /* * Performs the 1st and 2nd compilation step to generate stubs and run annotation processors */
259263 private fun stubsAndApt (messageCollector : PrintingMessageCollector ): ExitCode {
260264 // stubs and incrementalData are temporary
261- val kaptStubsDir = File (workingDir, " kapt/stubs" ).apply { mkdirs() }
262- val kaptIncrementalDataDir = File (workingDir, " kapt/incrementalData" ).apply { mkdirs() }
265+ val kaptStubsDir = File (kaptDir, " stubs" ).apply { mkdirs() }
266+ val kaptIncrementalDataDir = File (kaptDir, " incrementalData" ).apply { mkdirs() }
267+ kaptKotlinGeneratedDir.mkdirs()
263268
264269 require(kapt3Jar != null ) { " kapt3Jar has to be non-null if annotation processing is used" }
265270
@@ -280,19 +285,38 @@ data class KotlinCompilation(
280285 // write unsafe context
281286 " plugin:org.jetbrains.kotlin.kapt3:aptMode=stubsAndApt" ,
282287 " plugin:org.jetbrains.kotlin.kapt3:mapDiagnosticLocations=true" ,
288+ " plugin:org.jetbrains.kotlin.kapt3:apoptions=$encodedKaptArgs " ,
283289 * if (verbose)
284290 arrayOf(" plugin:org.jetbrains.kotlin.kapt3:verbose=true" )
285- else
286- emptyArray(),
287- * if (kaptArgs.isNotEmpty())
288- arrayOf(" plugin:org.jetbrains.kotlin.kapt3:apoptions=$encodedKaptArgs " )
289291 else
290292 emptyArray()
291293 )
294+
295+ val kotlinSources = sourcesDir.listFilesRecursively().filter(File ::isKotlinFile)
296+ val javaSources = sourcesDir.listFilesRecursively().filter(File ::isJavaFile)
297+
298+ val allSourcePaths = mutableListOf<File >().apply {
299+ addAll(javaSources)
300+
301+ if (kotlinSources.isNotEmpty()) {
302+ addAll(kotlinSources)
303+ }
304+ else {
305+ /* __HACK__: The K2JVMCompiler expects at least one Kotlin source file or it will crash.
306+ We still need kapt to run even if there are no Kotlin sources because it executes APs
307+ on Java sources as well. Alternatively we could call the JavaCompiler instead of kapt
308+ to do annotation processing when there are only Java sources, but that's quite a lot
309+ of work (It can not be done in the compileJava step because annotation processors on
310+ Java files might generate Kotlin files which then need to be compiled in the
311+ compileKotlin step before the compileJava step). So instead we trick K2JVMCompiler
312+ by just including an empty .kt-File. */
313+ add(SourceFile (" emptyKotlinFile.kt" , " " ).writeTo(kaptDir))
314+ }
315+
316+ } .map(File ::getAbsolutePath).distinct()
292317
293318 val k2JvmArgs = commonK2JVMArgs().also {
294- it.freeArgs = sourcesDir.listFilesRecursively()
295- .map(File ::getAbsolutePath).distinct() + addKotlincArgs
319+ it.freeArgs = allSourcePaths + addKotlincArgs
296320
297321 it.pluginOptions = if (it.pluginOptions != null )
298322 it.pluginOptions!! .plus(kaptPluginOptions)
@@ -315,6 +339,7 @@ data class KotlinCompilation(
315339 kaptKotlinGeneratedDir.listFilesRecursively() +
316340 kaptSourceDir.listFilesRecursively())
317341
342+ // if no Kotlin sources are available, skip the compileKotlin step
318343 if (sources.filter<File >(File ::isKotlinFile).isEmpty())
319344 return ExitCode .OK
320345
0 commit comments