Print recompilation reasons in build report

This commit is contained in:
Alexey Tsvetkov
2019-01-29 02:41:50 +03:00
parent 47bb938b94
commit 01c23510c7
6 changed files with 55 additions and 23 deletions
@@ -25,6 +25,20 @@ interface ICReporter {
fun reportCompileIteration(incremental: Boolean, sourceFiles: Collection<File>, exitCode: ExitCode) {} fun reportCompileIteration(incremental: Boolean, sourceFiles: Collection<File>, exitCode: ExitCode) {}
fun reportMarkDirtyClass(affectedFiles: Iterable<File>, classFqName: String) {
reportMarkDirty(affectedFiles, "dirty class $classFqName")
}
fun reportMarkDirtyMember(affectedFiles: Iterable<File>, scope: String, name: String) {
reportMarkDirty(affectedFiles, "dirty member $scope#$name")
}
fun reportMarkDirty(affectedFiles: Iterable<File>, reason: String) {
affectedFiles.forEach { file ->
reportVerbose { "${pathsAsString(file)} is marked dirty: $reason" }
}
}
fun pathsAsString(files: Iterable<File>): String = fun pathsAsString(files: Iterable<File>): String =
files.joinToString { it.canonicalPath } files.joinToString { it.canonicalPath }
@@ -31,8 +31,10 @@ import org.jetbrains.kotlin.modules.TargetId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.progress.CompilationCanceledStatus import org.jetbrains.kotlin.progress.CompilationCanceledStatus
import org.jetbrains.kotlin.synthetic.SAM_LOOKUP_NAME import org.jetbrains.kotlin.synthetic.SAM_LOOKUP_NAME
import org.jetbrains.kotlin.utils.addToStdlib.flattenTo
import java.io.File import java.io.File
import java.util.* import java.util.*
import kotlin.collections.HashSet
const val DELETE_MODULE_FILE_PROPERTY = "kotlin.delete.module.file.after.build" const val DELETE_MODULE_FILE_PROPERTY = "kotlin.delete.module.file.after.build"
@@ -143,8 +145,7 @@ fun ChangesCollector.getDirtyData(
val name = classFqName.shortName().identifier val name = classFqName.shortName().identifier
dirtyLookupSymbols.add(LookupSymbol(name, scope)) dirtyLookupSymbols.add(LookupSymbol(name, scope))
} }
} } else if (change is ChangeInfo.MembersChanged) {
else if (change is ChangeInfo.MembersChanged) {
val fqNames = withSubtypes(change.fqName, caches) val fqNames = withSubtypes(change.fqName, caches)
// need to recompile subtypes because changed member might break override // need to recompile subtypes because changed member might break override
dirtyClassesFqNames.addAll(fqNames) dirtyClassesFqNames.addAll(fqNames)
@@ -161,16 +162,16 @@ fun ChangesCollector.getDirtyData(
} }
fun mapLookupSymbolsToFiles( fun mapLookupSymbolsToFiles(
lookupStorage: LookupStorage, lookupStorage: LookupStorage,
lookupSymbols: Iterable<LookupSymbol>, lookupSymbols: Iterable<LookupSymbol>,
reporter: ICReporter, reporter: ICReporter,
excludes: Set<File> = emptySet() excludes: Set<File> = emptySet()
): Set<File> { ): Set<File> {
val dirtyFiles = HashSet<File>() val dirtyFiles = HashSet<File>()
for (lookup in lookupSymbols) { for (lookup in lookupSymbols) {
val affectedFiles = lookupStorage.get(lookup).map(::File).filter { it !in excludes } val affectedFiles = lookupStorage.get(lookup).map(::File).filter { it !in excludes }
reporter.report { "${lookup.scope}#${lookup.name} caused recompilation of: ${reporter.pathsAsString(affectedFiles)}" } reporter.reportMarkDirtyMember(affectedFiles, scope = lookup.scope, name = lookup.name)
dirtyFiles.addAll(affectedFiles) dirtyFiles.addAll(affectedFiles)
} }
@@ -183,19 +184,22 @@ fun mapClassesFqNamesToFiles(
reporter: ICReporter, reporter: ICReporter,
excludes: Set<File> = emptySet() excludes: Set<File> = emptySet()
): Set<File> { ): Set<File> {
val dirtyFiles = HashSet<File>() val fqNameToAffectedFiles = HashMap<FqName, MutableSet<File>>()
for (cache in caches) { for (cache in caches) {
for (dirtyClassFqName in classesFqNames) { for (classFqName in classesFqNames) {
val srcFile = cache.getSourceFileIfClass(dirtyClassFqName) val srcFile = cache.getSourceFileIfClass(classFqName)
if (srcFile == null || srcFile in excludes || srcFile.isJavaFile()) continue if (srcFile == null || srcFile in excludes || srcFile.isJavaFile()) continue
reporter.report { "Class $dirtyClassFqName caused recompilation of: ${reporter.pathsAsString(srcFile)}" } fqNameToAffectedFiles.getOrPut(classFqName) { HashSet() }.add(srcFile)
dirtyFiles.add(srcFile)
} }
} }
return dirtyFiles for ((classFqName, affectedFiles) in fqNameToAffectedFiles) {
reporter.reportMarkDirtyClass(affectedFiles, classFqName.asString())
}
return fqNameToAffectedFiles.values.flattenTo(HashSet())
} }
fun withSubtypes( fun withSubtypes(
@@ -34,6 +34,7 @@ internal class RemoteICReporter(
CompilationResultCategory.IC_COMPILE_ITERATION.code in compilationOptions.requestedCompilationResults CompilationResultCategory.IC_COMPILE_ITERATION.code in compilationOptions.requestedCompilationResults
private val shouldReportICLog = CompilationResultCategory.IC_LOG.code in compilationOptions.requestedCompilationResults private val shouldReportICLog = CompilationResultCategory.IC_LOG.code in compilationOptions.requestedCompilationResults
private val icLogLines = arrayListOf<String>() private val icLogLines = arrayListOf<String>()
private val recompilationReason = HashMap<File, String>()
override fun report(message: () -> String) { override fun report(message: () -> String) {
reportImpl(isMessageVerbose = false, message = message) reportImpl(isMessageVerbose = false, message = message)
@@ -64,9 +65,18 @@ internal class RemoteICReporter(
} }
if (shouldReportICLog && incremental) { if (shouldReportICLog && incremental) {
icLogLines.add("Compile iteration:") icLogLines.add("Compile iteration:")
sourceFiles.relativePaths(rootDir).forEach { sourceFiles.relativePaths(rootDir).forEach { file ->
icLogLines.add(" $it") val reason = recompilationReason[file]?.let { " <- $it" } ?: ""
icLogLines.add(" $file$reason")
} }
recompilationReason.clear()
}
}
override fun reportMarkDirty(affectedFiles: Iterable<File>, reason: String) {
super.reportMarkDirty(affectedFiles, reason)
if (shouldReportICLog) {
affectedFiles.forEach { recompilationReason[it] = reason }
} }
} }
@@ -18,10 +18,13 @@ class DirtyFilesContainer(
fun toMutableList(): MutableList<File> = fun toMutableList(): MutableList<File> =
ArrayList(myDirtyFiles) ArrayList(myDirtyFiles)
fun add(files: Iterable<File>) { fun add(files: Iterable<File>, reason: String?) {
val existingKotlinFiles = files.filter { it.isKotlinFile(sourceFilesExtensions) } val existingKotlinFiles = files.filter { it.isKotlinFile(sourceFilesExtensions) }
if (existingKotlinFiles.isNotEmpty()) { if (existingKotlinFiles.isNotEmpty()) {
myDirtyFiles.addAll(existingKotlinFiles) myDirtyFiles.addAll(existingKotlinFiles)
if (reason != null) {
reporter.reportMarkDirty(existingKotlinFiles, reason)
}
} }
} }
@@ -29,7 +32,8 @@ class DirtyFilesContainer(
if (lookupSymbols.isEmpty()) return if (lookupSymbols.isEmpty()) return
val dirtyFilesFromLookups = mapLookupSymbolsToFiles(caches.lookupCache, lookupSymbols, reporter) val dirtyFilesFromLookups = mapLookupSymbolsToFiles(caches.lookupCache, lookupSymbols, reporter)
add(dirtyFilesFromLookups) // reason is null, because files are reported in mapLookupSymbolsToFiles
add(dirtyFilesFromLookups, reason = null)
} }
fun addByDirtyClasses(dirtyClassesFqNames: Collection<FqName>) { fun addByDirtyClasses(dirtyClassesFqNames: Collection<FqName>) {
@@ -43,6 +47,7 @@ class DirtyFilesContainer(
} }
val dirtyFilesFromFqNames = val dirtyFilesFromFqNames =
mapClassesFqNamesToFiles(listOf(caches.platformCache), fqNamesWithSubtypes, reporter) mapClassesFqNamesToFiles(listOf(caches.platformCache), fqNamesWithSubtypes, reporter)
add(dirtyFilesFromFqNames) // reason is null, because files are reported in mapClassesFqNamesToFiles
add(dirtyFilesFromFqNames, reason = null)
} }
} }
@@ -139,16 +139,16 @@ abstract class IncrementalCompilerRunner<
protected abstract fun calculateSourcesToCompile(caches: CacheManager, changedFiles: ChangedFiles.Known, args: Args): CompilationMode protected abstract fun calculateSourcesToCompile(caches: CacheManager, changedFiles: ChangedFiles.Known, args: Args): CompilationMode
protected fun initDirtyFiles(dirtyFiles: DirtyFilesContainer, changedFiles: ChangedFiles.Known) { protected fun initDirtyFiles(dirtyFiles: DirtyFilesContainer, changedFiles: ChangedFiles.Known) {
dirtyFiles.add(changedFiles.modified) dirtyFiles.add(changedFiles.modified, "was modified since last time")
dirtyFiles.add(changedFiles.removed) dirtyFiles.add(changedFiles.removed, "was removed since last time")
if (dirtySourcesSinceLastTimeFile.exists()) { if (dirtySourcesSinceLastTimeFile.exists()) {
val files = dirtySourcesSinceLastTimeFile.readLines().map(::File) val files = dirtySourcesSinceLastTimeFile.readLines().map(::File)
if (files.isNotEmpty()) { if (files.isNotEmpty()) {
reporter.report { "Source files added since last compilation: ${reporter.pathsAsString(files)}" } reporter.reportVerbose { "Source files added since last compilation: ${reporter.pathsAsString(files)}" }
} }
dirtyFiles.add(files) dirtyFiles.add(files, "was not compiled last time")
} }
} }
@@ -47,7 +47,6 @@ import org.jetbrains.kotlin.jps.incremental.withLookupStorage
import org.jetbrains.kotlin.jps.model.kotlinKind import org.jetbrains.kotlin.jps.model.kotlinKind
import org.jetbrains.kotlin.jps.targets.KotlinJvmModuleBuildTarget import org.jetbrains.kotlin.jps.targets.KotlinJvmModuleBuildTarget
import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
import org.jetbrains.kotlin.jps.targets.KotlinUnsupportedModuleBuildTarget
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.preloading.ClassCondition import org.jetbrains.kotlin.preloading.ClassCondition
import org.jetbrains.kotlin.utils.KotlinPaths import org.jetbrains.kotlin.utils.KotlinPaths