Track changes for jar files for non-Android Gradle projects

This commit is contained in:
Alexey Tsvetkov
2018-05-16 23:34:43 +03:00
parent 61e330fa57
commit e5cdc64f4f
7 changed files with 119 additions and 33 deletions
@@ -22,7 +22,8 @@ data class IncrementalModuleEntry(
class IncrementalModuleInfo(
val projectRoot: File,
val dirToModule: Map<File, IncrementalModuleEntry>,
val nameToModules: Map<String, Set<IncrementalModuleEntry>>
val nameToModules: Map<String, Set<IncrementalModuleEntry>>,
val jarToClassListFile: Map<File, File>
) : Serializable {
companion object {
private const val serialVersionUID = 0L
@@ -48,8 +48,7 @@ import org.jetbrains.kotlin.daemon.report.RemoteICReporter
import org.jetbrains.kotlin.incremental.*
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.parsing.classesFqNames
import org.jetbrains.kotlin.incremental.multiproject.EmptyModulesApiHistory
import org.jetbrains.kotlin.incremental.multiproject.ModulesApiHistoryImpl
import org.jetbrains.kotlin.incremental.multiproject.ModulesApiHistoryJvm
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
import org.jetbrains.kotlin.modules.Module
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
@@ -524,9 +523,7 @@ class CompileServiceImpl(
workingDir,
enabled = true)
val modulesApiHistory = incrementalCompilationOptions.run {
ModulesApiHistoryImpl(modulesInfo)
}
val modulesApiHistory = ModulesApiHistoryJvm(incrementalCompilationOptions.modulesInfo)
val compiler = IncrementalJvmCompilerRunner(
workingDir,
@@ -21,10 +21,8 @@ object EmptyModulesApiHistory : ModulesApiHistory {
Either.Error("Multi-module IC is not configured")
}
class ModulesApiHistoryImpl(
private val modulesInfo: IncrementalModuleInfo
) : ModulesApiHistory {
private val projectRootPath = Paths.get(modulesInfo.projectRoot.absolutePath)
open class ModulesApiHistoryJvm(protected val modulesInfo: IncrementalModuleInfo) : ModulesApiHistory {
protected val projectRootPath: Path = Paths.get(modulesInfo.projectRoot.absolutePath)
private val dirToHistoryFileCache = HashMap<File, File?>()
override fun historyFilesForChangedFiles(changedFiles: Set<File>): Either<Set<File>> {
@@ -46,9 +44,9 @@ class ModulesApiHistoryImpl(
}
for (jar in jarFiles) {
val historyEither = getBuildHistoryForJar(jar)
val historyEither = getBuildHistoryFilesForJar(jar)
when (historyEither) {
is Either.Success<File> -> result.add(historyEither.value)
is Either.Success<Set<File>> -> result.addAll(historyEither.value)
is Either.Error -> return historyEither
}
}
@@ -78,9 +76,26 @@ class ModulesApiHistoryImpl(
}
}
private fun getBuildHistoryForJar(jar: File): Either<File> =
Either.Error("Cannot get changes for jar $jar")
protected open fun getBuildHistoryFilesForJar(jar: File): Either<Set<File>> {
val classListFile = modulesInfo.jarToClassListFile[jar] ?: return Either.Error("Unknown jar: $jar")
if (!classListFile.isFile) return Either.Error("Class list file does not exist $classListFile")
private fun Path.isParentOf(path: Path) = path.startsWith(this)
private fun Path.isParentOf(file: File) = this.isParentOf(Paths.get(file.absolutePath))
}
val classFiles = try {
classListFile.readText().split(File.pathSeparator).map(::File)
} catch (t: Throwable) {
return Either.Error("Could not read class list for $jar from $classListFile: $t")
}
val classFileDirs = classFiles.groupBy { it.parentFile }
val result = HashSet<File>()
for ((dir, files) in classFileDirs) {
val buildHistory = getBuildHistoryForDir(dir)
?: return Either.Error("Could not get build history for class files: ${files.joinToString()}")
result.add(buildHistory)
}
return Either.Success(result)
}
}
private fun Path.isParentOf(path: Path) = path.startsWith(this)
private fun Path.isParentOf(file: File) = this.isParentOf(Paths.get(file.absolutePath))
@@ -25,14 +25,14 @@ internal open class GradleCompilerEnvironment(
}
internal class GradleIncrementalCompilerEnvironment(
compilerClasspath: List<File>,
val changedFiles: ChangedFiles,
val workingDir: File,
messageCollector: GradleMessageCollector,
outputItemsCollector: OutputItemsCollector,
compilerArgs: CommonCompilerArguments,
val buildHistoryFile: File,
val kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null,
val usePreciseJavaTracking: Boolean = false,
val localStateDirs: List<File> = emptyList()
compilerClasspath: List<File>,
val changedFiles: ChangedFiles,
val workingDir: File,
messageCollector: GradleMessageCollector,
outputItemsCollector: OutputItemsCollector,
compilerArgs: CommonCompilerArguments,
val buildHistoryFile: File,
val kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null,
val usePreciseJavaTracking: Boolean = false,
val localStateDirs: List<File> = emptyList()
) : GradleCompilerEnvironment(compilerClasspath, messageCollector, outputItemsCollector, compilerArgs)
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.daemon.common.IncrementalModuleEntry
import org.jetbrains.kotlin.daemon.common.IncrementalModuleInfo
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
import org.jetbrains.kotlin.gradle.tasks.InspectClassesForMultiModuleIC
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.incremental.*
import java.io.ByteArrayOutputStream
@@ -356,17 +357,25 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
val dirToModule = HashMap<File, IncrementalModuleEntry>()
val nameToModules = HashMap<String, HashSet<IncrementalModuleEntry>>()
val jarToClassListFile = HashMap<File, File>()
for (project in gradle.rootProject.allprojects) {
for (task in project.tasks.withType(KotlinCompile::class.java)) {
val module = IncrementalModuleEntry(project.path, task.moduleName, project.buildDir, task.buildHistoryFile)
dirToModule[task.destinationDir] = module
task.javaOutputDir?.let { dirToModule[it] = module }
nameToModules.getOrPut(module.name) { HashSet() }.add(module)
for (task in project.tasks) {
when (task) {
is KotlinCompile -> {
val module = IncrementalModuleEntry(project.path, task.moduleName, project.buildDir, task.buildHistoryFile)
dirToModule[task.destinationDir] = module
task.javaOutputDir?.let { dirToModule[it] = module }
nameToModules.getOrPut(module.name) { HashSet() }.add(module)
}
is InspectClassesForMultiModuleIC -> {
jarToClassListFile[File(task.archivePath)] = task.classesListFile
}
}
}
}
return IncrementalModuleInfo(gradle.rootProject.projectDir, dirToModule, nameToModules)
return IncrementalModuleInfo(gradle.rootProject.projectDir, dirToModule, nameToModules, jarToClassListFile)
.also {
cachedGradle = WeakReference(gradle)
cachedModulesInfo = it
@@ -23,6 +23,7 @@ import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.*
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl
import org.jetbrains.kotlin.gradle.internal.*
@@ -358,6 +359,7 @@ internal abstract class AbstractKotlinPlugin(
configureSourceSetDefaults(project, javaBasePlugin, javaPluginConvention)
configureDefaultVersionsResolutionStrategy(project)
configureClassInspectionForIC(project)
}
open protected fun configureSourceSetDefaults(
@@ -390,6 +392,24 @@ internal abstract class AbstractKotlinPlugin(
}
}
}
private fun configureClassInspectionForIC(project: Project) {
val classesTask = project.tasks.findByName(JavaPlugin.CLASSES_TASK_NAME)
val jarTask = project.tasks.findByName(JavaPlugin.JAR_TASK_NAME)
if (classesTask == null || jarTask !is Jar) {
project.logger.info(
"Could not configure class inspection task " +
"(classes task = ${classesTask?.javaClass?.canonicalName}, " +
"jar task = ${classesTask?.javaClass?.canonicalName}"
)
return
}
val inspectTask = project.tasks.create("inspectClassesForKotlinIC", InspectClassesForMultiModuleIC::class.java)
inspectTask.jarTask = jarTask
inspectTask.dependsOn(classesTask)
jarTask.dependsOn(inspectTask)
}
}
internal open class KotlinPlugin(
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.*
import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.gradle.utils.inputsCompatible
import org.jetbrains.kotlin.incremental.isClassFile
import java.io.File
internal open class InspectClassesForMultiModuleIC : DefaultTask() {
@get:Internal
lateinit var jarTask: Jar
@Suppress("MemberVisibilityCanBePrivate")
@get:OutputFile
internal val classesListFile: File
get() = File(File(project.buildDir, KOTLIN_BUILD_DIR_NAME), "${sanitizeFileName(jarTask.archiveName)}-classes.txt")
@Suppress("MemberVisibilityCanBePrivate")
@get:InputFiles
internal val classFiles: FileCollection
get() = jarTask.inputsCompatible.files.filter { it.isClassFile() }
@get:Input
internal val archivePath: String
get() = jarTask.archivePath.canonicalPath
@TaskAction
fun run() {
classesListFile.parentFile.mkdirs()
val paths = classFiles.map { it.canonicalPath }.sorted()
val text = paths.joinToString(File.pathSeparator)
classesListFile.writeText(text)
}
private fun sanitizeFileName(candidate: String): String =
candidate.filter { it.isLetterOrDigit() }
}