Fix annotation processors outputting to classes dir with kapt 3
#KT-15915 fixed Annotation processor can access a classes directory provided by AP environment. Previously kapt 3 was using kotlin-classes directory as a a classes directory. However compileKotlin task does not expect this. Also having multiple tasks with the same output dir is a bad practise in Gradle. This change introduces a separate directory for classes generated by kapt 3. Its output is copied to a resulting classes dir (just as a kotlin-classes dir).
This commit is contained in:
+22
@@ -270,4 +270,26 @@ class Kapt3IT : BaseGradleIT() {
|
|||||||
assertCompiledKotlinSources(project.relativize(allMainKotlinSrc))
|
assertCompiledKotlinSources(project.relativize(allMainKotlinSrc))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testKaptClassesDirSync() {
|
||||||
|
val project = Project("autoService", GRADLE_VERSION, directoryPrefix = "kapt2")
|
||||||
|
|
||||||
|
project.build("build") {
|
||||||
|
assertSuccessful()
|
||||||
|
assertKaptSuccessful()
|
||||||
|
assertFileExists("processor/build/classes/main/META-INF/services/javax.annotation.processing.Processor")
|
||||||
|
assertFileExists("processor/build/classes/main/processor/MyProcessor.class")
|
||||||
|
}
|
||||||
|
|
||||||
|
project.projectDir.getFileByName("MyProcessor.kt").modify {
|
||||||
|
it.replace("@AutoService(Processor::class)", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
project.build(":processor:build") {
|
||||||
|
assertSuccessful()
|
||||||
|
assertNoSuchFile("processor/build/classes/main/META-INF/services/javax.annotation.processing.Processor")
|
||||||
|
assertFileExists("processor/build/classes/main/processor/MyProcessor.class")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
compile project(":processor")
|
||||||
|
kapt project(":processor")
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
ext.autoServiceVersion = '1.0-rc2'
|
||||||
|
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
compile "com.google.auto.service:auto-service:$autoServiceVersion"
|
||||||
|
kapt "com.google.auto.service:auto-service:$autoServiceVersion"
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package processor
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService
|
||||||
|
import javax.annotation.processing.*
|
||||||
|
import javax.lang.model.SourceVersion
|
||||||
|
import javax.lang.model.element.TypeElement
|
||||||
|
import javax.tools.Diagnostic
|
||||||
|
|
||||||
|
@AutoService(Processor::class)
|
||||||
|
@SupportedAnnotationTypes("annotation.ProcessThis")
|
||||||
|
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||||
|
class MyProcessor() : AbstractProcessor() {
|
||||||
|
|
||||||
|
private var fileCreated = false
|
||||||
|
|
||||||
|
override fun process(annotations: Set<TypeElement>,
|
||||||
|
roundEnv: RoundEnvironment): Boolean {
|
||||||
|
processingEnv.messager.printMessage(Diagnostic.Kind.NOTE, "Working!")
|
||||||
|
if (fileCreated) return true
|
||||||
|
fileCreated = true
|
||||||
|
val file = processingEnv.filer.createSourceFile("Check")
|
||||||
|
file.openWriter().use {
|
||||||
|
it.appendln("// $annotations")
|
||||||
|
it.appendln("public class Check {}")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
rootProject.name = 'kaptbug'
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package annotation
|
||||||
|
|
||||||
|
annotation class ProcessThis
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import annotation.ProcessThis
|
||||||
|
|
||||||
|
@ProcessThis
|
||||||
|
interface Model {
|
||||||
|
|
||||||
|
var a: Int
|
||||||
|
|
||||||
|
var b: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProcessThis
|
||||||
|
class Class {
|
||||||
|
|
||||||
|
var a = 0
|
||||||
|
}
|
||||||
-6
@@ -60,12 +60,6 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
|
|||||||
): ExitCode {
|
): ExitCode {
|
||||||
val outputDir = args.destinationAsFile
|
val outputDir = args.destinationAsFile
|
||||||
|
|
||||||
if (environment !is GradleIncrementalCompilerEnvironment) {
|
|
||||||
log.debug("Removing all kotlin classes in $outputDir")
|
|
||||||
// we're free to delete all classes since only we know about that directory
|
|
||||||
outputDir.deleteRecursively()
|
|
||||||
}
|
|
||||||
|
|
||||||
val moduleFile = makeModuleFile(
|
val moduleFile = makeModuleFile(
|
||||||
args.moduleName,
|
args.moduleName,
|
||||||
isTest = false,
|
isTest = false,
|
||||||
|
|||||||
+7
-1
@@ -29,6 +29,7 @@ import org.gradle.api.tasks.compile.JavaCompile
|
|||||||
import org.jetbrains.kotlin.gradle.plugin.*
|
import org.jetbrains.kotlin.gradle.plugin.*
|
||||||
import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
|
import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
import org.jetbrains.kotlin.gradle.tasks.SyncOutputTask
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
// apply plugin: 'kotlin-kapt'
|
// apply plugin: 'kotlin-kapt'
|
||||||
@@ -59,6 +60,9 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun findMainKaptConfiguration(project: Project) = project.findKaptConfiguration(MAIN_KAPT_CONFIGURATION_NAME)
|
fun findMainKaptConfiguration(project: Project) = project.findKaptConfiguration(MAIN_KAPT_CONFIGURATION_NAME)
|
||||||
|
|
||||||
|
fun getKaptClasssesDir(project: Project, sourceSetName: String): File =
|
||||||
|
File(project.project.buildDir, "tmp/kapt3/classes/$sourceSetName")
|
||||||
}
|
}
|
||||||
|
|
||||||
private val kotlinToKaptTasksMap = mutableMapOf<KotlinCompile, KaptTask>()
|
private val kotlinToKaptTasksMap = mutableMapOf<KotlinCompile, KaptTask>()
|
||||||
@@ -156,7 +160,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
|||||||
javaCompile.source(generatedFilesDir)
|
javaCompile.source(generatedFilesDir)
|
||||||
|
|
||||||
pluginOptions += SubpluginOption("sources", generatedFilesDir.canonicalPath)
|
pluginOptions += SubpluginOption("sources", generatedFilesDir.canonicalPath)
|
||||||
pluginOptions += SubpluginOption("classes", kotlinCompile.destinationDir.canonicalPath)
|
pluginOptions += SubpluginOption("classes", getKaptClasssesDir(project, sourceSetName).canonicalPath)
|
||||||
|
|
||||||
val androidPlugin = variantData?.let {
|
val androidPlugin = variantData?.let {
|
||||||
project.extensions.findByName("android") as? BaseExtension
|
project.extensions.findByName("android") as? BaseExtension
|
||||||
@@ -193,6 +197,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
|||||||
|
|
||||||
private fun Kapt3SubpluginContext.createKaptKotlinTask() {
|
private fun Kapt3SubpluginContext.createKaptKotlinTask() {
|
||||||
val sourcesOutputDir = getKaptGeneratedDir(project, sourceSetName)
|
val sourcesOutputDir = getKaptGeneratedDir(project, sourceSetName)
|
||||||
|
val classesOutputDir = getKaptClasssesDir(project, sourceSetName)
|
||||||
|
|
||||||
// Replace compile*Kotlin to kapt*Kotlin
|
// Replace compile*Kotlin to kapt*Kotlin
|
||||||
assert(kotlinCompile.name.startsWith("compile"))
|
assert(kotlinCompile.name.startsWith("compile"))
|
||||||
@@ -207,6 +212,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
|||||||
|
|
||||||
kaptTask.mapClasspath { kotlinCompile.classpath }
|
kaptTask.mapClasspath { kotlinCompile.classpath }
|
||||||
kaptTask.destinationDir = sourcesOutputDir
|
kaptTask.destinationDir = sourcesOutputDir
|
||||||
|
kaptTask.classesDir = classesOutputDir
|
||||||
kaptTask.dependsOn(*(javaCompile.dependsOn.filter { it !== kotlinCompile }.toTypedArray()))
|
kaptTask.dependsOn(*(javaCompile.dependsOn.filter { it !== kotlinCompile }.toTypedArray()))
|
||||||
kotlinCompile.dependsOn(kaptTask)
|
kotlinCompile.dependsOn(kaptTask)
|
||||||
|
|
||||||
|
|||||||
+5
@@ -39,12 +39,17 @@ open class KaptTask : AbstractCompile() {
|
|||||||
return FileUtil.isAncestor(destinationDir, this, /* strict = */ false)
|
return FileUtil.isAncestor(destinationDir, this, /* strict = */ false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lateinit var classesDir: File
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
override fun compile() {
|
override fun compile() {
|
||||||
/** Delete everything inside the [destinationDir] */
|
/** Delete everything inside the [destinationDir] */
|
||||||
destinationDir.deleteRecursively()
|
destinationDir.deleteRecursively()
|
||||||
destinationDir.mkdirs()
|
destinationDir.mkdirs()
|
||||||
|
|
||||||
|
classesDir.deleteRecursively()
|
||||||
|
classesDir.mkdirs()
|
||||||
|
|
||||||
val sourceRoots = SourceRoots.ForJvm.create(getSource(), rawSourceRoots)
|
val sourceRoots = SourceRoots.ForJvm.create(getSource(), rawSourceRoots)
|
||||||
val compileClasspath = classpath.toList().filter(File::exists)
|
val compileClasspath = classpath.toList().filter(File::exists)
|
||||||
|
|
||||||
|
|||||||
+5
-4
@@ -21,15 +21,15 @@ import org.gradle.api.tasks.bundling.Zip
|
|||||||
import org.gradle.api.tasks.compile.AbstractCompile
|
import org.gradle.api.tasks.compile.AbstractCompile
|
||||||
import org.gradle.api.tasks.compile.JavaCompile
|
import org.gradle.api.tasks.compile.JavaCompile
|
||||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl
|
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl
|
||||||
import org.jetbrains.kotlin.gradle.internal.AnnotationProcessingManager
|
import org.jetbrains.kotlin.gradle.internal.*
|
||||||
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
|
import org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin.Companion.getKaptClasssesDir
|
||||||
import org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin
|
|
||||||
import org.jetbrains.kotlin.gradle.internal.initKapt
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
|
import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
|
||||||
import org.jetbrains.kotlin.gradle.plugin.android.KotlinJillTask
|
import org.jetbrains.kotlin.gradle.plugin.android.KotlinJillTask
|
||||||
import org.jetbrains.kotlin.gradle.tasks.*
|
import org.jetbrains.kotlin.gradle.tasks.*
|
||||||
import org.jetbrains.kotlin.incremental.configureMultiProjectIncrementalCompilation
|
import org.jetbrains.kotlin.incremental.configureMultiProjectIncrementalCompilation
|
||||||
import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifferenceRegistryProviderAndroidWrapper
|
import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifferenceRegistryProviderAndroidWrapper
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -540,6 +540,7 @@ private fun createSyncOutputTask(
|
|||||||
syncTask.kotlinTask = kotlinCompile
|
syncTask.kotlinTask = kotlinCompile
|
||||||
kotlinTask.javaOutputDir = javaDir
|
kotlinTask.javaOutputDir = javaDir
|
||||||
kotlinAfterJavaTask?.javaOutputDir = javaDir
|
kotlinAfterJavaTask?.javaOutputDir = javaDir
|
||||||
|
syncTask.kaptClassesDir = getKaptClasssesDir(project, variantName)
|
||||||
|
|
||||||
// copying should be executed after a latter task
|
// copying should be executed after a latter task
|
||||||
val previousTask = kotlinAfterJavaTask ?: javaTask
|
val previousTask = kotlinAfterJavaTask ?: javaTask
|
||||||
|
|||||||
+21
-7
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.gradle.tasks
|
package org.jetbrains.kotlin.gradle.tasks
|
||||||
|
|
||||||
|
import ch.qos.logback.core.util.FileUtil
|
||||||
import org.gradle.api.DefaultTask
|
import org.gradle.api.DefaultTask
|
||||||
import org.gradle.api.tasks.InputFiles
|
import org.gradle.api.tasks.InputFiles
|
||||||
import org.gradle.api.tasks.OutputDirectory
|
import org.gradle.api.tasks.OutputDirectory
|
||||||
@@ -23,6 +24,7 @@ import org.gradle.api.tasks.OutputFiles
|
|||||||
import org.gradle.api.tasks.TaskAction
|
import org.gradle.api.tasks.TaskAction
|
||||||
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
|
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
|
||||||
import org.gradle.api.tasks.incremental.InputFileDetails
|
import org.gradle.api.tasks.incremental.InputFileDetails
|
||||||
|
import org.jetbrains.kotlin.com.intellij.openapi.util.io.FileUtil.*
|
||||||
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
|
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.ObjectInputStream
|
import java.io.ObjectInputStream
|
||||||
@@ -55,6 +57,13 @@ import kotlin.properties.Delegates
|
|||||||
internal open class SyncOutputTask : DefaultTask() {
|
internal open class SyncOutputTask : DefaultTask() {
|
||||||
@get:InputFiles
|
@get:InputFiles
|
||||||
var kotlinOutputDir: File by Delegates.notNull()
|
var kotlinOutputDir: File by Delegates.notNull()
|
||||||
|
|
||||||
|
@get:InputFiles
|
||||||
|
var kaptClassesDir: File by Delegates.notNull()
|
||||||
|
|
||||||
|
private val classesDirs: List<File>
|
||||||
|
get() = listOf(kotlinOutputDir, kaptClassesDir).filter(File::exists)
|
||||||
|
|
||||||
var javaOutputDir: File by Delegates.notNull()
|
var javaOutputDir: File by Delegates.notNull()
|
||||||
var kotlinTask: KotlinCompile by Delegates.notNull()
|
var kotlinTask: KotlinCompile by Delegates.notNull()
|
||||||
|
|
||||||
@@ -78,13 +87,14 @@ internal open class SyncOutputTask : DefaultTask() {
|
|||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@TaskAction
|
@TaskAction
|
||||||
fun execute(inputs: IncrementalTaskInputs): Unit {
|
fun execute(inputs: IncrementalTaskInputs): Unit {
|
||||||
|
val sourceDirs = classesDirs.joinToString()
|
||||||
if (inputs.isIncremental) {
|
if (inputs.isIncremental) {
|
||||||
logger.kotlinDebug { "Incremental copying files from $kotlinOutputDir to $javaOutputDir" }
|
logger.kotlinDebug { "Incremental copying files from $sourceDirs to $javaOutputDir" }
|
||||||
inputs.outOfDate { processIncrementally(it) }
|
inputs.outOfDate { processIncrementally(it) }
|
||||||
inputs.removed { processIncrementally(it) }
|
inputs.removed { processIncrementally(it) }
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger.kotlinDebug { "Non-incremental copying files from $kotlinOutputDir to $javaOutputDir" }
|
logger.kotlinDebug { "Non-incremental copying files from $sourceDirs to $javaOutputDir" }
|
||||||
processNonIncrementally()
|
processNonIncrementally()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,14 +113,16 @@ internal open class SyncOutputTask : DefaultTask() {
|
|||||||
timestampsFile.delete()
|
timestampsFile.delete()
|
||||||
timestamps.clear()
|
timestamps.clear()
|
||||||
|
|
||||||
kotlinOutputDir.walkTopDown().forEach {
|
for (dir in classesDirs) {
|
||||||
copy(it, it.siblingInJavaDir)
|
dir.walkTopDown().forEach {
|
||||||
|
copy(it, it.siblingInJavaDir(baseDir = dir))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processIncrementally(input: InputFileDetails) {
|
private fun processIncrementally(input: InputFileDetails) {
|
||||||
val fileInKotlinDir = input.file
|
val fileInKotlinDir = input.file
|
||||||
val fileInJavaDir = fileInKotlinDir.siblingInJavaDir
|
val fileInJavaDir = fileInKotlinDir.siblingInJavaDir()
|
||||||
|
|
||||||
if (input.isRemoved) {
|
if (input.isRemoved) {
|
||||||
// file was removed in kotlin dir, remove from java as well
|
// file was removed in kotlin dir, remove from java as well
|
||||||
@@ -146,8 +158,10 @@ internal open class SyncOutputTask : DefaultTask() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val File.siblingInJavaDir: File
|
private fun File.siblingInJavaDir(baseDir: File? = null): File {
|
||||||
get() = File(javaOutputDir, this.relativeTo(kotlinOutputDir).path)
|
val base = baseDir ?: classesDirs.find { isAncestor(it, this, true) }!!
|
||||||
|
return File(javaOutputDir, this.relativeTo(base).path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val TIMESTAMP_FILE_NAME = "kotlin-files-in-java-timestamps.bin"
|
private val TIMESTAMP_FILE_NAME = "kotlin-files-in-java-timestamps.bin"
|
||||||
|
|||||||
+6
@@ -231,6 +231,12 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!incremental) {
|
||||||
|
logger.kotlinDebug { "Removing all kotlin classes in $destinationDir" }
|
||||||
|
destinationDir.deleteRecursively()
|
||||||
|
destinationDir.mkdirs()
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val exitCode = compilerRunner.runJvmCompiler(sourceRoots.kotlinSourceFiles, sourceRoots.javaSourceRoots, args, environment)
|
val exitCode = compilerRunner.runJvmCompiler(sourceRoots.kotlinSourceFiles, sourceRoots.javaSourceRoots, args, environment)
|
||||||
processCompilerExitCode(exitCode)
|
processCompilerExitCode(exitCode)
|
||||||
|
|||||||
Reference in New Issue
Block a user