Track changes in generated source files

#KT-12962 fixed
This commit is contained in:
Alexey Tsvetkov
2016-07-25 20:55:44 +03:00
parent 7fe29a7e55
commit 2e3b3da029
9 changed files with 105 additions and 14 deletions
@@ -203,6 +203,12 @@ open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments>() {
logger.kotlinDebug { "friendTaskName = $friendTaskName" }
friendTaskName?.let { addFriendPathForTestTask(it) }
logger.kotlinDebug("args.moduleName = ${args.moduleName}")
fun dumpPaths(files: Iterable<File>): String =
"[${files.map { it.canonicalPath }.sorted().joinToString(prefix = "\n\t", separator = ",\n\t")}]"
logger.kotlinDebug { "$name source roots: ${dumpPaths(sourceRoots)}" }
logger.kotlinDebug { "$name java source roots: ${dumpPaths(getJavaSourceRoots())}" }
}
override fun callCompiler(args: K2JVMCompilerArguments, sources: List<File>, isIncrementalRequested: Boolean, modified: List<File>, removed: List<File>) {
@@ -21,6 +21,14 @@ import org.gradle.api.Project
import org.gradle.api.UnknownDomainObjectException
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.compile.JavaCompile
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.com.intellij.lang.Language
import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer
import org.jetbrains.kotlin.com.intellij.psi.PsiFileFactory
import org.jetbrains.kotlin.com.intellij.psi.PsiJavaFile
import org.jetbrains.kotlin.com.intellij.psi.impl.PsiFileFactoryImpl
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
@@ -57,6 +65,10 @@ fun Project.initKapt(
kotlinAfterJavaTask.source(kaptManager.generatedKotlinSourceDir)
kotlinAfterJavaTask.source(kaptManager.aptOutputDir)
subpluginEnvironment.addSubpluginArguments(this, kotlinAfterJavaTask)
javaTask.doLast {
moveGeneratedJavaFilesToCorrespondingDirectories(kaptManager.aptOutputDir)
}
} else {
kotlinAfterJavaTask = null
kotlinTask.logger.kotlinDebug("kapt: Class file stubs are not used")
@@ -336,4 +348,34 @@ public class AnnotationProcessingManager(
project.logger.kotlinDebug("kapt: Discovered annotation processors: ${annotationProcessors.joinToString()}")
return annotationProcessors
}
}
// Java files can be generated anywhere, but
// Kotlin searches for java fq-name only in directory corresponding to package.
// Previously this worked because generated files were added to classpath.
// However in that case incremental compilation worked unreliable with generated files.
// The solution is to post-process generated java files and move them to corresponding packages
fun moveGeneratedJavaFilesToCorrespondingDirectories(generatedJavaSourceRoot: File) {
val javaFiles = generatedJavaSourceRoot.walk().filter { it.extension.equals("java", ignoreCase = true) }.toList()
if (javaFiles.isEmpty()) return
val rootDisposable = Disposer.newDisposable()
val configuration = CompilerConfiguration()
val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
val project = environment.project
val psiFileFactory = PsiFileFactory.getInstance(project) as PsiFileFactoryImpl
for (javaFile in javaFiles) {
val psiFile = psiFileFactory.createFileFromText(javaFile.nameWithoutExtension, Language.findLanguageByID("JAVA")!!, javaFile.readText())
val packageName = (psiFile as? PsiJavaFile)?.packageName ?: continue
val expectedDir = File(generatedJavaSourceRoot, packageName.replace('.', '/'))
val expectedFile = File(expectedDir, javaFile.name)
if (javaFile != expectedFile) {
expectedFile.parentFile.mkdirs()
javaFile.copyTo(expectedFile, overwrite = true)
javaFile.delete()
}
}
}
@@ -70,7 +70,7 @@ abstract class KotlinSourceSetProcessor<T : AbstractCompile>(
if (kotlinSourceSet == null || kotlinDirSet == null) {
return
}
addSourcesToKotlinDirSet()
addKotlinDirSetToSources()
commonTaskConfiguration()
doTargetSpecificProcessing()
}
@@ -93,10 +93,10 @@ abstract class KotlinSourceSetProcessor<T : AbstractCompile>(
return kotlinDirSet
}
open protected fun addSourcesToKotlinDirSet() {
private fun addKotlinDirSetToSources() {
logger.kotlinDebug("Adding Kotlin SourceDirectorySet $kotlinDirSet to source set $sourceSet")
sourceSet.getAllJava()?.source(kotlinDirSet)
sourceSet.getAllSource()?.source(kotlinDirSet)
sourceSet.allJava?.source(kotlinDirSet)
sourceSet.allSource?.source(kotlinDirSet)
sourceSet.resources?.filter?.exclude { kotlinDirSet!!.contains(it.file) }
}
@@ -152,7 +152,7 @@ class Kotlin2JvmSourceSetProcessor(
if (project != null) {
kotlinTask.destinationDir = File(project.buildDir, "kotlin-classes/$sourceSetName")
for (dir in sourceSet.getJava().srcDirs) {
for (dir in sourceSet.java.srcDirs) {
kotlinDirSet?.srcDir(dir)
}
@@ -175,6 +175,7 @@ class Kotlin2JvmSourceSetProcessor(
kotlinAfterJavaTask = project.initKapt(kotlinTask, javaTask, kaptManager, sourceSetName, null, subpluginEnvironment, tasksProvider)
}
kotlinAfterJavaTask?.let { it.source(kotlinDirSet) }
configureJavaTask(kotlinTask, javaTask, kotlinAfterJavaTask, logger)
createSyncOutputTask(project, kotlinTask, javaTask, kotlinAfterJavaTask, sourceSetName)
}
@@ -142,13 +142,34 @@ fun getSomething() = 10
assertSuccessful()
}
val file = project.projectDir.getFileByName("AndroidModule.kt")
file.modify { it.replace("fun provideApplicationContext(): Context {",
"fun provideApplicationContext(): Context? {") }
val androidModuleKt = project.projectDir.getFileByName("AndroidModule.kt")
androidModuleKt.modify { it.replace("fun provideApplicationContext(): Context {",
"fun provideApplicationContext(): Context? {") }
// rebuilt because DaggerApplicationComponent.java was regenerated
val baseApplicationKt = project.projectDir.getFileByName("BaseApplication.kt")
// rebuilt because BuildConfig.java was regenerated (timestamp was changed)
val useBuildConfigJavaKt = project.projectDir.getFileByName("useBuildConfigJava.kt")
val stringsXml = project.projectDir.getFileByName("strings.xml")
stringsXml.modify { """
<resources>
<string name="app_name">kotlin</string>
<string name="app_name1">kotlin1</string>
</resources>
""" }
// rebuilt because R.java changed
val homeActivityKt = project.projectDir.getFileByName("HomeActivity.kt")
val useRJavaActivity = project.projectDir.getFileByName("UseRJavaActivity.kt")
project.build(":app:assembleDebug", options = options) {
assertSuccessful()
assertCompiledKotlinSources(project.relativizeToSubproject("app", file))
assertCompiledKotlinSources(project.relativizeToSubproject("app",
androidModuleKt,
baseApplicationKt,
useBuildConfigJavaKt,
homeActivityKt,
useRJavaActivity
))
}
}
@@ -64,7 +64,7 @@ class KaptIT: BaseGradleIT() {
assertContains(":compileKotlin")
assertContains(":compileJava")
assertFileExists("build/tmp/kapt/main/wrappers/annotations.main.txt")
assertFileExists("build/generated/source/kapt/main/TestClassGenerated.java")
assertFileExists("build/generated/source/kapt/main/example/TestClassGenerated.java")
assertFileExists("build/classes/main/example/TestClass.class")
assertFileExists("build/classes/main/example/TestClassGenerated.class")
assertFileExists("build/classes/main/example/SourceAnnotatedTestClassGenerated.class")
@@ -149,7 +149,7 @@ class KaptIT: BaseGradleIT() {
assertContains(":compileKotlin")
assertContains(":compileJava")
assertFileExists("build/tmp/kapt/main/wrappers/annotations.main.txt")
assertFileExists("build/generated/source/kapt/main/TestClassCustomized.java")
assertFileExists("build/generated/source/kapt/main/example/TestClassCustomized.java")
assertFileExists("build/classes/main/example/TestClass.class")
assertFileExists("build/classes/main/example/TestClassCustomized.class")
}
@@ -174,7 +174,7 @@ class KaptIT: BaseGradleIT() {
assertContains(":compileKotlin")
assertContains(":compileJava")
assertFileExists("build/tmp/kapt/main/wrappers/annotations.main.txt")
assertFileExists("build/generated/source/kapt/main/TestClassCustomized.java")
assertFileExists("build/generated/source/kapt/main/example/TestClassCustomized.java")
assertFileExists("build/tmp/kapt/main/kotlinGenerated/TestClass.kt")
assertFileExists("build/classes/main/example/TestClass.class")
assertFileExists("build/classes/main/example/TestClassCustomized.class")
@@ -133,8 +133,14 @@ abstract class KaptIncrementalBaseIT(val shouldUseStubs: Boolean): BaseGradleIT(
project.build("build") {
assertSuccessful()
val useBKt = project.projectDir.getFileByName("useB.kt")
assertCompiledKotlinSources(project.relativize(bKt, useBKt))
if (shouldUseStubs) {
// java removal is detected
assertCompiledKotlinSources(project.relativize(project.projectDir.allKotlinFiles()))
}
else {
val useBKt = project.projectDir.getFileByName("useB.kt")
assertCompiledKotlinSources(project.relativize(bKt, useBKt))
}
checkGenerated(*(annotatedElements.toSet() - affectedElements).toTypedArray())
checkNotGenerated(*affectedElements)
}
@@ -29,6 +29,7 @@ android {
targetSdkVersion 23
versionCode 1
versionName "1.0"
buildConfigField "long", "BUILD_TIME_MILLIS", "${System.currentTimeMillis()}L"
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
@@ -0,0 +1,9 @@
package com.example.dagger.kotlin
import android.app.Activity
class UseRJavaActivity : Activity() {
fun useRJava() {
val app_name = getResources().getString(R.string.app_name)
}
}
@@ -0,0 +1,5 @@
package com.example.dagger.kotlin
fun useBuildConfigJava() {
if (BuildConfig.APPLICATION_ID != "com.example.dagger.kotlin") throw AssertionError()
}