Compare commits

...

5 Commits

Author SHA1 Message Date
Ivan Gavrilovic
d19fdb2008 KT-31291: Wrap ASM version in a method to avoid having it inlined
This commit adds a method to get the value of org.jetbrains.org.objectweb.asm.Oopcodes.API_VERSION.
In general, this should not be a problem, but some Gradle plugins package
Jetbrains ASM version. At runtime, their version may be loaded which may be
different than the one this code was compiled against.

(cherry picked from commit 4caddd2c1c)
2019-07-15 16:55:04 +09:00
Ivan Gavrilovic
4f27cc0efb Improve incremental KAPT logging
This commit improves incremental KAPT logging. It reports processor
stats using their actual names. Also, warning is printed if incremental
annotation processing is requested, but not all APs are incremental

(cherry picked from commit 6619106af2)
2019-07-15 16:54:54 +09:00
Ivan Gavrilovic
226bdfe993 KAPT: Mark incremental cache as LocalState
Use @LocalState to mark incremental annotation processing cache directory.
This is to make sure this is not stored in the build cache as it contains
absolute paths and cannot be shared between machines.

(cherry picked from commit 5cab6fd6cf)
2019-07-15 16:54:40 +09:00
Ivan Gavrilovic
293eb56a9f KT-31840: Fix caching for incremental annotation processing
Artifact transform that uses ASM to analyze KAPT classpath stored absolute
paths in the output artifact. This resulted in remote build cache misses.

This commit changes how analysis is stored. Actual analysis file is the
output of the transform, and there is not need to use a marker file any more.
Output does not store the classpath entry absolute path. Instead, it uses
task action incremental information to find analysis outputs that changed.

Additionally, class that handles analysis snapshot comparison has been
simplified, and lazy loading of the structural information is handled in
a more straightforward way.

(cherry picked from commit 251d8ccd97)
2019-07-15 16:54:29 +09:00
nikita.movshin
8442ecc368 Add changelog for 1.3.50 EAP 1 2019-07-13 15:12:08 +03:00
13 changed files with 381 additions and 8503 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -286,7 +286,12 @@ abstract class BaseGradleIT {
build(*params, options = options.copy(kotlinDaemonDebugPort = debugPort), check = check)
}
fun Project.build(vararg params: String, options: BuildOptions = defaultBuildOptions(), check: CompiledProject.() -> Unit) {
fun Project.build(
vararg params: String,
options: BuildOptions = defaultBuildOptions(),
projectDir: File = File(workingDir, projectName),
check: CompiledProject.() -> Unit
) {
val wrapperVersion = chooseWrapperVersionOrFinishTest()
val env = createEnvironmentVariablesMap(options)
@@ -295,7 +300,6 @@ abstract class BaseGradleIT {
println("<=== Test build: ${this.projectName} $cmd ===>")
val projectDir = File(workingDir, projectName)
if (!projectDir.exists()) {
setupWorkingDir()
}

View File

@@ -135,34 +135,33 @@ class BuildCacheIT : BaseGradleIT() {
}
@Test
fun testKaptCachingEnabledByDefault() = with(Project("simple", GRADLE_VERSION, directoryPrefix = "kapt2")) {
prepareLocalBuildCache()
fun testKaptCachingWithIncrementalApt() {
with(Project("kaptAvoidance", GRADLE_VERSION, directoryPrefix = "kapt2")) {
prepareLocalBuildCache()
build("clean", "build") {
assertSuccessful()
assertContains("Packing task ':kaptGenerateStubsKotlin'")
assertContains("Packing task ':kaptKotlin'")
}
build("clean", "build") {
assertSuccessful()
assertContains(":kaptGenerateStubsKotlin FROM-CACHE")
assertContains(":kaptKotlin FROM-CACHE")
}
File(projectDir, "build.gradle").appendText(
"\n" + """
afterEvaluate {
kaptKotlin.useBuildCache = false
val options = defaultBuildOptions().copy(
kaptOptions = KaptOptions(
verbose = true,
useWorkers = false,
incrementalKapt = true,
includeCompileClasspath = false
)
)
build(options = options, params = *arrayOf("clean", ":app:build")) {
assertSuccessful()
assertContains("Packing task ':app:kaptGenerateStubsKotlin'")
assertContains("Packing task ':app:kaptKotlin'")
}
""".trimIndent()
)
build("clean", "build") {
assertSuccessful()
assertContains(":kaptGenerateStubsKotlin FROM-CACHE")
assertNotContains(":kaptKotlin FROM-CACHE")
assertContains("Caching disabled for task ':kaptKotlin': 'Caching is disabled for kapt")
// copy project to a new location
val copyProject = projectDir.resolveSibling("copy_${projectDir.name}").also { it.mkdirs() }
copyRecursively(projectDir, copyProject)
build(options = options, projectDir = copyProject.resolve(projectName), params = *arrayOf("clean", "build")) {
assertSuccessful()
assertContains(":app:kaptGenerateStubsKotlin FROM-CACHE")
assertContains(":app:kaptKotlin FROM-CACHE")
}
}
}
}

View File

@@ -6,8 +6,8 @@ import org.gradle.api.file.FileCollection
import org.gradle.api.internal.ConventionTask
import org.gradle.api.tasks.*
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptClasspathChanges
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.ClasspathSnapshot
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptClasspathChanges
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptIncrementalChanges
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.UnknownSnapshot
import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState
@@ -58,7 +58,7 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
internal var classpathStructure: FileCollection? = null
/** Output directory that contains caches necessary to support incremental annotation processing. */
@get:OutputDirectory
@get:LocalState
@get:Optional
var incAptCache: File? = null
@@ -171,21 +171,21 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
clearLocalState()
return KaptIncrementalChanges.Unknown
}
val allDataFiles = classpathStructure!!.files
if (!inputs.isIncremental) {
clearLocalState()
findClasspathChanges(classpath.files)
findClasspathChanges(allDataFiles, allDataFiles)
return KaptIncrementalChanges.Unknown
}
val changedFiles = with(mutableSetOf<File>()) {
inputs.outOfDate { this.add(it.file) }
inputs.removed { this.add(it.file) }
return@with this.toList()
return@with this
}
val classpathChanges = classpath.files.let { cp -> changedFiles.filter { cp.contains(it) } }
val classpathStatus = findClasspathChanges(classpathChanges)
val changedDataFiles = allDataFiles.filterTo(HashSet<File>()) { it in changedFiles }
val classpathStatus = findClasspathChanges(allDataFiles, changedDataFiles)
return when (classpathStatus) {
is KaptClasspathChanges.Unknown -> KaptIncrementalChanges.Unknown
is KaptClasspathChanges.Known -> KaptIncrementalChanges.Known(
@@ -194,18 +194,17 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
}
}
private fun findClasspathChanges(changedClasspath: Iterable<File>): KaptClasspathChanges {
private fun findClasspathChanges(allDataFile: Set<File>, changedDataFiles: Set<File>): KaptClasspathChanges {
val incAptCacheDir = incAptCache!!
incAptCacheDir.mkdirs()
val startTime = System.currentTimeMillis()
val previousSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.loadFrom(incAptCacheDir)
val currentSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(
incAptCacheDir, classpath.files.toList(), classpathStructure!!.files
)
val currentSnapshot =
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(incAptCacheDir, classpath.files.toList(), allDataFile)
val classpathChanges = currentSnapshot.diff(previousSnapshot, changedClasspath.toSet())
val classpathChanges = currentSnapshot.diff(previousSnapshot, changedDataFiles)
currentSnapshot.writeToCache()
if (logger.isInfoEnabled) {

View File

@@ -9,7 +9,19 @@ import org.jetbrains.org.objectweb.asm.*
const val metadataDescriptor: String = "Lkotlin/Metadata;"
class ClassAbiExtractor(private val writer: ClassWriter) : ClassVisitor(Opcodes.API_VERSION, writer) {
/**
* Use this to get the ASM version, as otherwise value gets inlined which may cause runtime issues if
* another plugin adds ASM to the classpath. See https://youtrack.jetbrains.com/issue/KT-31291 for more details.
*/
internal val lazyAsmApiVersion = lazy {
try {
Opcodes::API_VERSION.get()
} catch(e: Throwable) {
Opcodes.API_VERSION
}
}
class ClassAbiExtractor(private val writer: ClassWriter) : ClassVisitor(lazyAsmApiVersion.value, writer) {
override fun visitMethod(
access: Int,

View File

@@ -9,7 +9,7 @@ import org.jetbrains.org.objectweb.asm.*
private val toIgnore = setOf("java/lang/Object", "kotlin/Metadata", "org/jetbrains/annotations/NotNull")
class ClassTypeExtractorVisitor(visitor: ClassVisitor) : ClassVisitor(Opcodes.API_VERSION, visitor) {
class ClassTypeExtractorVisitor(visitor: ClassVisitor) : ClassVisitor(lazyAsmApiVersion.value, visitor) {
private val abiTypes = mutableSetOf<String>()
private val privateTypes = mutableSetOf<String>()

View File

@@ -26,10 +26,7 @@ class StructureArtifactTransform : ArtifactTransform() {
val dataFile = outputDirectory.resolve("output.bin")
data.saveTo(dataFile)
val lazyStructureFile = outputDirectory.resolve("lazy-output.bin")
LazyClasspathEntryData(input, dataFile).saveToFile(lazyStructureFile)
return mutableListOf(lazyStructureFile)
return mutableListOf(dataFile)
} catch (e: Throwable) {
throw e
}
@@ -86,25 +83,6 @@ private fun analyzeInputStream(input: InputStream, internalName: String, entryDa
ClassDependencies(typeDependenciesExtractor.getAbiTypes(), typeDependenciesExtractor.getPrivateTypes())
}
class LazyClasspathEntryData(val classpathEntry: File, private val dataFile: File) : Serializable {
object LazyClasspathEntrySerializer {
fun loadFromFile(file: File): LazyClasspathEntryData {
ObjectInputStream(BufferedInputStream(file.inputStream())).use {
return it.readObject() as LazyClasspathEntryData
}
}
}
fun saveToFile(file: File) {
ObjectOutputStream(BufferedOutputStream(file.outputStream())).use {
it.writeObject(this)
}
}
fun getClasspathEntryData(): ClasspathEntryData = ClasspathEntryData.ClasspathEntrySerializer.loadFrom(dataFile)
}
class ClasspathEntryData : Serializable {
object ClasspathEntrySerializer {

View File

@@ -7,61 +7,46 @@ package org.jetbrains.kotlin.gradle.internal.kapt.incremental
import java.io.*
import java.util.*
import kotlin.collections.HashMap
private const val CLASSPATH_ENTRIES_FILE = "classpath-entries.bin"
private const val CLASSPATH_STRUCTURE_FILE = "classpath-structure.bin"
open class ClasspathSnapshot protected constructor(
private val cacheDir: File,
private val classpath: Iterable<File>,
val dataForFiles: (Set<File>) -> Map<File, ClasspathEntryData>
private val classpath: List<File>,
private val dataForFiles: MutableMap<File, ClasspathEntryData?>
) {
val classpathData: (Set<File>) -> Map<File, ClasspathEntryData> = { files ->
val missingFiles = files.filter { !computedClasspathData.keys.contains(it) }.toSet()
if (!missingFiles.isEmpty()) {
val computedData = dataForFiles(missingFiles)
computedClasspathData.putAll(computedData)
}
computedClasspathData
}
private val computedClasspathData: MutableMap<File, ClasspathEntryData> = mutableMapOf()
object ClasspathSnapshotFactory {
fun loadFrom(cacheDir: File): ClasspathSnapshot {
val classpathEntries = cacheDir.resolve("classpath-entries.bin")
val classpathStructureData = cacheDir.resolve("classpath-structure.bin")
val classpathEntries = cacheDir.resolve(CLASSPATH_ENTRIES_FILE)
val classpathStructureData = cacheDir.resolve(CLASSPATH_STRUCTURE_FILE)
if (!classpathEntries.exists() || !classpathStructureData.exists()) {
return UnknownSnapshot
}
val classpathFiles = ObjectInputStream(BufferedInputStream(classpathEntries.inputStream())).use {
@Suppress("UNCHECKED_CAST")
it.readObject() as Iterable<File>
it.readObject() as List<File>
}
val classpathData = { _: Set<File> ->
loadPreviousData(classpathStructureData)
}
return ClasspathSnapshot(cacheDir, classpathFiles, classpathData)
val dataForFiles =
ObjectInputStream(BufferedInputStream(classpathStructureData.inputStream())).use {
@Suppress("UNCHECKED_CAST")
it.readObject() as MutableMap<File, ClasspathEntryData?>
}
return ClasspathSnapshot(cacheDir, classpathFiles, dataForFiles)
}
fun createCurrent(cacheDir: File, classpath: Iterable<File>, lazyClasspathData: Set<File>): ClasspathSnapshot {
val lazyData = lazyClasspathData.map { LazyClasspathEntryData.LazyClasspathEntrySerializer.loadFromFile(it) }
val data = { files: Set<File> ->
lazyData.filter { files.contains(it.classpathEntry) }.associate { it.classpathEntry to it.getClasspathEntryData() }
}
fun createCurrent(cacheDir: File, classpath: List<File>, allStructureData: Set<File>): ClasspathSnapshot {
val data = allStructureData.associateTo(HashMap<File, ClasspathEntryData?>(allStructureData.size)) { it to null }
return ClasspathSnapshot(cacheDir, classpath, data)
}
private fun loadPreviousData(file: File): Map<File, ClasspathEntryData> {
ObjectInputStream(BufferedInputStream(file.inputStream())).use {
@Suppress("UNCHECKED_CAST")
return it.readObject() as Map<File, ClasspathEntryData>
}
}
}
private fun isCompatible(snapshot: ClasspathSnapshot) =
this != UnknownSnapshot && classpath == snapshot.classpath
this != UnknownSnapshot && snapshot != UnknownSnapshot && classpath == snapshot.classpath
/** Compare this snapshot with the specified one only for the specified files. */
fun diff(previousSnapshot: ClasspathSnapshot, changedFiles: Set<File>): KaptClasspathChanges {
@@ -69,54 +54,88 @@ open class ClasspathSnapshot protected constructor(
return KaptClasspathChanges.Unknown
}
val currentData = classpathData(changedFiles)
val previousData = previousSnapshot.classpathData(changedFiles)
loadEntriesFor(changedFiles)
val currentHashAbiSize = changedFiles.sumBy { dataForFiles[it]!!.classAbiHash.size }
val currentHashesToAnalyze =
HashMap<String, ByteArray>(currentHashAbiSize).also { hashes ->
changedFiles.forEach {
hashes.putAll(dataForFiles[it]!!.classAbiHash)
}
}
val currentUnchanged = dataForFiles.keys.filter { it !in changedFiles }
val previousChanged = previousSnapshot.dataForFiles.keys.filter { it !in currentUnchanged }
check(changedFiles.size == previousChanged.size) {
"""
Number of changed files in snapshots differs. Reported changed files: $changedFiles
Current snapshot data files: ${dataForFiles.keys}
Previous snapshot data files: ${previousSnapshot.dataForFiles.keys}
""".trimIndent()
}
val previousHashAbiSize = previousChanged.sumBy { previousSnapshot.dataForFiles.get(it)?.classAbiHash?.size ?: 0 }
val previousHashesToAnalyze =
HashMap<String, ByteArray>(previousHashAbiSize).also { hashes ->
for (c in previousChanged) {
previousSnapshot.dataForFiles[c]?.let {
hashes.putAll(it.classAbiHash)
}
}
}
val changedClasses = mutableSetOf<String>()
for (changed in changedFiles) {
val previous = previousData.getValue(changed)
val current = currentData.getValue(changed)
for (key in previous.classAbiHash.keys + current.classAbiHash.keys) {
val previousHash = previous.classAbiHash[key]
if (previousHash == null) {
changedClasses.add(key)
continue
}
val currentHash = current.classAbiHash[key]
if (currentHash == null) {
changedClasses.add(key)
continue
}
if (!previousHash.contentEquals(currentHash)) {
changedClasses.add(key)
}
for (key in previousHashesToAnalyze.keys + currentHashesToAnalyze.keys) {
val previousHash = previousHashesToAnalyze[key]
if (previousHash == null) {
changedClasses.add(key)
continue
}
val currentHash = currentHashesToAnalyze[key]
if (currentHash == null) {
changedClasses.add(key)
continue
}
if (!previousHash.contentEquals(currentHash)) {
changedClasses.add(key)
}
}
// We do not compute structural data for unchanged files of the current snapshot for performance reasons.
// That is why we reuse the previous snapshot as that one contains all unchanged entries.
previousData.filterTo(computedClasspathData) { (key, _) -> key !in computedClasspathData }
for (unchanged in currentUnchanged) {
dataForFiles[unchanged] = previousSnapshot.dataForFiles[unchanged]!!
}
val allImpactedClasses = findAllImpacted(changedClasses)
return KaptClasspathChanges.Known(allImpactedClasses)
}
private fun loadEntriesFor(file: Iterable<File>) {
for (f in file) {
if (dataForFiles[f] == null) {
dataForFiles[f] = ClasspathEntryData.ClasspathEntrySerializer.loadFrom(f)
}
}
}
private fun loadAll() {
loadEntriesFor(dataForFiles.keys)
}
fun writeToCache() {
val classpathEntries = cacheDir.resolve("classpath-entries.bin")
loadAll()
val classpathEntries = cacheDir.resolve(CLASSPATH_ENTRIES_FILE)
ObjectOutputStream(BufferedOutputStream(classpathEntries.outputStream())).use {
it.writeObject(classpath)
}
val classpathStructureData = cacheDir.resolve("classpath-structure.bin")
storeCurrentStructure(classpathStructureData, classpathData(classpath.toSet()))
}
private fun storeCurrentStructure(file: File, structure: Map<File, ClasspathEntryData>) {
ObjectOutputStream(BufferedOutputStream(file.outputStream())).use {
it.writeObject(structure)
val classpathStructureData = cacheDir.resolve(CLASSPATH_STRUCTURE_FILE)
ObjectOutputStream(BufferedOutputStream(classpathStructureData.outputStream())).use {
it.writeObject(dataForFiles)
}
}
@@ -125,8 +144,8 @@ open class ClasspathSnapshot protected constructor(
val transitiveDeps = HashMap<String, MutableList<String>>()
val nonTransitiveDeps = HashMap<String, MutableList<String>>()
for (entry in computedClasspathData.values) {
for ((className, classDependency) in entry.classDependencies) {
for (entry in dataForFiles.values) {
for ((className, classDependency) in entry!!.classDependencies) {
for (abiType in classDependency.abiTypes) {
(transitiveDeps[abiType] ?: LinkedList()).let {
it.add(className)
@@ -165,7 +184,7 @@ open class ClasspathSnapshot protected constructor(
}
}
object UnknownSnapshot : ClasspathSnapshot(File(""), emptyList(), { emptyMap() })
object UnknownSnapshot : ClasspathSnapshot(File(""), emptyList(), mutableMapOf())
sealed class KaptIncrementalChanges {
object Unknown : KaptIncrementalChanges()

View File

@@ -35,10 +35,7 @@ class ClasspathAnalyzerTest {
val transform = StructureArtifactTransform().also { it.outputDirectory = tmp.newFolder() }
val outputs = transform.transform(classesDir)
val lazyData = LazyClasspathEntryData.LazyClasspathEntrySerializer.loadFromFile(outputs.single())
assertEquals(classesDir, lazyData.classpathEntry)
val data = lazyData.getClasspathEntryData()
val data = ClasspathEntryData.ClasspathEntrySerializer.loadFrom(outputs.single())
assertEquals(setOf("test/A", "test/B"), data.classAbiHash.keys)
assertEquals(setOf("test/A", "test/B"), data.classDependencies.keys)
assertEquals(emptySet<String>(), data.classDependencies["test/A"]!!.abiTypes)
@@ -71,10 +68,7 @@ class ClasspathAnalyzerTest {
val transform = StructureArtifactTransform().also { it.outputDirectory = tmp.newFolder() }
val outputs = transform.transform(inputJar)
val lazyData = LazyClasspathEntryData.LazyClasspathEntrySerializer.loadFromFile(outputs.single())
assertEquals(inputJar, lazyData.classpathEntry)
val data = lazyData.getClasspathEntryData()
val data = ClasspathEntryData.ClasspathEntrySerializer.loadFrom(outputs.single())
assertEquals(setOf("test/A", "test/B"), data.classAbiHash.keys)
assertEquals(setOf("test/A", "test/B"), data.classDependencies.keys)
assertEquals(emptySet<String>(), data.classDependencies["test/A"]!!.abiTypes)
@@ -90,10 +84,7 @@ class ClasspathAnalyzerTest {
val transform = StructureArtifactTransform().also { it.outputDirectory = tmp.newFolder() }
val outputs = transform.transform(inputDir)
val lazyData = LazyClasspathEntryData.LazyClasspathEntrySerializer.loadFromFile(outputs.single())
assertEquals(inputDir, lazyData.classpathEntry)
val data = lazyData.getClasspathEntryData()
val data = ClasspathEntryData.ClasspathEntrySerializer.loadFrom(outputs.single())
assertTrue(data.classAbiHash.isEmpty())
assertTrue(data.classDependencies.isEmpty())
}

View File

@@ -18,93 +18,80 @@ class ClasspathSnapshotTest {
@Test
fun testSerialization() {
val (firstJar, lazyData) = generateLazyData(
val data = generateStructureData(
ClassData("first/A"), ClassData("first/B")
)
val snapshotDir = tmp.newFolder()
val currentSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(snapshotDir, listOf(firstJar), setOf(lazyData))
assertEquals(KaptClasspathChanges.Unknown, currentSnapshot.diff(UnknownSnapshot, setOf(firstJar)))
val currentSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(snapshotDir, listOf(), setOf(data))
assertEquals(KaptClasspathChanges.Unknown, currentSnapshot.diff(UnknownSnapshot, setOf(data)))
currentSnapshot.writeToCache()
val loadedSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.loadFrom(snapshotDir)
val diff = loadedSnapshot.diff(currentSnapshot, setOf(firstJar)) as KaptClasspathChanges.Known
val diff = loadedSnapshot.diff(currentSnapshot, setOf(data)) as KaptClasspathChanges.Known
assertEquals(emptySet<String>(), diff.names)
}
@Test
fun testIncompatibleClasspaths() {
val firstSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(File(""), listOf(File("1.jar")), emptySet())
val firstSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(File(""), listOf(File("a.jar")), emptySet())
val secondSnapshot =
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(File(""), listOf(File("1.jar"), File("added.jar")), emptySet())
assertEquals(KaptClasspathChanges.Unknown, firstSnapshot.diff(secondSnapshot, setOf(File("added.jar"))))
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(File(""), listOf(File("b.jar")), setOf(File("")))
assertEquals(KaptClasspathChanges.Unknown, firstSnapshot.diff(secondSnapshot, emptySet()))
}
@Test
fun testChangedClassesFound() {
val (firstJar, firstLazyData) = generateLazyData(
val dataFile = generateStructureData(
ClassData("first/A"),
ClassData("first/B").also { it.withAbiDependencies("first/A") }
)
val firstSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(File(""), listOf(firstJar), setOf(firstLazyData))
firstSnapshot.diff(UnknownSnapshot, setOf(firstJar))
val firstSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(tmp.newFolder(), listOf(), setOf(dataFile))
firstSnapshot.writeToCache()
val (_, changedLazyData) = generateLazyData(
generateStructureData(
ClassData("first/A", ByteArray(1)),
ClassData("first/B").also { it.withAbiDependencies("first/A") },
jarInput = firstJar
outputFile = dataFile
)
val changedSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(File(""), listOf(firstJar), setOf(changedLazyData))
val changedSnapshot = ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(tmp.newFolder(), listOf(), setOf(dataFile))
val diff = changedSnapshot.diff(firstSnapshot, setOf(firstJar)) as KaptClasspathChanges.Known
val diff = changedSnapshot.diff(firstSnapshot, setOf(dataFile)) as KaptClasspathChanges.Known
assertEquals(setOf("first/A", "first/B"), diff.names)
}
@Test
fun testChangedClassesAcrossEntries() {
val (firstJar, firstLazyData) = generateLazyData(
val dataFile = generateStructureData(
ClassData("first/A").also { it.withAbiDependencies("library/C") },
ClassData("first/B").also { it.withAbiDependencies("first/A") }
)
val (libraryJar, libraryLazyData) = generateLazyData(ClassData("library/C"))
val libraryDataFile = generateStructureData(ClassData("library/C"))
val cacheDir = tmp.newFolder()
val firstSnapshot =
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(
cacheDir,
listOf(firstJar, libraryJar),
setOf(firstLazyData, libraryLazyData)
)
firstSnapshot.diff(UnknownSnapshot, setOf(firstJar, libraryJar))
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(cacheDir, listOf(), setOf(dataFile, libraryDataFile))
firstSnapshot.writeToCache()
val (_, changedLazyData) = generateLazyData(ClassData("library/C", ByteArray(1)), jarInput = libraryJar)
generateStructureData(ClassData("library/C", ByteArray(1)), outputFile = libraryDataFile)
val changedSnapshot =
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(
cacheDir,
listOf(firstJar, libraryJar),
setOf(firstLazyData, changedLazyData)
)
ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(cacheDir, listOf(), setOf(dataFile, libraryDataFile))
val diff = changedSnapshot.diff(firstSnapshot, setOf(libraryJar)) as KaptClasspathChanges.Known
val diff = changedSnapshot.diff(firstSnapshot, setOf(libraryDataFile)) as KaptClasspathChanges.Known
assertEquals(setOf("library/C", "first/A", "first/B"), diff.names)
}
private fun generateLazyData(
vararg classData: ClassData,
jarInput: File = tmp.newFile()
): Pair<File, File> {
private fun generateStructureData(vararg classData: ClassData, outputFile: File = tmp.newFile()): File {
val data = ClasspathEntryData()
classData.forEach {
data.classAbiHash[it.internalName] = it.hash
data.classDependencies[it.internalName] = ClassDependencies(it.abiDeps, it.privateDeps)
}
val serialized = tmp.newFile().also { data.saveTo(it) }
val lazyData = tmp.newFile().also { LazyClasspathEntryData(jarInput, serialized).saveToFile(it) }
data.saveTo(outputFile)
return Pair(jarInput, lazyData)
return outputFile
}
private class ClassData(

View File

@@ -65,7 +65,6 @@ open class ProcessorLoader(private val options: KaptOptions, private val logger:
val nonIncremental = processorNames.filter { !processorsInfo.containsKey(it) }
return if (nonIncremental.isNotEmpty()) {
logger.info("Incremental KAPT support is disabled. Processors that are not incremental: ${nonIncremental.joinToString()}.")
processors.map { IncrementalProcessor(it, DeclaredProcType.NON_INCREMENTAL) }
} else {
processors.map { IncrementalProcessor(it, processorsInfo.getValue(it.javaClass.name)) }

View File

@@ -13,10 +13,7 @@ import com.sun.tools.javac.processing.JavacFiler
import com.sun.tools.javac.processing.JavacProcessingEnvironment
import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.base.kapt3.KaptFlag
import org.jetbrains.kotlin.kapt3.base.incremental.DeclaredProcType
import org.jetbrains.kotlin.kapt3.base.incremental.GeneratedTypesTaskListener
import org.jetbrains.kotlin.kapt3.base.incremental.IncrementalProcessor
import org.jetbrains.kotlin.kapt3.base.incremental.MentionedTypesTaskListener
import org.jetbrains.kotlin.kapt3.base.incremental.*
import org.jetbrains.kotlin.kapt3.base.util.KaptBaseError
import org.jetbrains.kotlin.kapt3.base.util.isJava9OrLater
import org.jetbrains.kotlin.kapt3.base.util.measureTimeMillisWithResult
@@ -87,6 +84,20 @@ fun KaptContext.doAnnotationProcessing(
cacheManager?.updateCache(processors)
sourcesStructureListener?.let {
if (logger.isVerbose) {
logger.info("Analyzing sources structure took ${it.time}[ms].")
}
}
if (cacheManager != null && processors.any { it.getRuntimeType() == RuntimeProcType.NON_INCREMENTAL }) {
val nonIncremental =
processors.filter { it.getRuntimeType() == RuntimeProcType.NON_INCREMENTAL }.map { it.processorName }
logger.warn(
"Incremental annotation processing requested, but support is disabled because the following " +
"processors are not incremental: ${nonIncremental.joinToString()}."
)
}
val log = compilerAfterAP.log
val filer = processingEnvironment.filer as JavacFiler
@@ -123,7 +134,7 @@ private fun showProcessorTimings(wrappedProcessors: List<ProcessorWrapper>, logg
}
}
private class ProcessorWrapper(private val delegate: Processor) : Processor by delegate {
private class ProcessorWrapper(private val delegate: IncrementalProcessor) : Processor by delegate {
private var initTime: Long = 0
private val roundTime = mutableListOf<Long>()
@@ -136,7 +147,7 @@ private class ProcessorWrapper(private val delegate: Processor) : Processor by d
return result
}
override fun init(processingEnv: ProcessingEnvironment?) {
override fun init(processingEnv: ProcessingEnvironment) {
initTime += measureTimeMillis {
delegate.init(processingEnv)
}
@@ -161,7 +172,7 @@ private class ProcessorWrapper(private val delegate: Processor) : Processor by d
}
fun renderSpentTime(): String {
val processorName = delegate.javaClass.simpleName
val processorName = delegate.processorName
val totalTime = initTime + roundTime.sum()
return "$processorName: " +

View File

@@ -23,6 +23,8 @@ class IncrementalProcessor(private val processor: Processor, val kind: DeclaredP
private var dependencyCollector = lazy { createDependencyCollector() }
val processorName: String = processor.javaClass.name
override fun init(processingEnv: ProcessingEnvironment) {
if (kind == DeclaredProcType.NON_INCREMENTAL) {
processor.init(processingEnv)