Run KAPT non-incrementally if annotation processor classpath changes

Annotation processor classpath for a KAPT run is recorded, and the current
run is compared against the previous one. If those differ, KAPT should
run non-incrementally.

Test: KaptIncrementalWithIsolatingApt.testChangingAnnotationProcessorClasspath
This commit is contained in:
Ivan Gavrilovic
2019-08-07 20:12:01 +09:00
committed by Yan Zhulanow
parent 2412b5f992
commit 0a26742501
4 changed files with 75 additions and 17 deletions
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.gradle
import org.jetbrains.kotlin.gradle.incapt.IncrementalProcessor
import org.jetbrains.kotlin.gradle.util.modify
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import java.io.File
import java.util.zip.ZipEntry
@@ -65,6 +64,28 @@ class KaptIncrementalWithIsolatingApt : KaptIncrementalIT() {
)
}
}
@Test
fun testChangingAnnotationProcessorClasspath() {
val project = getProject()
project.build("clean", "build") {
assertSuccessful()
}
project.gradleBuildScript().appendText(
"""
dependencies {
kapt 'com.google.guava:guava:12.0'
}
""".trimIndent()
)
project.build("build") {
assertSuccessful()
assertContains("Unable to use existing data, re-initializing classpath information for KAPT.")
}
}
}
private const val patternApt = "Processing java sources with annotation processors:"
@@ -201,7 +201,12 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
ClasspathSnapshot.ClasspathSnapshotFactory.getEmptySnapshot()
}
val currentSnapshot =
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(incAptCacheDir, classpath.files.toList(), allDataFiles)
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(
incAptCacheDir,
classpath.files.toList(),
kaptClasspath.files.toList(),
allDataFiles
)
val classpathChanges = currentSnapshot.diff(previousSnapshot, changedFiles)
if (classpathChanges == KaptClasspathChanges.Unknown) {
@@ -10,18 +10,21 @@ import java.util.*
import kotlin.collections.HashMap
private const val CLASSPATH_ENTRIES_FILE = "classpath-entries.bin"
private const val ANNOTATION_PROCESSOR_CLASSPATH_ENTRIES_FILE = "ap-classpath-entries.bin"
private const val CLASSPATH_STRUCTURE_FILE = "classpath-structure.bin"
open class ClasspathSnapshot protected constructor(
private val cacheDir: File,
private val classpath: List<File>,
private val annotationProcessorClasspath: List<File>,
private val dataForFiles: MutableMap<File, ClasspathEntryData?>
) {
object ClasspathSnapshotFactory {
fun loadFrom(cacheDir: File): ClasspathSnapshot {
val classpathEntries = cacheDir.resolve(CLASSPATH_ENTRIES_FILE)
val classpathStructureData = cacheDir.resolve(CLASSPATH_STRUCTURE_FILE)
if (!classpathEntries.exists() || !classpathStructureData.exists()) {
val annotationProcessorClasspathEntries= cacheDir.resolve(ANNOTATION_PROCESSOR_CLASSPATH_ENTRIES_FILE)
if (!classpathEntries.exists() || !classpathStructureData.exists() || !annotationProcessorClasspathEntries.exists()) {
return UnknownSnapshot
}
@@ -30,25 +33,34 @@ open class ClasspathSnapshot protected constructor(
it.readObject() as List<File>
}
val annotationProcessorClasspathFiles =
ObjectInputStream(BufferedInputStream(annotationProcessorClasspathEntries.inputStream())).use {
@Suppress("UNCHECKED_CAST")
it.readObject() as List<File>
}
val dataForFiles =
ObjectInputStream(BufferedInputStream(classpathStructureData.inputStream())).use {
@Suppress("UNCHECKED_CAST")
it.readObject() as MutableMap<File, ClasspathEntryData?>
}
return ClasspathSnapshot(cacheDir, classpathFiles, dataForFiles)
return ClasspathSnapshot(cacheDir, classpathFiles, annotationProcessorClasspathFiles, dataForFiles)
}
fun createCurrent(cacheDir: File, classpath: List<File>, allStructureData: Set<File>): ClasspathSnapshot {
fun createCurrent(cacheDir: File, classpath: List<File>, annotationProcessorClasspath: List<File>, allStructureData: Set<File>): ClasspathSnapshot {
val data = allStructureData.associateTo(HashMap<File, ClasspathEntryData?>(allStructureData.size)) { it to null }
return ClasspathSnapshot(cacheDir, classpath, data)
return ClasspathSnapshot(cacheDir, classpath, annotationProcessorClasspath, data)
}
fun getEmptySnapshot() = UnknownSnapshot
}
private fun isCompatible(snapshot: ClasspathSnapshot) =
this != UnknownSnapshot && snapshot != UnknownSnapshot && classpath == snapshot.classpath
this != UnknownSnapshot
&& snapshot != UnknownSnapshot
&& classpath == snapshot.classpath
&& annotationProcessorClasspath == snapshot.annotationProcessorClasspath
/** Compare this snapshot with the specified one only for the specified files. */
fun diff(previousSnapshot: ClasspathSnapshot, changedFiles: Set<File>): KaptClasspathChanges {
@@ -128,6 +140,11 @@ open class ClasspathSnapshot protected constructor(
it.writeObject(classpath)
}
val annotationProcessorClasspathEntries = cacheDir.resolve(ANNOTATION_PROCESSOR_CLASSPATH_ENTRIES_FILE)
ObjectOutputStream(BufferedOutputStream(annotationProcessorClasspathEntries.outputStream())).use {
it.writeObject(annotationProcessorClasspath)
}
val classpathStructureData = cacheDir.resolve(CLASSPATH_STRUCTURE_FILE)
ObjectOutputStream(BufferedOutputStream(classpathStructureData.outputStream())).use {
it.writeObject(dataForFiles)
@@ -179,7 +196,7 @@ open class ClasspathSnapshot protected constructor(
}
}
object UnknownSnapshot : ClasspathSnapshot(File(""), emptyList(), mutableMapOf())
object UnknownSnapshot : ClasspathSnapshot(File(""), emptyList(), emptyList(), mutableMapOf())
sealed class KaptIncrementalChanges {
object Unknown : KaptIncrementalChanges()
@@ -23,7 +23,7 @@ class ClasspathSnapshotTest {
)
val snapshotDir = tmp.newFolder()
val currentSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(snapshotDir, listOf(), setOf(data))
val currentSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(snapshotDir, listOf(), listOf(), setOf(data))
assertEquals(KaptClasspathChanges.Unknown, currentSnapshot.diff(UnknownSnapshot, setOf(data)))
currentSnapshot.writeToCache()
@@ -35,9 +35,23 @@ class ClasspathSnapshotTest {
@Test
fun testIncompatibleClasspaths() {
val firstSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(File(""), listOf(File("a.jar")), emptySet())
val firstSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(File(""), listOf(File("a.jar")), listOf(), emptySet())
val secondSnapshot =
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(File(""), listOf(File("b.jar")), setOf(File("")))
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(File(""), listOf(File("b.jar")), listOf(), setOf(File("")))
assertEquals(KaptClasspathChanges.Unknown, firstSnapshot.diff(secondSnapshot, emptySet()))
}
@Test
fun testIncompatibleAnnotationProcessorClasspaths() {
val firstSnapshot =
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(File(""), listOf(File("a.jar")), listOf(File("ap2.jar")), emptySet())
val secondSnapshot =
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(
File(""),
listOf(File("a.jar")),
listOf(File("ap1.jar")),
setOf(File(""))
)
assertEquals(KaptClasspathChanges.Unknown, firstSnapshot.diff(secondSnapshot, emptySet()))
}
@@ -47,7 +61,7 @@ class ClasspathSnapshotTest {
ClassData("first/A"),
ClassData("first/B").also { it.withAbiDependencies("first/A") }
)
val firstSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(tmp.newFolder(), listOf(), setOf(dataFile))
val firstSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(tmp.newFolder(), listOf(), listOf(), setOf(dataFile))
firstSnapshot.writeToCache()
generateStructureData(
@@ -55,7 +69,7 @@ class ClasspathSnapshotTest {
ClassData("first/B").also { it.withAbiDependencies("first/A") },
outputFile = dataFile
)
val changedSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(tmp.newFolder(), listOf(), setOf(dataFile))
val changedSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(tmp.newFolder(), listOf(), listOf(), setOf(dataFile))
val diff = changedSnapshot.diff(firstSnapshot, setOf(dataFile)) as KaptClasspathChanges.Known
assertEquals(setOf("first/A", "first/B"), diff.names)
@@ -72,12 +86,12 @@ class ClasspathSnapshotTest {
val cacheDir = tmp.newFolder()
val firstSnapshot =
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(cacheDir, listOf(), setOf(dataFile, libraryDataFile))
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(cacheDir, listOf(), listOf(), setOf(dataFile, libraryDataFile))
firstSnapshot.writeToCache()
generateStructureData(ClassData("library/C", ByteArray(1)), outputFile = libraryDataFile)
val changedSnapshot =
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(cacheDir, listOf(), setOf(dataFile, libraryDataFile))
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(cacheDir, listOf(), listOf(), setOf(dataFile, libraryDataFile))
val diff = changedSnapshot.diff(firstSnapshot, setOf(libraryDataFile)) as KaptClasspathChanges.Known
assertEquals(setOf("library/C", "first/A", "first/B"), diff.names)
@@ -89,11 +103,12 @@ class ClasspathSnapshotTest {
ClassData("first/A"),
ClassData("first/B").also { it.withAbiDependencies("first/A") }
)
val firstSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(tmp.newFolder(), listOf(), setOf(dataFile))
val firstSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(tmp.newFolder(), listOf(), listOf(), setOf(dataFile))
firstSnapshot.writeToCache()
val copyOfDataFile = dataFile.copyTo(tmp.newFile(), overwrite = true)
val secondSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(tmp.newFolder(), listOf(), setOf(copyOfDataFile))
val secondSnapshot =
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(tmp.newFolder(), listOf(), listOf(), setOf(copyOfDataFile))
val diff = secondSnapshot.diff(firstSnapshot, setOf()) as KaptClasspathChanges.Known
assertEquals(emptySet<String>(), diff.names)