From e9d4d85af27c792efe5d3f67af5b3f0edbbb7cdc Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Mon, 3 Oct 2016 16:11:47 +0300 Subject: [PATCH] Refactoring: extract parts of IC --- .../jetbrains/kotlin/gradle/tasks/Tasks.kt | 161 +++++++++--------- .../kotlin/gradle/tasks/fileUtils.kt | 19 +++ 2 files changed, 101 insertions(+), 79 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/fileUtils.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt index 98b8e062227..e40b5aae4fc 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt @@ -1,11 +1,9 @@ package org.jetbrains.kotlin.gradle.tasks -import org.apache.commons.io.FilenameUtils import org.codehaus.groovy.runtime.MethodClosure import org.gradle.api.GradleException import org.gradle.api.file.SourceDirectorySet import org.gradle.api.logging.Logger -import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.SourceTask import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.compile.AbstractCompile @@ -24,7 +22,6 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil import org.jetbrains.kotlin.cli.js.K2JSCompiler import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler -import org.jetbrains.kotlin.com.intellij.ide.highlighter.JavaFileType import org.jetbrains.kotlin.com.intellij.openapi.util.io.FileUtil import org.jetbrains.kotlin.compilerRunner.ArgumentUtils import org.jetbrains.kotlin.compilerRunner.OutputItemsCollector @@ -103,10 +100,7 @@ abstract class AbstractKotlinCompile() : AbstractCo private fun getKotlinSources(): List = (getSource() as Iterable).filter { it.isKotlinFile() } - protected fun File.isKotlinFile(): Boolean = - FilenameUtils.isExtension(name.toLowerCase(), listOf("kt", "kts")) - - protected abstract fun callCompiler(args: T, sources: List, isIncremental: Boolean, modified: List, removed: List) + protected abstract fun callCompiler(args: T, allKotlinSources: List, isIncremental: Boolean, modified: List, removed: List) } @@ -181,18 +175,11 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl return args } - override fun callCompiler(args: K2JVMCompilerArguments, sources: List, isIncrementalRequested: Boolean, modified: List, removed: List) { - fun relativePathOrCanonical(file: File): String = - file.relativeToOrNull(project.rootProject.projectDir)?.path - ?: file.canonicalPath - - fun filesToString(files: Iterable) = - "[" + files.map(::relativePathOrCanonical).sorted().joinToString(separator = ", \n") + "]" - + override fun callCompiler(args: K2JVMCompilerArguments, allKotlinSources: List, isIncrementalRequested: Boolean, modified: List, removed: List) { val outputDir = destinationDir var currentRemoved = removed.filter { it.isKotlinFile() } - val allGeneratedFiles = hashSetOf>() val logAction = { logStr: String -> logger.kotlinInfo(logStr) } + val relativePathOrCanonical = { file: File -> relativePathOrCanonical(file) } fun getClasspathChanges(modifiedClasspath: List, lastBuildInfo: BuildInfo?): ChangesEither { if (modifiedClasspath.isEmpty()) { @@ -239,16 +226,16 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl logger.kotlinInfo("Non-incremental compilation will be performed: $reason") caches.clean() dirtySourcesSinceLastTimeFile.delete() - return Pair(sources.toSet(), false) + return Pair(allKotlinSources.toSet(), false) } if (!incremental) return rebuild("incremental compilation is not enabled") if (!isIncrementalRequested) return rebuild("inputs' changes are unknown (first or clean build)") - val removedClassFiles = removed.filter(File::hasClassFileExtension) + val removedClassFiles = removed.filter(File::isClassFile) if (removedClassFiles.any()) return rebuild("Removed class files: ${filesToString(removedClassFiles)}") - val modifiedClassFiles = modified.filter(File::hasClassFileExtension) + val modifiedClassFiles = modified.filter(File::isClassFile) if (modifiedClassFiles.any()) return rebuild("Modified class files: ${filesToString(modifiedClassFiles)}") val modifiedClasspathEntries = modified.filter { it in classpath } @@ -274,13 +261,13 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl lookupSymbols.addAll(classpathChanges.lookupSymbols) if (lookupSymbols.any()) { - val dirtyFilesFromLookups = mapLookupSymbolsToFiles(caches.lookupCache, lookupSymbols, logAction, ::relativePathOrCanonical) + val dirtyFilesFromLookups = mapLookupSymbolsToFiles(caches.lookupCache, lookupSymbols, logAction, relativePathOrCanonical) dirtyFiles.addAll(dirtyFilesFromLookups) } val dirtyClassesFqNames = classpathChanges.fqNames.flatMap { withSubtypes(it, listOf(caches.incrementalCache)) } if (dirtyClassesFqNames.any()) { - val dirtyFilesFromFqNames = mapClassesFqNamesToFiles(listOf(caches.incrementalCache), dirtyClassesFqNames, logAction, ::relativePathOrCanonical) + val dirtyFilesFromFqNames = mapClassesFqNamesToFiles(listOf(caches.incrementalCache), dirtyClassesFqNames, logAction, relativePathOrCanonical) dirtyFiles.addAll(dirtyFilesFromFqNames) } @@ -296,31 +283,9 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl return Pair(dirtyFiles, true) } - fun cleanupOnError() { - logger.kotlinInfo("deleting $destinationDir on error") - destinationDir.deleteRecursively() - } - - fun processCompilerExitCode(exitCode: ExitCode) { - if (exitCode != ExitCode.OK) { - cleanupOnError() - } - - when (exitCode) { - ExitCode.COMPILATION_ERROR -> throw GradleException("Compilation error. See log for more details") - ExitCode.INTERNAL_ERROR -> throw GradleException("Internal compiler error. See log for more details") - ExitCode.SCRIPT_EXECUTION_ERROR -> throw GradleException("Script execution error. See log for more details") - ExitCode.OK -> { - sourceAnnotationsRegistry?.flush() - cacheVersions.forEach { it.saveIfNeeded() } - logger.kotlinInfo("Compilation succeeded") - } - } - } - if (!incremental) { anyClassesCompiled = true - processCompilerExitCode(compileNotIncremental(sources, outputDir, args)) + processCompilerExitCode(compileNotIncremental(allKotlinSources, outputDir, args)) return } @@ -335,11 +300,30 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl // TODO: decide what to do if no files are considered dirty - rebuild or skip the module var (sourcesToCompile, isIncrementalDecided) = calculateSourcesToCompile(javaFilesProcessor, caches, lastBuildInfo) + compileIncrementally(allGeneratedFiles, args, caches, currentRemoved, isIncrementalDecided, javaFilesProcessor, logAction, lookupTracker, outputDir, relativePathOrCanonical, allKotlinSources, sourcesToCompile, targetId) + } - if (isIncrementalDecided) { + private fun compileIncrementally( + args: K2JVMCompilerArguments, + caches: IncrementalCachesManager, + currentRemoved: List, + isIncrementalDecided: Boolean, + javaFilesProcessor: ChangedJavaFilesProcessor, + logAction: (String) -> Unit, + lookupTracker: LookupTrackerImpl, + outputDir: File, + relativePathOrCanonical: (File) -> String, + allKotlinSources: List, + sourcesToCompile: Set, + targetId: TargetId + ) { + val allGeneratedFiles = hashSetOf>() + var currentRemoved1 = currentRemoved + var isIncrementalDecided1 = isIncrementalDecided + var sourcesToCompile1 = sourcesToCompile + if (isIncrementalDecided1) { additionalClasspath.add(destinationDir) - } - else { + } else { // there is no point in updating annotation file since all files will be compiled anyway kaptAnnotationsFileUpdater = null } @@ -351,19 +335,19 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl val buildDirtyFqNames = HashSet() var exitCode = ExitCode.OK - while (sourcesToCompile.any() || currentRemoved.any()) { - val removedAndModified = (sourcesToCompile + currentRemoved).toList() + while (sourcesToCompile1.any() || currentRemoved1.any()) { + val removedAndModified = (sourcesToCompile1 + currentRemoved1).toList() val outdatedClasses = caches.incrementalCache.classesBySources(removedAndModified) caches.incrementalCache.markOutputClassesDirty(removedAndModified) caches.incrementalCache.removeClassfilesBySources(removedAndModified) // can be empty if only removed sources are present - if (sourcesToCompile.isNotEmpty()) { - logger.kotlinInfo("compile iteration: ${sourcesToCompile.joinToString(transform = ::relativePathOrCanonical)}") + if (sourcesToCompile1.isNotEmpty()) { + logger.kotlinInfo("compile iteration: ${sourcesToCompile1.joinToString(transform = relativePathOrCanonical)}") } - val (existingSource, nonExistingSource) = sourcesToCompile.partition { it.isFile } - assert(nonExistingSource.isEmpty()) { "Trying to compile removed files: ${nonExistingSource.map(::relativePathOrCanonical)}" } + val (existingSource, nonExistingSource) = sourcesToCompile1.partition { it.isFile } + assert(nonExistingSource.isEmpty()) { "Trying to compile removed files: ${nonExistingSource.map(relativePathOrCanonical)}" } val text = existingSource.map { it.canonicalPath }.joinToString(separator = System.getProperty("line.separator")) dirtySourcesSinceLastTimeFile.writeText(text) @@ -374,23 +358,22 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl if (exitCode == ExitCode.OK) { dirtySourcesSinceLastTimeFile.delete() kaptAnnotationsFileUpdater?.updateAnnotations(outdatedClasses) - } - else { + } else { kaptAnnotationsFileUpdater?.revert() break } allGeneratedFiles.addAll(compilerOutput.generatedFiles) val compilationResult = updateIncrementalCaches(listOf(targetId), compilerOutput.generatedFiles, - compiledWithErrors = exitCode != ExitCode.OK, - getIncrementalCache = { caches.incrementalCache }) + compiledWithErrors = exitCode != ExitCode.OK, + getIncrementalCache = { caches.incrementalCache }) - caches.lookupCache.update(lookupTracker, sourcesToCompile, currentRemoved) + caches.lookupCache.update(lookupTracker, sourcesToCompile1, currentRemoved1) val generatedJavaFiles = kapt2GeneratedSourcesDir.walk().filter { it.isJavaFile() }.toList() val generatedJavaFilesDiff = caches.incrementalCache.compareAndUpdateFileSnapshots(generatedJavaFiles) - if (!isIncrementalDecided) { + if (!isIncrementalDecided1) { artifactFile?.let { artifactDifferenceRegistry?.remove(it) } break } @@ -400,28 +383,28 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl val dirtyKotlinFilesFromJava = when (generatedJavaFilesChanges) { is ChangesEither.Unknown -> { logger.kotlinDebug { "Could not get changes for generated java files, recompiling all kotlin" } - isIncrementalDecided = false - sources.toSet() + isIncrementalDecided1 = false + allKotlinSources.toSet() } is ChangesEither.Known -> { - mapLookupSymbolsToFiles(caches.lookupCache, generatedJavaFilesChanges.lookupSymbols, logAction, ::relativePathOrCanonical, excludes = sourcesToCompile) + mapLookupSymbolsToFiles(caches.lookupCache, generatedJavaFilesChanges.lookupSymbols, logAction, relativePathOrCanonical, excludes = sourcesToCompile1) } else -> throw IllegalStateException("Unknown ChangesEither implementation: $generatedJavaFiles") } - sourcesToCompile = dirtyKotlinFilesFromJava + - mapLookupSymbolsToFiles(caches.lookupCache, dirtyLookupSymbols, logAction, ::relativePathOrCanonical, excludes = sourcesToCompile) + - mapClassesFqNamesToFiles(listOf(caches.incrementalCache), dirtyClassFqNames, logAction, ::relativePathOrCanonical, excludes = sourcesToCompile) + sourcesToCompile1 = dirtyKotlinFilesFromJava + + mapLookupSymbolsToFiles(caches.lookupCache, dirtyLookupSymbols, logAction, relativePathOrCanonical, excludes = sourcesToCompile1) + + mapClassesFqNamesToFiles(listOf(caches.incrementalCache), dirtyClassFqNames, logAction, relativePathOrCanonical, excludes = sourcesToCompile1) buildDirtyLookupSymbols.addAll(dirtyLookupSymbols) buildDirtyFqNames.addAll(dirtyClassFqNames) - if (currentRemoved.any()) { + if (currentRemoved1.any()) { anyClassesCompiled = true - currentRemoved = listOf() + currentRemoved1 = listOf() } } - if (exitCode == ExitCode.OK && isIncrementalDecided) { + if (exitCode == ExitCode.OK && isIncrementalDecided1) { buildDirtyLookupSymbols.addAll(javaFilesProcessor.allChangedSymbols) } if (artifactFile != null && artifactDifferenceRegistry != null) { @@ -441,6 +424,35 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl processCompilerExitCode(exitCode) } + private fun relativePathOrCanonical(file: File): String = + file.relativeToOrNull(project.rootProject.projectDir)?.path + ?: file.canonicalPath + + private fun filesToString(files: Iterable) = + "[" + files.map { relativePathOrCanonical(it) }.sorted().joinToString(separator = ", \n") + "]" + + private fun cleanupOnError() { + logger.kotlinInfo("deleting $destinationDir on error") + destinationDir.deleteRecursively() + } + + private fun processCompilerExitCode(exitCode: ExitCode) { + if (exitCode != ExitCode.OK) { + cleanupOnError() + } + + when (exitCode) { + ExitCode.COMPILATION_ERROR -> throw GradleException("Compilation error. See log for more details") + ExitCode.INTERNAL_ERROR -> throw GradleException("Internal compiler error. See log for more details") + ExitCode.SCRIPT_EXECUTION_ERROR -> throw GradleException("Script execution error. See log for more details") + ExitCode.OK -> { + sourceAnnotationsRegistry?.flush() + cacheVersions.forEach { it.saveIfNeeded() } + logger.kotlinInfo("Compilation succeeded") + } + } + } + private data class CompileChangedResults(val exitCode: ExitCode, val generatedFiles: List>) private fun compileChanged( @@ -547,9 +559,6 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl private fun getJavaSourceRoots(): Set = findRootsForSources(getSource().filter { it.isJavaFile() }) - private fun File.isJavaFile() = - extension.equals(JavaFileType.INSTANCE.defaultExtension, ignoreCase = true) - private fun File.isKapt2GeneratedDirectory(): Boolean { if (!kapt2GeneratedSourcesDir.isDirectory) return false return FileUtil.isAncestor(kapt2GeneratedSourcesDir, this, /* strict = */ false) @@ -630,11 +639,11 @@ open class Kotlin2JsCompile() : AbstractKotlinCompile(), return args } - override fun callCompiler(args: K2JSCompilerArguments, sources: List, isIncremental: Boolean, modified: List, removed: List) { + override fun callCompiler(args: K2JSCompilerArguments, allKotlinSources: List, isIncremental: Boolean, modified: List, removed: List) { val messageCollector = GradleMessageCollector(logger) logger.debug("Calling compiler") destinationDir.mkdirs() - args.freeArgs = args.freeArgs + sources.map { it.absolutePath } + args.freeArgs = args.freeArgs + allKotlinSources.map { it.absolutePath } if (args.outputFile == null) { throw GradleException("$name.kotlinOptions.outputFile should be specified.") @@ -719,9 +728,3 @@ internal inline fun Logger.kotlinDebug(message: ()->String) { kotlinDebug(message()) } } - -internal fun listClassFiles(path: String): Sequence = - File(path).walk().filter { it.isFile && it.hasClassFileExtension() } - -private fun File.hasClassFileExtension(): Boolean = - extension.toLowerCase() == "class" diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/fileUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/fileUtils.kt new file mode 100644 index 00000000000..41ba184ed55 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/fileUtils.kt @@ -0,0 +1,19 @@ +package org.jetbrains.kotlin.gradle.tasks + +import java.io.File + +internal fun File.isJavaFile() = + extension.equals("java", ignoreCase = true) + +internal fun File.isKotlinFile(): Boolean = + extension.let { + "kt".equals(it, ignoreCase = true) || + "kts".equals(it, ignoreCase = true) + } + +internal fun File.isClassFile(): Boolean = + extension.equals("class", ignoreCase = true) + +internal fun listClassFiles(path: String): Sequence = + File(path).walk().filter { it.isFile && it.isClassFile() } +