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:
committed by
Yan Zhulanow
parent
92282072fc
commit
95d1210317
+1
-1
@@ -39,7 +39,7 @@ class ExampleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
|
||||
return listOf(SubpluginOption("exampleKey", "exampleValue"))
|
||||
}
|
||||
|
||||
override fun getPluginName(): String {
|
||||
override fun getCompilerPluginId(): String {
|
||||
return "example.plugin"
|
||||
}
|
||||
|
||||
|
||||
+7
-2
@@ -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
|
||||
}
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+44
-11
@@ -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"
|
||||
}
|
||||
+65
@@ -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))
|
||||
}
|
||||
}
|
||||
+57
-38
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+88
@@ -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
|
||||
}
|
||||
}
|
||||
+70
-108
@@ -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.")
|
||||
|
||||
-17
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user