From 95d12103171315bc2cb71d1a51a564234f0b5477 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Thu, 3 Nov 2016 18:15:38 +0300 Subject: [PATCH] Kapt3: Extract annotation processing to its own task in Gradle. Now the kotlinCompile task should know nothing about kapt, for the main task it's just a regular Java source root. --- .../main/kotlin/example/ExampleSubplugin.kt | 2 +- .../gradle/plugin/KotlinGradleSubplugin.kt | 9 +- .../gradle/internal/AndroidSubplugin.kt | 2 +- .../internal/AnnotationProcessingManager.kt | 2 +- .../internal/Kapt3KotlinGradleSubplugin.kt | 55 ++++-- .../kotlin/gradle/internal/KaptTask.kt | 65 +++++++ .../kotlin/gradle/plugin/KotlinPlugin.kt | 95 +++++---- .../kotlin/gradle/tasks/SourceRoots.kt | 88 +++++++++ .../jetbrains/kotlin/gradle/tasks/Tasks.kt | 180 +++++++----------- .../IncrementalJvmCompilerRunner.kt | 17 -- .../org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt | 2 +- 11 files changed, 336 insertions(+), 181 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptTask.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/SourceRoots.kt diff --git a/libraries/examples/kotlin-gradle-subplugin-example/src/main/kotlin/example/ExampleSubplugin.kt b/libraries/examples/kotlin-gradle-subplugin-example/src/main/kotlin/example/ExampleSubplugin.kt index 6b7b8bb2383cc..742f93ef0b311 100644 --- a/libraries/examples/kotlin-gradle-subplugin-example/src/main/kotlin/example/ExampleSubplugin.kt +++ b/libraries/examples/kotlin-gradle-subplugin-example/src/main/kotlin/example/ExampleSubplugin.kt @@ -39,7 +39,7 @@ class ExampleSubplugin : KotlinGradleSubplugin { return listOf(SubpluginOption("exampleKey", "exampleValue")) } - override fun getPluginName(): String { + override fun getCompilerPluginId(): String { return "example.plugin" } diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleSubplugin.kt b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleSubplugin.kt index a9fc4224f4e6a..bab262875c158 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleSubplugin.kt @@ -32,8 +32,13 @@ interface KotlinGradleSubplugin { variantData: Any?, javaSourceSet: SourceSet? ): List - - fun getPluginName(): String + + fun getSubpluginKotlinTasks( + project: Project, + kotlinCompile: KotlinCompile + ): List = emptyList() + + fun getCompilerPluginId(): String fun getGroupName(): String fun getArtifactName(): String } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt index a1e4e445bf3b5..fbef2e47da2b2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt @@ -105,7 +105,7 @@ class AndroidSubplugin : KotlinGradleSubplugin { } } - override fun getPluginName() = "org.jetbrains.kotlin.android" + override fun getCompilerPluginId() = "org.jetbrains.kotlin.android" override fun getGroupName() = "org.jetbrains.kotlin" diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AnnotationProcessingManager.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AnnotationProcessingManager.kt index 9395f19e67692..d8f5d735f2d8b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AnnotationProcessingManager.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AnnotationProcessingManager.kt @@ -63,7 +63,7 @@ internal fun Project.initKapt( kotlinAfterJavaTask.source(kaptManager.generatedKotlinSourceDir) kotlinAfterJavaTask.source(kaptManager.aptOutputDir) - subpluginEnvironment.addSubpluginArguments(this, kotlinAfterJavaTask, javaTask, null, null) + subpluginEnvironment.addSubpluginOptions(this, kotlinAfterJavaTask, javaTask, null, null) javaTask.doLast { moveGeneratedJavaFilesToCorrespondingDirectories(kaptManager.aptOutputDir) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt3KotlinGradleSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt3KotlinGradleSubplugin.kt index 498bfb878ce92..9ba665e76627d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt3KotlinGradleSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt3KotlinGradleSubplugin.kt @@ -26,10 +26,7 @@ import org.gradle.api.artifacts.Configuration import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.JavaCompile -import org.jetbrains.kotlin.annotation.SourceAnnotationsRegistry -import org.jetbrains.kotlin.gradle.plugin.KaptExtension -import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin -import org.jetbrains.kotlin.gradle.plugin.SubpluginOption +import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import java.io.File @@ -51,6 +48,8 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { return if (sourceSetName != "main") "kapt${sourceSetName.capitalize()}" else "kapt" } } + + private val kotlinToKaptTasksMap = mutableMapOf() override fun isApplicable(project: Project, task: KotlinCompile) = Kapt3GradleSubplugin.isEnabled(project) @@ -73,7 +72,6 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { val kotlinCompile: KotlinCompile, val javaCompile: AbstractCompile, val variantData: Any?, - val javaSourceSet: SourceSet?, val sourceSetName: String, val kaptExtension: KaptExtension, val kaptClasspath: MutableList) @@ -114,9 +112,16 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { val kaptExtension = project.extensions.getByType(KaptExtension::class.java) val context = Kapt3SubpluginContext(project, kotlinCompile, javaCompile, - variantData, javaSourceSet, sourceSetName, kaptExtension, kaptClasspath) + variantData, sourceSetName, kaptExtension, kaptClasspath) + + context.createKaptKotlinTask() + + /** Plugin options are applied to kapt*Compile inside [createKaptKotlinTask] */ + return emptyList() + } - return context.buildOptions() + override fun getSubpluginKotlinTasks(project: Project, kotlinCompile: KotlinCompile): List { + return kotlinToKaptTasksMap[kotlinCompile]?.let { listOf(it) } ?: emptyList() } // This method should be called no more than once for each Kapt3SubpluginContext @@ -146,9 +151,6 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { pluginOptions += SubpluginOption("apoption", "$key:$value") } - val annotationsFile = File(kotlinCompile.taskBuildDirectory, "source-annotations.txt") - kotlinCompile.sourceAnnotationsRegistry = SourceAnnotationsRegistry(annotationsFile) - addMiscOptions(pluginOptions) return pluginOptions @@ -159,12 +161,43 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { project.logger.warn("'kapt.generateStubs' is not used by the 'kotlin-kapt' plugin") } + pluginOptions += SubpluginOption("aptOnly", "true") + if (project.hasProperty(VERBOSE_OPTION_NAME) && project.property(VERBOSE_OPTION_NAME) == "true") { pluginOptions += SubpluginOption("verbose", "true") pluginOptions += SubpluginOption("stubs", getKaptStubsDir(project, sourceSetName).canonicalPath) } } + private fun Kapt3SubpluginContext.createKaptKotlinTask() { + val sourcesOutputDir = getKaptGeneratedDir(project, sourceSetName) + + // Replace compile*Kotlin to kapt*Kotlin + assert(kotlinCompile.name.startsWith("compile")) + val kaptTaskName = kotlinCompile.name.replaceFirst("compile", "kapt") + val kaptTask = project.tasks.create(kaptTaskName, KaptTask::class.java) + kaptTask.kotlinCompileTask = kotlinCompile + kotlinToKaptTasksMap[kotlinCompile] = kaptTask + + project.resolveSubpluginArtifacts(listOf(this@Kapt3KotlinGradleSubplugin)).flatMap { it.value }.forEach { + kaptTask.pluginOptions.addClasspathEntry(it) + } + + kaptTask.mapClasspath { kotlinCompile.classpath } + kaptTask.destinationDir = sourcesOutputDir + kotlinCompile.dependsOn(kaptTask) + + // Add generated source dir as a source root for kotlinCompile and javaCompile + kotlinCompile.source(sourcesOutputDir) + javaCompile.source(sourcesOutputDir) + + val pluginOptions = kaptTask.pluginOptions + val compilerPluginId = getCompilerPluginId() + for (option in buildOptions()) { + pluginOptions.addPluginArgument(compilerPluginId, option.key, option.value) + } + } + private fun Kapt3SubpluginContext.disableAnnotationProcessingInJavaTask() { (javaCompile as? JavaCompile)?.let { javaCompile -> val options = javaCompile.options @@ -175,7 +208,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { private val BaseVariantData<*>.sourceProviders: List get() = variantConfiguration.sortedSourceProviders - override fun getPluginName() = "org.jetbrains.kotlin.kapt3" + override fun getCompilerPluginId() = "org.jetbrains.kotlin.kapt3" override fun getGroupName() = "org.jetbrains.kotlin" override fun getArtifactName() = "kotlin-annotation-processing" } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptTask.kt new file mode 100644 index 0000000000000..b3ea808aad992 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptTask.kt @@ -0,0 +1,65 @@ +package org.jetbrains.kotlin.gradle.internal + +import org.gradle.api.GradleException +import org.gradle.api.tasks.SourceTask +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.compile.AbstractCompile +import org.jetbrains.kotlin.cli.common.ExitCode +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments +import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler +import org.jetbrains.kotlin.com.intellij.openapi.util.io.FileUtil +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl +import org.jetbrains.kotlin.gradle.dsl.fillDefaultValues +import org.jetbrains.kotlin.gradle.tasks.* +import java.io.File + +open class KaptTask : AbstractCompile() { + private val rawSourceRoots = FilteringSourceRootsContainer({ !it.isInsideDestinationDir() }) + private val args = K2JVMCompilerArguments().apply { fillDefaultValues() } + + internal val pluginOptions = CompilerPluginOptions() + internal lateinit var kotlinCompileTask: KotlinCompile + + override fun setSource(sources: Any?) { + val filteredSources = rawSourceRoots.set(sources) + super.setSource(filteredSources) + } + + override fun source(vararg sources: Any?): SourceTask? { + val filteredSources = rawSourceRoots.add(*sources) + return super.source(filteredSources) + } + + private fun File.isInsideDestinationDir(): Boolean { + return FileUtil.isAncestor(destinationDir, this, /* strict = */ false) + } + + private fun processCompilerExitCode(exitCode: ExitCode) { + when (exitCode) { + ExitCode.COMPILATION_ERROR -> throw GradleException("Annotation processing error. See log for more details") + ExitCode.INTERNAL_ERROR -> throw GradleException("Annotation processing internal error. See log for more details") + else -> {} + } + } + + @TaskAction + override fun compile() { + /** Delete everything inside the [destinationDir] */ + destinationDir.deleteRecursively() + destinationDir.mkdirs() + + val compiler = K2JVMCompiler() + val sourceRoots = SourceRoots.ForJvm.create(getSource(), rawSourceRoots) + val compileClasspath = classpath.filter(File::exists) + + args.moduleName = kotlinCompileTask.moduleName + args.pluginClasspaths = pluginOptions.classpath.toTypedArray() + args.pluginOptions = pluginOptions.arguments.toTypedArray() + kotlinCompileTask.parentKotlinOptionsImpl?.updateArguments(args) + KotlinJvmOptionsImpl().updateArguments(args) + + processCompilerExitCode(compileJvmNotIncrementally(compiler, logger, + sourceRoots.kotlinSourceFiles, sourceRoots.javaSourceRoots, compileClasspath, + destinationDir, args)) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt index e91a5a6a519a3..2cc96d4329cbf 100755 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt @@ -118,8 +118,8 @@ internal class Kotlin2JvmSourceSetProcessor( val javaTask = project.tasks.findByName(sourceSet.compileJavaTaskName) val subpluginEnvironment = loadSubplugins(project) - subpluginEnvironment.addSubpluginArguments(project, kotlinTask, - javaTask as JavaCompile, null, sourceSet) + val appliedPlugins = subpluginEnvironment.addSubpluginOptions( + project, kotlinTask, javaTask as JavaCompile, null, sourceSet) var kotlinAfterJavaTask: KotlinCompile? = null @@ -135,10 +135,15 @@ internal class Kotlin2JvmSourceSetProcessor( } sourceSet.java.srcDirs.forEach { kotlinSourceSet.kotlin.srcDir(it) } + // KotlinCompile.source(kotlinDirSet) should be called only after all java roots are added to kotlinDirSet // otherwise some java roots can be ignored kotlinTask.source(kotlinSourceSet.kotlin) - kotlinAfterJavaTask?.let { it.source(kotlinSourceSet.kotlin) } + kotlinAfterJavaTask?.source(kotlinSourceSet.kotlin) + appliedPlugins + .flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) } + .forEach { it.source(kotlinSourceSet.kotlin) } + configureJavaTask(kotlinTask, javaTask, logger) createSyncOutputTask(project, kotlinTask, javaTask, kotlinAfterJavaTask, sourceSetName) val artifactFile = project.tryGetSingleArtifact() @@ -355,7 +360,8 @@ internal open class KotlinAndroidPlugin( } } - subpluginEnvironment.addSubpluginArguments(project, kotlinTask, javaTask, variantData, null) + val appliedPlugins = subpluginEnvironment.addSubpluginOptions( + project, kotlinTask, javaTask, variantData, null) kotlinTask.mapClasspath { javaTask.classpath + project.files(AndroidGradleWrapper.getRuntimeJars(androidPlugin, androidExt)) @@ -379,6 +385,9 @@ internal open class KotlinAndroidPlugin( if (kotlinAfterJavaTask != null) { configureSources(kotlinAfterJavaTask, variantData) } + appliedPlugins + .flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) } + .forEach { configureSources(it, variantData) } configureJavaTask(kotlinTask, javaTask, logger) createSyncOutputTask(project, kotlinTask, javaTask, kotlinAfterJavaTask, variantDataName) @@ -393,17 +402,17 @@ internal open class KotlinAndroidPlugin( } } - private fun configureSources(kotlinTask: KotlinCompile, variantData: BaseVariantData) { - val logger = kotlinTask.project.logger + private fun configureSources(compileTask: AbstractCompile, variantData: BaseVariantData) { + val logger = compileTask.project.logger for (provider in variantData.sourceProviders) { val kotlinSourceSet = provider.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet ?: continue - kotlinTask.source(kotlinSourceSet.kotlin) + compileTask.source(kotlinSourceSet.kotlin) } for (javaSrcDir in AndroidGradleWrapper.getJavaSources(variantData)) { - kotlinTask.source(javaSrcDir) - logger.kotlinDebug("Source directory $javaSrcDir was added to kotlin source for ${kotlinTask.name}") + compileTask.source(javaSrcDir) + logger.kotlinDebug("Source directory $javaSrcDir was added to kotlin source for ${compileTask.name}") } } @@ -482,29 +491,7 @@ private fun loadSubplugins(project: Project): SubpluginEnvironment { val subplugins = ServiceLoader.load(KotlinGradleSubplugin::class.java, project.buildscript.classLoader) .map { @Suppress("UNCHECKED_CAST") (it as KotlinGradleSubplugin) } - fun Project.getResolvedArtifacts() = buildscript.configurations.getByName("classpath") - .resolvedConfiguration.resolvedArtifacts - - val resolvedClasspathArtifacts = project.getResolvedArtifacts().toMutableList() - val rootProject = project.rootProject - if (rootProject != project) { - resolvedClasspathArtifacts += rootProject.getResolvedArtifacts() - } - - val subpluginClasspaths = hashMapOf, List>() - - for (subplugin in subplugins) { - val file = resolvedClasspathArtifacts - .firstOrNull { - val id = it.moduleVersion.id - subplugin.getGroupName() == id.group && subplugin.getArtifactName() == id.name - }?.file - if (file != null) { - subpluginClasspaths.put(subplugin, listOf(file)) - } - } - - return SubpluginEnvironment(subpluginClasspaths, subplugins) + return SubpluginEnvironment(project.resolveSubpluginArtifacts(subplugins), subplugins) } catch (e: NoClassDefFoundError) { // Skip plugin loading if KotlinGradleSubplugin is not defined. // It is true now for tests in kotlin-gradle-plugin-core. @@ -512,33 +499,65 @@ private fun loadSubplugins(project: Project): SubpluginEnvironment { } } +internal fun Project.resolveSubpluginArtifacts( + subplugins: List> +): Map, List> { + fun Project.getResolvedArtifacts() = buildscript.configurations.getByName("classpath") + .resolvedConfiguration.resolvedArtifacts + + val resolvedClasspathArtifacts = getResolvedArtifacts().toMutableList() + val rootProject = rootProject + if (rootProject != this) { + resolvedClasspathArtifacts += rootProject.getResolvedArtifacts() + } + + val subpluginClasspaths = hashMapOf, List>() + + for (subplugin in subplugins) { + val file = resolvedClasspathArtifacts + .firstOrNull { + val id = it.moduleVersion.id + subplugin.getGroupName() == id.group && subplugin.getArtifactName() == id.name + }?.file + if (file != null) { + subpluginClasspaths.put(subplugin, listOf(file)) + } + } + + return subpluginClasspaths +} + internal class SubpluginEnvironment( val subpluginClasspaths: Map, List>, val subplugins: List> ) { - fun addSubpluginArguments( + fun addSubpluginOptions( project: Project, kotlinTask: KotlinCompile, javaTask: AbstractCompile, variantData: Any?, - javaSourceSet: SourceSet?) { + javaSourceSet: SourceSet? + ): List> { val pluginOptions = kotlinTask.pluginOptions - for (subplugin in subplugins) { + val appliedSubplugins = subplugins.filter { it.isApplicable(project, kotlinTask) } + for (subplugin in appliedSubplugins) { if (!subplugin.isApplicable(project, kotlinTask)) continue with(subplugin) { - project.logger.kotlinDebug("Subplugin ${getPluginName()} (${getGroupName()}:${getArtifactName()}) loaded.") + project.logger.kotlinDebug("Subplugin ${getCompilerPluginId()} (${getGroupName()}:${getArtifactName()}) loaded.") } val subpluginClasspath = subpluginClasspaths[subplugin] ?: continue subpluginClasspath.forEach { pluginOptions.addClasspathEntry(it) } - for (arg in subplugin.apply(project, kotlinTask, javaTask, variantData, javaSourceSet)) { - pluginOptions.addPluginArgument(subplugin.getPluginName(), arg.key, arg.value) + for (option in subplugin.apply(project, kotlinTask, javaTask, variantData, javaSourceSet)) { + pluginOptions.addPluginArgument(subplugin.getCompilerPluginId(), option.key, option.value) } } + + return appliedSubplugins } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/SourceRoots.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/SourceRoots.kt new file mode 100644 index 0000000000000..bb91c9c045c1d --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/SourceRoots.kt @@ -0,0 +1,88 @@ +package org.jetbrains.kotlin.gradle.tasks + +import org.gradle.api.file.FileTree +import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.logging.Logger +import org.jetbrains.kotlin.com.intellij.openapi.util.io.FileUtil +import org.jetbrains.kotlin.gradle.plugin.kotlinDebug +import org.jetbrains.kotlin.incremental.isJavaFile +import org.jetbrains.kotlin.incremental.isKotlinFile +import java.io.File +import java.util.* + +internal sealed class SourceRoots(val kotlinSourceFiles: List) { + private companion object { + fun dumpPaths(files: Iterable): String = + "[${files.map { it.canonicalPath }.sorted().joinToString(prefix = "\n\t", separator = ",\n\t")}]" + } + + open fun log(taskName: String, logger: Logger) { + logger.kotlinDebug { "$taskName source roots: ${dumpPaths(kotlinSourceFiles)}" } + } + + class ForJvm(kotlinSourceFiles: List, val javaSourceRoots: Set) : SourceRoots(kotlinSourceFiles) { + companion object { + fun create(taskSource: FileTree, sourceRoots: FilteringSourceRootsContainer): ForJvm { + val kotlinSourceFiles = (taskSource as Iterable).filter(File::isKotlinFile) + val javaSourceRoots = findRootsForSources( + sourceRoots.sourceRoots, taskSource.filter(File::isJavaFile)) + return ForJvm(kotlinSourceFiles, javaSourceRoots) + } + + private fun findRootsForSources(allSourceRoots: Iterable, sources: Iterable): Set { + val resultRoots = HashSet() + val sourceDirs = sources.mapTo(HashSet()) { it.parentFile } + + for (sourceDir in sourceDirs) { + for (sourceRoot in allSourceRoots) { + if (FileUtil.isAncestor(sourceRoot, sourceDir, /* strict = */false)) { + resultRoots.add(sourceRoot) + } + } + } + + return resultRoots + } + } + + override fun log(taskName: String, logger: Logger) { + super.log(taskName, logger) + logger.kotlinDebug { "$taskName java source roots: ${dumpPaths(javaSourceRoots)}" } + } + } + + class ForJs(kotlinSourceFiles: List) : SourceRoots(kotlinSourceFiles) { + companion object { + fun create(taskSource: FileTree) = ForJs((taskSource as Iterable).filter(File::isKotlinFile)) + } + } +} + +internal class FilteringSourceRootsContainer(val filter: (File) -> Boolean = { true }) { + private val mutableSourceRoots = mutableListOf() + + val sourceRoots: List + get() = mutableSourceRoots + + fun clear() { + mutableSourceRoots.clear() + } + + fun set(source: Any?): List { + clear() + return add(source) + } + + fun add(vararg sources: Any?): List { + val filteredDirs = mutableListOf() + for (source in sources) { + when (source) { + is SourceDirectorySet -> filteredDirs += source.srcDirs.filter { filter(it) } + is File -> if (filter(source)) filteredDirs.add(source) + } + } + + mutableSourceRoots += filteredDirs + return filteredDirs + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt index 59a0edee8404d..292d345914991 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.gradle.tasks import org.codehaus.groovy.runtime.MethodClosure import org.gradle.api.GradleException import org.gradle.api.Task +import org.gradle.api.file.FileTree import org.gradle.api.file.SourceDirectorySet import org.gradle.api.logging.Logger import org.gradle.api.tasks.SourceTask @@ -28,6 +29,7 @@ import org.gradle.api.tasks.incremental.IncrementalTaskInputs import org.gradle.util.GradleVersion import org.jetbrains.kotlin.annotation.AnnotationFileUpdater import org.jetbrains.kotlin.annotation.SourceAnnotationsRegistry +import org.jetbrains.kotlin.build.JvmSourceRoot import org.jetbrains.kotlin.cli.common.CLICompiler import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments @@ -38,7 +40,6 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.js.K2JSCompiler import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler -import org.jetbrains.kotlin.com.intellij.openapi.util.io.FileUtil import org.jetbrains.kotlin.compilerRunner.ArgumentUtils import org.jetbrains.kotlin.config.Services import org.jetbrains.kotlin.gradle.dsl.* @@ -46,7 +47,6 @@ import org.jetbrains.kotlin.gradle.plugin.kotlinDebug import org.jetbrains.kotlin.gradle.plugin.kotlinInfo import org.jetbrains.kotlin.gradle.utils.ParsedGradleVersion import org.jetbrains.kotlin.incremental.* -import org.jetbrains.kotlin.incremental.components.SourceRetentionAnnotationHandler import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifferenceRegistryProvider import org.jetbrains.kotlin.utils.LibraryUtils import java.io.File @@ -79,7 +79,7 @@ abstract class AbstractKotlinCompile() : AbstractCo internal var friendTaskName: String? = null internal var javaOutputDir: File? = null internal var sourceSetName: String by Delegates.notNull() - protected val moduleName: String + internal val moduleName: String get() = "${project.name}_$sourceSetName" override fun compile() { @@ -88,7 +88,9 @@ abstract class AbstractKotlinCompile() : AbstractCo @TaskAction fun execute(inputs: IncrementalTaskInputs): Unit { - val allKotlinSources = getKotlinSources() + val sourceRoots = getSourceRoots() + val allKotlinSources = sourceRoots.kotlinSourceFiles + logger.kotlinDebug { "all kotlin sources: ${allKotlinSources.pathsAsStringRelativeTo(project.rootProject.projectDir)}" } if (allKotlinSources.isEmpty()) { @@ -96,14 +98,14 @@ abstract class AbstractKotlinCompile() : AbstractCo return } + sourceRoots.log(this.name, logger) val args = populateCompilerArguments() compilerCalled = true - callCompiler(args, allKotlinSources, ChangedFiles(inputs)) + callCompiler(args, sourceRoots, ChangedFiles(inputs)) } - private fun getKotlinSources(): List = (getSource() as Iterable).filter(File::isKotlinFile) - - internal abstract fun callCompiler(args: T, allKotlinSources: List, changedFiles: ChangedFiles) + internal abstract fun getSourceRoots(): SourceRoots + internal abstract fun callCompiler(args: T, sourceRoots: SourceRoots, changedFiles: ChangedFiles) } open class KotlinCompile : AbstractKotlinCompile(), KotlinJvmCompile { @@ -113,8 +115,7 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl private val kotlinOptionsImpl = KotlinJvmOptionsImpl() override val kotlinOptions: KotlinJvmOptions get() = kotlinOptionsImpl - - private val sourceRoots = HashSet() + internal val sourceRootsContainer = FilteringSourceRootsContainer() internal val taskBuildDirectory: File get() = File(File(project.buildDir, KOTLIN_BUILD_DIR_NAME), name).apply { mkdirs() } @@ -136,9 +137,6 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl private val compileClasspath: Iterable get() = (classpath + additionalClasspath) .filterTo(LinkedHashSet(), File::exists) - private val kapt2GeneratedSourcesDir: File - get() = File(project.buildDir, "generated/source/kapt2") - internal val kaptOptions = KaptOptions() internal val pluginOptions = CompilerPluginOptions() @@ -171,21 +169,30 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl parentKotlinOptionsImpl?.updateArguments(args) kotlinOptionsImpl.updateArguments(args) - fun dumpPaths(files: Iterable): String = - "[${files.map { it.canonicalPath }.sorted().joinToString(prefix = "\n\t", separator = ",\n\t")}]" - logger.kotlinDebug { "$name destinationDir = $destinationDir" } - logger.kotlinDebug { "$name source roots: ${dumpPaths(sourceRoots)}" } - logger.kotlinDebug { "$name java source roots: ${dumpPaths(getJavaSourceRoots())}" } return args } - override fun callCompiler(args: K2JVMCompilerArguments, allKotlinSources: List, changedFiles: ChangedFiles) { + internal fun addFriendPathForTestTask(friendKotlinTaskName: String, args: K2JVMCompilerArguments) { + val friendTask = project.getTasksByName(friendKotlinTaskName, /* recursive = */false).firstOrNull() as? KotlinCompile ?: return + args.friendPaths = arrayOf(friendTask.javaOutputDir!!.absolutePath) + args.moduleName = friendTask.moduleName + logger.kotlinDebug("java destination directory for production = ${friendTask.javaOutputDir}") + } + + override fun getSourceRoots() = SourceRoots.ForJvm.create(getSource(), sourceRootsContainer) + + override fun callCompiler(args: K2JVMCompilerArguments, sourceRoots: SourceRoots, changedFiles: ChangedFiles) { + sourceRoots as SourceRoots.ForJvm + val outputDir = destinationDir if (!incremental) { anyClassesCompiled = true - processCompilerExitCode(compileNotIncremental(allKotlinSources, outputDir, args)) + processCompilerExitCode(compileJvmNotIncrementally( + compiler, logger, + sourceRoots.kotlinSourceFiles, sourceRoots.javaSourceRoots, + compileClasspath, outputDir, args)) return } @@ -194,19 +201,18 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl val messageCollector = GradleMessageCollector(logger) val compiler = IncrementalJvmCompilerRunner( taskBuildDirectory, - getJavaSourceRoots(), + sourceRoots.javaSourceRoots, cacheVersions, reporter, kaptAnnotationsFileUpdater, sourceAnnotationsRegistry, - kapt2GeneratedSourcesDir, artifactDifferenceRegistryProvider, artifactFile ) args.classpathAsList = classpath.toList() args.destinationAsFile = destinationDir try { - val exitCode = compiler.compile(allKotlinSources, args, messageCollector, { changedFiles }) + val exitCode = compiler.compile(sourceRoots.kotlinSourceFiles, args, messageCollector, { changedFiles }) processCompilerExitCode(exitCode) } catch (e: Throwable) { @@ -236,45 +242,6 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl } } - private fun compileNotIncremental( - sourcesToCompile: List, - outputDir: File, - args: K2JVMCompilerArguments - ): ExitCode { - logger.kotlinDebug("Removing all kotlin classes in $outputDir") - // we're free to delete all classes since only we know about that directory - // todo: can be optimized -- compile and remove only files that were not generated - listClassFiles(outputDir.canonicalPath).forEach { it.delete() } - - val moduleFile = makeModuleFile( - args.moduleName, - isTest = false, - outputDir = outputDir, - sourcesToCompile = sourcesToCompile, - javaSourceRoots = getJavaSourceRoots(), - classpath = compileClasspath, - friendDirs = listOf()) - args.module = moduleFile.absolutePath - val messageCollector = GradleMessageCollector(logger) - - sourceAnnotationsRegistry?.clear() - val services = with (Services.Builder()) { - sourceAnnotationsRegistry?.let { handler -> - register(SourceRetentionAnnotationHandler::class.java, handler) - } - build() - } - - try { - logger.kotlinDebug("compiling with args: ${ArgumentUtils.convertArgumentsToStringList(args)}") - logger.kotlinDebug("compiling with classpath: ${compileClasspath.toList().sorted().joinToString()}") - return compiler.exec(messageCollector, services, args) - } - finally { - moduleFile.delete() - } - } - private fun handleKaptProperties() { kaptOptions.annotationsFile?.let { kaptAnnotationsFile -> if (incremental) { @@ -293,61 +260,51 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl } } - private fun getJavaSourceRoots(): Set = - findRootsForSources(getSource().filter { it.isJavaFile() }) - - private fun File.isKapt2GeneratedDirectory(): Boolean { - if (!kapt2GeneratedSourcesDir.isDirectory) return false - return FileUtil.isAncestor(kapt2GeneratedSourcesDir, this, /* strict = */ false) - } - - private fun filterOutKapt2Directories(vararg sources: Any?): Array { - return sources.flatMap { source -> - when (source) { - is File -> if (source.isKapt2GeneratedDirectory()) emptyList() else listOf(source) - is SourceDirectorySet -> source.srcDirs.filter { !it.isKapt2GeneratedDirectory() } - else -> emptyList() - } - }.toTypedArray() - } - // override setSource to track source directory sets and files (for generated android folders) override fun setSource(sources: Any?) { - sourceRoots.clear() - val sourcesToAdd = filterOutKapt2Directories(sources) - addSourceRoots(*sourcesToAdd) - super.setSource(sourcesToAdd.firstOrNull()) + sourceRootsContainer.set(sources) + super.setSource(sources) } // override source to track source directory sets and files (for generated android folders) override fun source(vararg sources: Any?): SourceTask? { - val sourcesToAdd = filterOutKapt2Directories(*sources) - addSourceRoots(*sourcesToAdd) - return super.source(*sourcesToAdd) + sourceRootsContainer.add(*sources) + return super.source(*sources) } +} - internal fun findRootsForSources(sources: Iterable): Set { - val resultRoots = HashSet() - val sourceDirs = sources.mapTo(HashSet()) { it.parentFile } - - for (sourceDir in sourceDirs) { - for (sourceRoot in sourceRoots) { - if (FileUtil.isAncestor(sourceRoot, sourceDir, /* strict = */false)) { - resultRoots.add(sourceRoot) - } - } - } +internal fun compileJvmNotIncrementally( + compiler: K2JVMCompiler, + logger: Logger, + sourcesToCompile: List, + javaSourceRoots: Iterable, + compileClasspath: Iterable, + outputDir: File, + args: K2JVMCompilerArguments +): ExitCode { + logger.kotlinDebug("Removing all kotlin classes in $outputDir") + // we're free to delete all classes since only we know about that directory + // todo: can be optimized -- compile and remove only files that were not generated + listClassFiles(outputDir.canonicalPath).forEach { it.delete() } + + val moduleFile = makeModuleFile( + args.moduleName, + isTest = false, + outputDir = outputDir, + sourcesToCompile = sourcesToCompile, + javaSourceRoots = javaSourceRoots, + classpath = compileClasspath, + friendDirs = listOf()) + args.module = moduleFile.absolutePath + val messageCollector = GradleMessageCollector(logger) - return resultRoots + try { + logger.kotlinDebug("compiling with args: ${ArgumentUtils.convertArgumentsToStringList(args)}") + logger.kotlinDebug("compiling with classpath: ${compileClasspath.toList().sorted().joinToString()}") + return compiler.exec(messageCollector, Services.EMPTY, args) } - - private fun addSourceRoots(vararg sources: Any?) { - for (source in sources) { - when (source) { - is SourceDirectorySet -> sourceRoots.addAll(source.srcDirs) - is File -> sourceRoots.add(source) - } - } + finally { + moduleFile.delete() } } @@ -378,10 +335,15 @@ open class Kotlin2JsCompile() : AbstractKotlinCompile(), return args } - override fun callCompiler(args: K2JSCompilerArguments, allKotlinSources: List, changedFiles: ChangedFiles) { + override fun getSourceRoots() = SourceRoots.ForJs.create(getSource()) + + override fun callCompiler(args: K2JSCompilerArguments, sourceRoots: SourceRoots, changedFiles: ChangedFiles) { + sourceRoots as SourceRoots.ForJs + + val messageCollector = GradleMessageCollector(logger) logger.debug("Calling compiler") destinationDir.mkdirs() - args.freeArgs = args.freeArgs + allKotlinSources.map { it.absolutePath } + args.freeArgs = args.freeArgs + sourceRoots.kotlinSourceFiles.map { it.absolutePath } if (args.outputFile == null) { throw GradleException("$name.kotlinOptions.outputFile should be specified.") diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunner.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunner.kt index db6709f131087..a1dc3834f5319 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunner.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunner.kt @@ -98,7 +98,6 @@ internal class IncrementalJvmCompilerRunner( private val reporter: IncReporter, private var kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null, private val sourceAnnotationsRegistry: SourceAnnotationsRegistry? = null, - private val kapt2GeneratedSourcesDir: File? = null, private val artifactDifferenceRegistryProvider: ArtifactDifferenceRegistryProvider? = null, private val artifactFile: File? = null ) { @@ -356,9 +355,6 @@ internal class IncrementalJvmCompilerRunner( caches.lookupCache.update(lookupTracker, sourcesToCompile, removedKotlinSources) - val generatedJavaFiles = (kapt2GeneratedSourcesDir?.walk() ?: emptySequence()).filter(File::isJavaFile).toList() - val generatedJavaFilesDiff = caches.incrementalCache.generatedSourceSnapshotMap.compareAndUpdate(generatedJavaFiles) - if (compilationMode is CompilationMode.Rebuild) { artifactFile?.let { artifactFile -> artifactDifferenceRegistryProvider?.withRegistry(reporter) { registry -> @@ -369,23 +365,10 @@ internal class IncrementalJvmCompilerRunner( } val (dirtyLookupSymbols, dirtyClassFqNames) = compilationResult.getDirtyData(listOf(caches.incrementalCache), reporter) - val generatedJavaFilesChanges = javaFilesProcessor.process(generatedJavaFilesDiff) val compiledInThisIterationSet = sourcesToCompile.toHashSet() - val dirtyKotlinFilesFromJava = when (generatedJavaFilesChanges) { - is ChangesEither.Unknown -> { - reporter.report { "Could not get changes for generated java files, recompiling all kotlin" } - compilationMode = CompilationMode.Rebuild - allKotlinSources.toSet() - } - is ChangesEither.Known -> { - mapLookupSymbolsToFiles(caches.lookupCache, generatedJavaFilesChanges.lookupSymbols, reporter, excludes = compiledInThisIterationSet) - } - else -> throw IllegalStateException("Unknown ChangesEither implementation: $generatedJavaFiles") - } with (dirtySources) { clear() - addAll(dirtyKotlinFilesFromJava) addAll(mapLookupSymbolsToFiles(caches.lookupCache, dirtyLookupSymbols, reporter, excludes = compiledInThisIterationSet)) addAll(mapClassesFqNamesToFiles(listOf(caches.incrementalCache), dirtyClassFqNames, reporter, excludes = compiledInThisIterationSet)) } diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt index dc128facd61e1..0ff9a15758ab8 100644 --- a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt +++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt @@ -90,7 +90,7 @@ class Kapt3CommandLineProcessor : CommandLineProcessor { override val pluginOptions: Collection = listOf(SOURCE_OUTPUT_DIR_OPTION, ANNOTATION_PROCESSOR_CLASSPATH_OPTION, APT_OPTIONS_OPTION, - CLASS_OUTPUT_DIR_OPTION, VERBOSE_MODE_OPTION, STUBS_OUTPUT_DIR_OPTION) + CLASS_OUTPUT_DIR_OPTION, VERBOSE_MODE_OPTION, STUBS_OUTPUT_DIR_OPTION, APT_ONLY_OPTION) private fun CompilerConfiguration.appendList(option: CompilerConfigurationKey>, value: T) { val paths = getList(option).toMutableList()