From e5cdc64f4f7003ca8f5a9f3c9441e39ecd9bdedf Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Wed, 16 May 2018 23:34:43 +0300 Subject: [PATCH] Track changes for jar files for non-Android Gradle projects --- .../daemon/common/IncrementalModuleInfo.kt | 3 +- .../kotlin/daemon/CompileServiceImpl.kt | 7 +-- .../multiproject/ModulesApiHistory.kt | 37 +++++++++++----- .../GradleCompilerEnvironment.kt | 20 ++++----- .../GradleKotlinCompilerRunner.kt | 21 ++++++--- .../kotlin/gradle/plugin/KotlinPlugin.kt | 20 +++++++++ .../gradle/tasks/UpdateKotlinCachesTask.kt | 44 +++++++++++++++++++ 7 files changed, 119 insertions(+), 33 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/UpdateKotlinCachesTask.kt diff --git a/compiler/daemon/daemon-common/src/org/jetbrains/kotlin/daemon/common/IncrementalModuleInfo.kt b/compiler/daemon/daemon-common/src/org/jetbrains/kotlin/daemon/common/IncrementalModuleInfo.kt index 2410b8d8d81..1848e622986 100644 --- a/compiler/daemon/daemon-common/src/org/jetbrains/kotlin/daemon/common/IncrementalModuleInfo.kt +++ b/compiler/daemon/daemon-common/src/org/jetbrains/kotlin/daemon/common/IncrementalModuleInfo.kt @@ -22,7 +22,8 @@ data class IncrementalModuleEntry( class IncrementalModuleInfo( val projectRoot: File, val dirToModule: Map, - val nameToModules: Map> + val nameToModules: Map>, + val jarToClassListFile: Map ) : Serializable { companion object { private const val serialVersionUID = 0L diff --git a/compiler/daemon/src/org/jetbrains/kotlin/daemon/CompileServiceImpl.kt b/compiler/daemon/src/org/jetbrains/kotlin/daemon/CompileServiceImpl.kt index 96013541e1a..444ec073e5d 100644 --- a/compiler/daemon/src/org/jetbrains/kotlin/daemon/CompileServiceImpl.kt +++ b/compiler/daemon/src/org/jetbrains/kotlin/daemon/CompileServiceImpl.kt @@ -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, diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/multiproject/ModulesApiHistory.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/multiproject/ModulesApiHistory.kt index 36f8f08b14f..d36e1827a54 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/multiproject/ModulesApiHistory.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/multiproject/ModulesApiHistory.kt @@ -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() override fun historyFilesForChangedFiles(changedFiles: Set): Either> { @@ -46,9 +44,9 @@ class ModulesApiHistoryImpl( } for (jar in jarFiles) { - val historyEither = getBuildHistoryForJar(jar) + val historyEither = getBuildHistoryFilesForJar(jar) when (historyEither) { - is Either.Success -> result.add(historyEither.value) + is Either.Success> -> result.addAll(historyEither.value) is Either.Error -> return historyEither } } @@ -78,9 +76,26 @@ class ModulesApiHistoryImpl( } } - private fun getBuildHistoryForJar(jar: File): Either = - Either.Error("Cannot get changes for jar $jar") + protected open fun getBuildHistoryFilesForJar(jar: File): Either> { + 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)) -} \ No newline at end of file + 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() + 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)) \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilerEnvironment.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilerEnvironment.kt index 1fc188fb11e..daa6a94ea15 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilerEnvironment.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilerEnvironment.kt @@ -25,14 +25,14 @@ internal open class GradleCompilerEnvironment( } internal class GradleIncrementalCompilerEnvironment( - compilerClasspath: List, - 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 = emptyList() + compilerClasspath: List, + 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 = emptyList() ) : GradleCompilerEnvironment(compilerClasspath, messageCollector, outputItemsCollector, compilerArgs) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt index ee8d153a854..8d47931d1fc 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt @@ -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() val nameToModules = HashMap>() + val jarToClassListFile = HashMap() 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 diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt index 3326746f13e..63ddf2860ac 100755 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt @@ -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( diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/UpdateKotlinCachesTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/UpdateKotlinCachesTask.kt new file mode 100644 index 00000000000..ff963b660df --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/UpdateKotlinCachesTask.kt @@ -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() } +} \ No newline at end of file