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.

This commit is contained in:
Yan Zhulanow
2016-11-03 18:15:38 +03:00
committed by Yan Zhulanow
parent 92282072fc
commit 95d1210317
11 changed files with 335 additions and 180 deletions
@@ -39,7 +39,7 @@ class ExampleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
return listOf(SubpluginOption("exampleKey", "exampleValue"))
}
override fun getPluginName(): String {
override fun getCompilerPluginId(): String {
return "example.plugin"
}
@@ -32,8 +32,13 @@ interface KotlinGradleSubplugin<KotlinCompile : AbstractCompile> {
variantData: Any?,
javaSourceSet: SourceSet?
): List<SubpluginOption>
fun getPluginName(): String
fun getSubpluginKotlinTasks(
project: Project,
kotlinCompile: KotlinCompile
): List<AbstractCompile> = emptyList()
fun getCompilerPluginId(): String
fun getGroupName(): String
fun getArtifactName(): String
}
@@ -105,7 +105,7 @@ class AndroidSubplugin : KotlinGradleSubplugin<KotlinCompile> {
}
}
override fun getPluginName() = "org.jetbrains.kotlin.android"
override fun getCompilerPluginId() = "org.jetbrains.kotlin.android"
override fun getGroupName() = "org.jetbrains.kotlin"
@@ -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)
@@ -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<KotlinCompile> {
return if (sourceSetName != "main") "kapt${sourceSetName.capitalize()}" else "kapt"
}
}
private val kotlinToKaptTasksMap = mutableMapOf<KotlinCompile, KaptTask>()
override fun isApplicable(project: Project, task: KotlinCompile) = Kapt3GradleSubplugin.isEnabled(project)
@@ -73,7 +72,6 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
val kotlinCompile: KotlinCompile,
val javaCompile: AbstractCompile,
val variantData: Any?,
val javaSourceSet: SourceSet?,
val sourceSetName: String,
val kaptExtension: KaptExtension,
val kaptClasspath: MutableList<File>)
@@ -114,9 +112,16 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
val kaptExtension = project.extensions.getByType(KaptExtension::class.java)
val context = Kapt3SubpluginContext(project, kotlinCompile, javaCompile,
variantData, javaSourceSet, sourceSetName, kaptExtension, kaptClasspath)
variantData, sourceSetName, kaptExtension, kaptClasspath)
return context.buildOptions()
context.createKaptKotlinTask()
/** Plugin options are applied to kapt*Compile inside [createKaptKotlinTask] */
return emptyList()
}
override fun getSubpluginKotlinTasks(project: Project, kotlinCompile: KotlinCompile): List<KaptTask> {
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<KotlinCompile> {
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<KotlinCompile> {
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<KotlinCompile> {
private val BaseVariantData<*>.sourceProviders: List<SourceProvider>
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"
}
@@ -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))
}
}
@@ -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<out BaseVariantOutputData>) {
val logger = kotlinTask.project.logger
private fun configureSources(compileTask: AbstractCompile, variantData: BaseVariantData<out BaseVariantOutputData>) {
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<KotlinCompile>) }
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<KotlinGradleSubplugin<KotlinCompile>, List<File>>()
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<KotlinGradleSubplugin<KotlinCompile>>
): Map<KotlinGradleSubplugin<KotlinCompile>, List<File>> {
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<KotlinGradleSubplugin<KotlinCompile>, List<File>>()
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<KotlinGradleSubplugin<KotlinCompile>, List<File>>,
val subplugins: List<KotlinGradleSubplugin<KotlinCompile>>
) {
fun addSubpluginArguments(
fun addSubpluginOptions(
project: Project,
kotlinTask: KotlinCompile,
javaTask: AbstractCompile,
variantData: Any?,
javaSourceSet: SourceSet?) {
javaSourceSet: SourceSet?
): List<KotlinGradleSubplugin<KotlinCompile>> {
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
}
}
@@ -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<File>) {
private companion object {
fun dumpPaths(files: Iterable<File>): 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<File>, val javaSourceRoots: Set<File>) : SourceRoots(kotlinSourceFiles) {
companion object {
fun create(taskSource: FileTree, sourceRoots: FilteringSourceRootsContainer): ForJvm {
val kotlinSourceFiles = (taskSource as Iterable<File>).filter(File::isKotlinFile)
val javaSourceRoots = findRootsForSources(
sourceRoots.sourceRoots, taskSource.filter(File::isJavaFile))
return ForJvm(kotlinSourceFiles, javaSourceRoots)
}
private fun findRootsForSources(allSourceRoots: Iterable<File>, sources: Iterable<File>): Set<File> {
val resultRoots = HashSet<File>()
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<File>) : SourceRoots(kotlinSourceFiles) {
companion object {
fun create(taskSource: FileTree) = ForJs((taskSource as Iterable<File>).filter(File::isKotlinFile))
}
}
}
internal class FilteringSourceRootsContainer(val filter: (File) -> Boolean = { true }) {
private val mutableSourceRoots = mutableListOf<File>()
val sourceRoots: List<File>
get() = mutableSourceRoots
fun clear() {
mutableSourceRoots.clear()
}
fun set(source: Any?): List<File> {
clear()
return add(source)
}
fun add(vararg sources: Any?): List<File> {
val filteredDirs = mutableListOf<File>()
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
}
}
@@ -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<T : CommonCompilerArguments>() : 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<T : CommonCompilerArguments>() : 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<T : CommonCompilerArguments>() : 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<File> = (getSource() as Iterable<File>).filter(File::isKotlinFile)
internal abstract fun callCompiler(args: T, allKotlinSources: List<File>, changedFiles: ChangedFiles)
internal abstract fun getSourceRoots(): SourceRoots
internal abstract fun callCompiler(args: T, sourceRoots: SourceRoots, changedFiles: ChangedFiles)
}
open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), KotlinJvmCompile {
@@ -113,8 +115,7 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
private val kotlinOptionsImpl = KotlinJvmOptionsImpl()
override val kotlinOptions: KotlinJvmOptions
get() = kotlinOptionsImpl
private val sourceRoots = HashSet<File>()
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<K2JVMCompilerArguments>(), Kotl
private val compileClasspath: Iterable<File>
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<K2JVMCompilerArguments>(), Kotl
parentKotlinOptionsImpl?.updateArguments(args)
kotlinOptionsImpl.updateArguments(args)
fun dumpPaths(files: Iterable<File>): 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<File>, 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<K2JVMCompilerArguments>(), 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<K2JVMCompilerArguments>(), Kotl
}
}
private fun compileNotIncremental(
sourcesToCompile: List<File>,
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<K2JVMCompilerArguments>(), Kotl
}
}
private fun getJavaSourceRoots(): Set<File> =
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<Any> {
return sources.flatMap { source ->
when (source) {
is File -> if (source.isKapt2GeneratedDirectory()) emptyList<File>() else listOf(source)
is SourceDirectorySet -> source.srcDirs.filter { !it.isKapt2GeneratedDirectory() }
else -> emptyList<File>()
}
}.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<File>): Set<File> {
val resultRoots = HashSet<File>()
val sourceDirs = sources.mapTo(HashSet()) { it.parentFile }
internal fun compileJvmNotIncrementally(
compiler: K2JVMCompiler,
logger: Logger,
sourcesToCompile: List<File>,
javaSourceRoots: Iterable<File>,
compileClasspath: Iterable<File>,
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() }
for (sourceDir in sourceDirs) {
for (sourceRoot in sourceRoots) {
if (FileUtil.isAncestor(sourceRoot, sourceDir, /* strict = */false)) {
resultRoots.add(sourceRoot)
}
}
}
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<K2JSCompilerArguments>(),
return args
}
override fun callCompiler(args: K2JSCompilerArguments, allKotlinSources: List<File>, 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.")
@@ -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<File>()).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))
}
@@ -90,7 +90,7 @@ class Kapt3CommandLineProcessor : CommandLineProcessor {
override val pluginOptions: Collection<CliOption> =
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 <T> CompilerConfiguration.appendList(option: CompilerConfigurationKey<List<T>>, value: T) {
val paths = getList(option).toMutableList()