Add test to validate incremental changes in compiler plugin.
If compiler plugin was recompiled incrementally, other projects in the same build should pick it up and regenerate sources. ^KT-38570 Fixed
This commit is contained in:
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle
|
||||
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.kotlin.gradle.testbase.*
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import kotlin.io.path.exists
|
||||
|
||||
@DisplayName("Compiler plugin incremental compilation")
|
||||
@OtherGradlePluginTests
|
||||
class CompilerPluginsIncrementalIT : KGPBaseTest() {
|
||||
override val defaultBuildOptions: BuildOptions
|
||||
get() = super.defaultBuildOptions.copy(
|
||||
incremental = true
|
||||
)
|
||||
|
||||
@DisplayName("KT-38570: After changing compiler plugin code, next incremental build picks it up")
|
||||
@GradleTest
|
||||
internal fun afterChangeInPluginBuildDoesIncrementalProcessing(gradleVersion: GradleVersion) {
|
||||
project("incrementalChangeInPlugin".prefix, gradleVersion) {
|
||||
build("assemble")
|
||||
|
||||
val genDir = subProject("library").projectPath.resolve("build/sample-dir/plugin/test/gen")
|
||||
assert(genDir.exists()) { "$genDir does not exists!" }
|
||||
|
||||
val generatedFiles = genDir.toFile()
|
||||
.walkTopDown()
|
||||
.asSequence()
|
||||
.filterNot { it.isDirectory }
|
||||
.associate {
|
||||
it.absolutePath to it.readBytes()
|
||||
}
|
||||
|
||||
subProject("plugin")
|
||||
.kotlinSourcesDir()
|
||||
.resolve("test/compiler/plugin/TestComponentRegistrar.kt")
|
||||
.modify {
|
||||
it.replace("world.", "something else.")
|
||||
}
|
||||
|
||||
build("assemble") {
|
||||
assert(genDir.exists()) { "$genDir does not exists!" }
|
||||
}
|
||||
|
||||
genDir.toFile()
|
||||
.walkTopDown()
|
||||
.asSequence()
|
||||
.filterNot { it.isDirectory }
|
||||
.forEach {
|
||||
assert(!it.readBytes().contentEquals(generatedFiles[it.absolutePath])) {
|
||||
"""
|
||||
|
||||
File ${it.absolutePath} content is equals to previous one!
|
||||
New content:
|
||||
${it.readText()}
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val String.prefix get() = "compilerPlugins/$this"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.jetbrains.kotlin.jvm'
|
||||
}
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
kotlinCompilerPluginClasspath project(':plugin')
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package library
|
||||
|
||||
class SomeClass
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package library
|
||||
|
||||
interface SomeInterface
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-gradle-plugin-api"
|
||||
|
||||
compileOnly "org.jetbrains.kotlin:kotlin-compiler-embeddable"
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package test.compiler.plugin
|
||||
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult.RetryWithAdditionalRoots
|
||||
import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
|
||||
import org.jetbrains.kotlin.cli.common.config.kotlinSourceRoots
|
||||
import org.jetbrains.kotlin.com.intellij.mock.MockProject
|
||||
import org.jetbrains.kotlin.com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
|
||||
import java.io.File
|
||||
|
||||
class TestComponentRegistrar : ComponentRegistrar {
|
||||
override fun registerProjectComponents(
|
||||
project: MockProject,
|
||||
configuration: CompilerConfiguration
|
||||
) {
|
||||
val kotlinSourceRoots = configuration.kotlinSourceRoots.ifEmpty { return }
|
||||
val sourceGenFolder = createSourceGenFolder(kotlinSourceRoots)
|
||||
|
||||
var counter = 0
|
||||
|
||||
AnalysisHandlerExtension.registerExtension(project, object : AnalysisHandlerExtension {
|
||||
private var didRecompile = false
|
||||
|
||||
override fun analysisCompleted(
|
||||
project: Project,
|
||||
module: ModuleDescriptor,
|
||||
bindingTrace: BindingTrace,
|
||||
files: Collection<KtFile>
|
||||
): AnalysisResult? {
|
||||
if (didRecompile) return null
|
||||
didRecompile = true
|
||||
|
||||
val classes = files
|
||||
.flatMap { ktFile ->
|
||||
val packageName = ktFile.packageFqName.asString()
|
||||
ktFile.findChildrenByClass(KtClassOrObject::class.java)
|
||||
.map { packageName + "." + it.nameAsSafeName.asString() }
|
||||
}
|
||||
.mapNotNull {
|
||||
module.findClassAcrossModuleDependencies(ClassId.topLevel(FqName(it)))
|
||||
}
|
||||
|
||||
// Nothing to do.
|
||||
if (classes.isEmpty()) return null
|
||||
|
||||
classes.forEach { classDescriptor ->
|
||||
val className = classDescriptor.name.asString()
|
||||
|
||||
val directory = File(sourceGenFolder, "plugin/test/gen")
|
||||
val file = File(directory, "$className.kt")
|
||||
.also {
|
||||
check(it.parentFile.exists() || it.parentFile.mkdirs()) {
|
||||
"Could not generate package directory: $this"
|
||||
}
|
||||
}
|
||||
|
||||
file.writeText(
|
||||
"""
|
||||
package plugin.test.gen
|
||||
|
||||
val hello${counter++} = "world."
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
// This restarts the analysis phase and will include our file.
|
||||
return RetryWithAdditionalRoots(
|
||||
bindingTrace.bindingContext, module, emptyList(), listOf(sourceGenFolder), addToEnvironment = true
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun createSourceGenFolder(kotlinSourceRoots: List<KotlinSourceRoot>): File {
|
||||
fun sampleDir(parent: File): File = File(parent, "sample-dir").also {
|
||||
check(it.exists() || it.mkdirs()) {
|
||||
"Could not create source generation directory: $it"
|
||||
}
|
||||
}
|
||||
|
||||
val oneSourceFile = kotlinSourceRoots.first().path.let { File(it) }
|
||||
val parentSequence = generateSequence(oneSourceFile) { it.parentFile }
|
||||
|
||||
// Try to find the src dir.
|
||||
parentSequence.firstOrNull { it.name == "src" }
|
||||
?.let { return sampleDir(File(it.parentFile, "build")) }
|
||||
|
||||
// If the src dir is not part of the input (incremental build), look for the build dir
|
||||
// directly.
|
||||
parentSequence.firstOrNull { it.name == "build" }
|
||||
?.let { return sampleDir(it) }
|
||||
|
||||
throw IllegalStateException(
|
||||
"Could not create source generation directory: $oneSourceFile"
|
||||
)
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#
|
||||
# Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
# Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
#
|
||||
|
||||
test.compiler.plugin.TestComponentRegistrar
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
include ':plugin'
|
||||
include ':library'
|
||||
Reference in New Issue
Block a user