Move IncrementalCompilerRunner to separate file
This commit is contained in:
+267
@@ -0,0 +1,267 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.incremental
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.build.GeneratedFile
|
||||||
|
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||||
|
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||||
|
import org.jetbrains.kotlin.compilerRunner.MessageCollectorToOutputItemsCollectorAdapter
|
||||||
|
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
|
||||||
|
import org.jetbrains.kotlin.compilerRunner.SimpleOutputItem
|
||||||
|
import org.jetbrains.kotlin.compilerRunner.toGeneratedFile
|
||||||
|
import org.jetbrains.kotlin.config.Services
|
||||||
|
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||||
|
import org.jetbrains.kotlin.incremental.multiproject.ArtifactChangesProvider
|
||||||
|
import org.jetbrains.kotlin.incremental.multiproject.ChangesRegistry
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
|
||||||
|
import java.io.File
|
||||||
|
import java.util.ArrayList
|
||||||
|
import java.util.HashSet
|
||||||
|
|
||||||
|
abstract class IncrementalCompilerRunner<
|
||||||
|
Args : CommonCompilerArguments,
|
||||||
|
CacheManager : IncrementalCachesManager<*>
|
||||||
|
>(
|
||||||
|
workingDir: File,
|
||||||
|
cacheDirName: String,
|
||||||
|
protected val cacheVersions: List<CacheVersion>,
|
||||||
|
protected val reporter: ICReporter,
|
||||||
|
protected val artifactChangesProvider: ArtifactChangesProvider?,
|
||||||
|
protected val changesRegistry: ChangesRegistry?
|
||||||
|
) {
|
||||||
|
|
||||||
|
protected val cacheDirectory = File(workingDir, cacheDirName)
|
||||||
|
protected val dirtySourcesSinceLastTimeFile = File(workingDir, DIRTY_SOURCES_FILE_NAME)
|
||||||
|
protected val lastBuildInfoFile = File(workingDir, LAST_BUILD_INFO_FILE_NAME)
|
||||||
|
|
||||||
|
protected abstract fun isICEnabled(): Boolean
|
||||||
|
protected abstract fun createCacheManager(args: Args): CacheManager
|
||||||
|
protected abstract fun destionationDir(args: Args): File
|
||||||
|
|
||||||
|
fun compile(
|
||||||
|
allKotlinSources: List<File>,
|
||||||
|
args: Args,
|
||||||
|
messageCollector: MessageCollector,
|
||||||
|
getChangedFiles: (CacheManager)-> ChangedFiles
|
||||||
|
): ExitCode {
|
||||||
|
assert(isICEnabled()) { "Incremental compilation is not enabled" }
|
||||||
|
var caches = createCacheManager(args)
|
||||||
|
|
||||||
|
fun rebuild(): ExitCode {
|
||||||
|
caches.clean()
|
||||||
|
dirtySourcesSinceLastTimeFile.delete()
|
||||||
|
destionationDir(args).deleteRecursively()
|
||||||
|
|
||||||
|
caches = createCacheManager(args)
|
||||||
|
return compileIncrementally(args, caches, allKotlinSources, CompilationMode.Rebuild(), messageCollector)
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
val changedFiles = getChangedFiles(caches)
|
||||||
|
val compilationMode = sourcesToCompile(caches, changedFiles, args)
|
||||||
|
|
||||||
|
val exitCode = when (compilationMode) {
|
||||||
|
is CompilationMode.Incremental -> {
|
||||||
|
compileIncrementally(args, caches, allKotlinSources, compilationMode, messageCollector)
|
||||||
|
}
|
||||||
|
is CompilationMode.Rebuild -> {
|
||||||
|
reporter.report { "Non-incremental compilation will be performed: ${compilationMode.reason}" }
|
||||||
|
rebuild()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!caches.close(flush = true)) throw RuntimeException("Could not flush caches")
|
||||||
|
|
||||||
|
return exitCode
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
// todo: warn?
|
||||||
|
reporter.report { "Possible cache corruption. Rebuilding. $e" }
|
||||||
|
rebuild()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sourcesToCompile(caches: CacheManager, changedFiles: ChangedFiles, args: Args): CompilationMode =
|
||||||
|
when (changedFiles) {
|
||||||
|
is ChangedFiles.Known -> calculateSourcesToCompile(caches, changedFiles, args)
|
||||||
|
is ChangedFiles.Unknown -> CompilationMode.Rebuild { "inputs' changes are unknown (first or clean build)" }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun calculateSourcesToCompile(caches: CacheManager, changedFiles: ChangedFiles.Known, args: Args): CompilationMode =
|
||||||
|
CompilationMode.Incremental(getDirtyFiles(changedFiles))
|
||||||
|
|
||||||
|
protected fun getDirtyFiles(changedFiles: ChangedFiles.Known): HashSet<File> {
|
||||||
|
val dirtyFiles = HashSet<File>(with(changedFiles) { modified.size + removed.size })
|
||||||
|
with(changedFiles) {
|
||||||
|
modified.asSequence() + removed.asSequence()
|
||||||
|
}.forEach { if (it.isKotlinFile()) dirtyFiles.add(it) }
|
||||||
|
|
||||||
|
if (dirtySourcesSinceLastTimeFile.exists()) {
|
||||||
|
val files = dirtySourcesSinceLastTimeFile.readLines().map(::File).filter(File::exists)
|
||||||
|
if (files.isNotEmpty()) {
|
||||||
|
reporter.report { "Source files added since last compilation: ${reporter.pathsAsString(files)}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
dirtyFiles.addAll(files)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dirtyFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
protected sealed class CompilationMode {
|
||||||
|
class Incremental(val dirtyFiles: Set<File>) : CompilationMode()
|
||||||
|
class Rebuild(getReason: ()->String = { "" }) : CompilationMode() {
|
||||||
|
val reason: String by lazy(getReason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun markOutputDirty(caches: CacheManager, dirtySources: List<File>) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun compareAndUpdateCache(caches: CacheManager, generatedFiles: List<GeneratedFile>): CompilationResult
|
||||||
|
|
||||||
|
protected open fun preBuildHook(args: Args, compilationMode: CompilationMode) {}
|
||||||
|
protected open fun postCompilationHook(exitCode: ExitCode) {}
|
||||||
|
protected open fun additionalDirtyFiles(caches: CacheManager, generatedFiles: List<GeneratedFile>): Iterable<File> =
|
||||||
|
emptyList()
|
||||||
|
protected open fun additionalDirtyLookupSymbols(): Iterable<LookupSymbol> =
|
||||||
|
emptyList()
|
||||||
|
|
||||||
|
protected abstract fun runCompiler(
|
||||||
|
sourcesToCompile: Set<File>,
|
||||||
|
args: Args,
|
||||||
|
caches: CacheManager,
|
||||||
|
services: Services.Builder,
|
||||||
|
messageCollector: MessageCollector
|
||||||
|
): ExitCode
|
||||||
|
|
||||||
|
private fun compileIncrementally(
|
||||||
|
args: Args,
|
||||||
|
caches: CacheManager,
|
||||||
|
allKotlinSources: List<File>,
|
||||||
|
compilationMode: CompilationMode,
|
||||||
|
messageCollector: MessageCollector
|
||||||
|
): ExitCode {
|
||||||
|
preBuildHook(args, compilationMode)
|
||||||
|
|
||||||
|
val dirtySources = when (compilationMode) {
|
||||||
|
is CompilationMode.Incremental -> ArrayList(compilationMode.dirtyFiles)
|
||||||
|
is CompilationMode.Rebuild -> allKotlinSources.toMutableList()
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentBuildInfo = BuildInfo(startTS = System.currentTimeMillis())
|
||||||
|
BuildInfo.write(currentBuildInfo, lastBuildInfoFile)
|
||||||
|
val buildDirtyLookupSymbols = HashSet<LookupSymbol>()
|
||||||
|
val buildDirtyFqNames = HashSet<FqName>()
|
||||||
|
val allSourcesToCompile = HashSet<File>()
|
||||||
|
|
||||||
|
var exitCode = ExitCode.OK
|
||||||
|
val allGeneratedFiles = hashSetOf<GeneratedFile>()
|
||||||
|
|
||||||
|
while (dirtySources.any()) {
|
||||||
|
markOutputDirty(caches, dirtySources)
|
||||||
|
caches.inputsCache.removeOutputForSourceFiles(dirtySources)
|
||||||
|
|
||||||
|
val lookupTracker = LookupTrackerImpl(LookupTracker.DO_NOTHING)
|
||||||
|
val (sourcesToCompile, removedKotlinSources) = dirtySources.partition(File::exists)
|
||||||
|
|
||||||
|
// todo: more optimal to save only last iteration, but it will require adding standalone-ic specific logs
|
||||||
|
// (because jps rebuilds all files from last build if it failed and gradle rebuilds everything)
|
||||||
|
allSourcesToCompile.addAll(sourcesToCompile)
|
||||||
|
val text = allSourcesToCompile.joinToString(separator = System.getProperty("line.separator")) { it.canonicalPath }
|
||||||
|
dirtySourcesSinceLastTimeFile.writeText(text)
|
||||||
|
|
||||||
|
val services = Services.Builder().apply {
|
||||||
|
register(LookupTracker::class.java, lookupTracker)
|
||||||
|
register(CompilationCanceledStatus::class.java, EmptyCompilationCanceledStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
args.reportOutputFiles = true
|
||||||
|
val outputItemsCollector = OutputItemsCollectorImpl()
|
||||||
|
val messageCollectorAdapter = MessageCollectorToOutputItemsCollectorAdapter(messageCollector, outputItemsCollector)
|
||||||
|
|
||||||
|
exitCode = runCompiler(sourcesToCompile.toSet(), args, caches, services, messageCollectorAdapter)
|
||||||
|
postCompilationHook(exitCode)
|
||||||
|
|
||||||
|
if (exitCode != ExitCode.OK) break
|
||||||
|
|
||||||
|
dirtySourcesSinceLastTimeFile.delete()
|
||||||
|
val generatedFiles = outputItemsCollector.outputs.map(SimpleOutputItem::toGeneratedFile)
|
||||||
|
|
||||||
|
if (compilationMode is CompilationMode.Incremental) {
|
||||||
|
// todo: feels dirty, can this be refactored?
|
||||||
|
val dirtySourcesSet = dirtySources.toHashSet()
|
||||||
|
val additionalDirtyFiles = additionalDirtyFiles(caches, generatedFiles).filter { it !in dirtySourcesSet }
|
||||||
|
if (additionalDirtyFiles.isNotEmpty()) {
|
||||||
|
dirtySources.addAll(additionalDirtyFiles)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allGeneratedFiles.addAll(generatedFiles)
|
||||||
|
caches.inputsCache.registerOutputForSourceFiles(generatedFiles)
|
||||||
|
caches.lookupCache.update(lookupTracker, sourcesToCompile, removedKotlinSources)
|
||||||
|
val compilationResult = compareAndUpdateCache(caches, generatedFiles)
|
||||||
|
|
||||||
|
if (compilationMode is CompilationMode.Rebuild) break
|
||||||
|
|
||||||
|
val (dirtyLookupSymbols, dirtyClassFqNames) = compilationResult.getDirtyData(listOf(caches.platformCache), reporter)
|
||||||
|
val compiledInThisIterationSet = sourcesToCompile.toHashSet()
|
||||||
|
|
||||||
|
with (dirtySources) {
|
||||||
|
clear()
|
||||||
|
addAll(mapLookupSymbolsToFiles(caches.lookupCache, dirtyLookupSymbols, reporter, excludes = compiledInThisIterationSet))
|
||||||
|
addAll(mapClassesFqNamesToFiles(listOf(caches.platformCache), dirtyClassFqNames, reporter, excludes = compiledInThisIterationSet))
|
||||||
|
}
|
||||||
|
|
||||||
|
buildDirtyLookupSymbols.addAll(dirtyLookupSymbols)
|
||||||
|
buildDirtyFqNames.addAll(dirtyClassFqNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exitCode == ExitCode.OK && compilationMode is CompilationMode.Incremental) {
|
||||||
|
buildDirtyLookupSymbols.addAll(additionalDirtyLookupSymbols())
|
||||||
|
}
|
||||||
|
if (changesRegistry != null) {
|
||||||
|
if (compilationMode is CompilationMode.Incremental) {
|
||||||
|
val dirtyData = DirtyData(buildDirtyLookupSymbols, buildDirtyFqNames)
|
||||||
|
changesRegistry.registerChanges(currentBuildInfo.startTS, dirtyData)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(compilationMode is CompilationMode.Rebuild) { "Unexpected compilation mode: ${compilationMode::class.java}" }
|
||||||
|
changesRegistry.unknownChanges(currentBuildInfo.startTS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exitCode == ExitCode.OK) {
|
||||||
|
cacheVersions.forEach { it.saveIfNeeded() }
|
||||||
|
}
|
||||||
|
|
||||||
|
return exitCode
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val DIRTY_SOURCES_FILE_NAME = "dirty-sources.txt"
|
||||||
|
const val LAST_BUILD_INFO_FILE_NAME = "last-build.bin"
|
||||||
|
}
|
||||||
|
|
||||||
|
private object EmptyCompilationCanceledStatus : CompilationCanceledStatus {
|
||||||
|
override fun checkCanceled() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-236
@@ -22,21 +22,18 @@ import org.jetbrains.kotlin.build.GeneratedJvmClass
|
|||||||
import org.jetbrains.kotlin.build.isModuleMappingFile
|
import org.jetbrains.kotlin.build.isModuleMappingFile
|
||||||
import org.jetbrains.kotlin.build.JvmSourceRoot
|
import org.jetbrains.kotlin.build.JvmSourceRoot
|
||||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
||||||
import org.jetbrains.kotlin.compilerRunner.*
|
import org.jetbrains.kotlin.compilerRunner.ArgumentUtils
|
||||||
import org.jetbrains.kotlin.config.IncrementalCompilation
|
import org.jetbrains.kotlin.config.IncrementalCompilation
|
||||||
import org.jetbrains.kotlin.config.Services
|
import org.jetbrains.kotlin.config.Services
|
||||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
|
||||||
import org.jetbrains.kotlin.incremental.multiproject.ArtifactChangesProvider
|
import org.jetbrains.kotlin.incremental.multiproject.ArtifactChangesProvider
|
||||||
import org.jetbrains.kotlin.incremental.multiproject.ChangesRegistry
|
import org.jetbrains.kotlin.incremental.multiproject.ChangesRegistry
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
|
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
|
||||||
import org.jetbrains.kotlin.modules.TargetId
|
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.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -84,233 +81,6 @@ inline fun <R> withIC(enabled: Boolean = true, fn: ()->R): R {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class IncrementalCompilerRunner<
|
|
||||||
Args : CommonCompilerArguments,
|
|
||||||
CacheManager : IncrementalCachesManager<*>
|
|
||||||
>(
|
|
||||||
workingDir: File,
|
|
||||||
cacheDirName: String,
|
|
||||||
protected val cacheVersions: List<CacheVersion>,
|
|
||||||
protected val reporter: ICReporter,
|
|
||||||
protected val artifactChangesProvider: ArtifactChangesProvider?,
|
|
||||||
protected val changesRegistry: ChangesRegistry?
|
|
||||||
) {
|
|
||||||
|
|
||||||
protected val cacheDirectory = File(workingDir, cacheDirName)
|
|
||||||
protected val dirtySourcesSinceLastTimeFile = File(workingDir, DIRTY_SOURCES_FILE_NAME)
|
|
||||||
protected val lastBuildInfoFile = File(workingDir, LAST_BUILD_INFO_FILE_NAME)
|
|
||||||
|
|
||||||
protected abstract fun isICEnabled(): Boolean
|
|
||||||
protected abstract fun createCacheManager(args: Args): CacheManager
|
|
||||||
protected abstract fun destionationDir(args: Args): File
|
|
||||||
|
|
||||||
fun compile(
|
|
||||||
allKotlinSources: List<File>,
|
|
||||||
args: Args,
|
|
||||||
messageCollector: MessageCollector,
|
|
||||||
getChangedFiles: (CacheManager)->ChangedFiles
|
|
||||||
): ExitCode {
|
|
||||||
assert(isICEnabled()) { "Incremental compilation is not enabled" }
|
|
||||||
var caches = createCacheManager(args)
|
|
||||||
|
|
||||||
fun rebuild(): ExitCode {
|
|
||||||
caches.clean()
|
|
||||||
dirtySourcesSinceLastTimeFile.delete()
|
|
||||||
destionationDir(args).deleteRecursively()
|
|
||||||
|
|
||||||
caches = createCacheManager(args)
|
|
||||||
return compileIncrementally(args, caches, allKotlinSources, CompilationMode.Rebuild(), messageCollector)
|
|
||||||
}
|
|
||||||
|
|
||||||
return try {
|
|
||||||
val changedFiles = getChangedFiles(caches)
|
|
||||||
val compilationMode = sourcesToCompile(caches, changedFiles, args)
|
|
||||||
|
|
||||||
val exitCode = when (compilationMode) {
|
|
||||||
is CompilationMode.Incremental -> {
|
|
||||||
compileIncrementally(args, caches, allKotlinSources, compilationMode, messageCollector)
|
|
||||||
}
|
|
||||||
is CompilationMode.Rebuild -> {
|
|
||||||
reporter.report { "Non-incremental compilation will be performed: ${compilationMode.reason}" }
|
|
||||||
rebuild()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!caches.close(flush = true)) throw RuntimeException("Could not flush caches")
|
|
||||||
|
|
||||||
return exitCode
|
|
||||||
}
|
|
||||||
catch (e: Exception) {
|
|
||||||
// todo: warn?
|
|
||||||
reporter.report { "Possible cache corruption. Rebuilding. $e" }
|
|
||||||
rebuild()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun sourcesToCompile(caches: CacheManager, changedFiles: ChangedFiles, args: Args): CompilationMode =
|
|
||||||
when (changedFiles) {
|
|
||||||
is ChangedFiles.Known -> calculateSourcesToCompile(caches, changedFiles, args)
|
|
||||||
is ChangedFiles.Unknown -> CompilationMode.Rebuild { "inputs' changes are unknown (first or clean build)" }
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun calculateSourcesToCompile(caches: CacheManager, changedFiles: ChangedFiles.Known, args: Args): CompilationMode =
|
|
||||||
CompilationMode.Incremental(getDirtyFiles(changedFiles))
|
|
||||||
|
|
||||||
protected fun getDirtyFiles(changedFiles: ChangedFiles.Known): HashSet<File> {
|
|
||||||
val dirtyFiles = HashSet<File>(with(changedFiles) { modified.size + removed.size })
|
|
||||||
with(changedFiles) {
|
|
||||||
modified.asSequence() + removed.asSequence()
|
|
||||||
}.forEach { if (it.isKotlinFile()) dirtyFiles.add(it) }
|
|
||||||
|
|
||||||
if (dirtySourcesSinceLastTimeFile.exists()) {
|
|
||||||
val files = dirtySourcesSinceLastTimeFile.readLines().map(::File).filter(File::exists)
|
|
||||||
if (files.isNotEmpty()) {
|
|
||||||
reporter.report { "Source files added since last compilation: ${reporter.pathsAsString(files)}" }
|
|
||||||
}
|
|
||||||
|
|
||||||
dirtyFiles.addAll(files)
|
|
||||||
}
|
|
||||||
|
|
||||||
return dirtyFiles
|
|
||||||
}
|
|
||||||
|
|
||||||
protected sealed class CompilationMode {
|
|
||||||
class Incremental(val dirtyFiles: Set<File>) : CompilationMode()
|
|
||||||
class Rebuild(getReason: ()->String = { "" }) : CompilationMode() {
|
|
||||||
val reason: String by lazy(getReason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun markOutputDirty(caches: CacheManager, dirtySources: List<File>) {
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract fun compareAndUpdateCache(caches: CacheManager, generatedFiles: List<GeneratedFile>): CompilationResult
|
|
||||||
|
|
||||||
protected open fun preBuildHook(args: Args, compilationMode: CompilationMode) {}
|
|
||||||
protected open fun postCompilationHook(exitCode: ExitCode) {}
|
|
||||||
protected open fun additionalDirtyFiles(caches: CacheManager, generatedFiles: List<GeneratedFile>): Iterable<File> =
|
|
||||||
emptyList()
|
|
||||||
protected open fun additionalDirtyLookupSymbols(): Iterable<LookupSymbol> =
|
|
||||||
emptyList()
|
|
||||||
|
|
||||||
protected abstract fun runCompiler(
|
|
||||||
sourcesToCompile: Set<File>,
|
|
||||||
args: Args,
|
|
||||||
caches: CacheManager,
|
|
||||||
services: Services.Builder,
|
|
||||||
messageCollector: MessageCollector
|
|
||||||
): ExitCode
|
|
||||||
|
|
||||||
private fun compileIncrementally(
|
|
||||||
args: Args,
|
|
||||||
caches: CacheManager,
|
|
||||||
allKotlinSources: List<File>,
|
|
||||||
compilationMode: CompilationMode,
|
|
||||||
messageCollector: MessageCollector
|
|
||||||
): ExitCode {
|
|
||||||
preBuildHook(args, compilationMode)
|
|
||||||
|
|
||||||
val dirtySources = when (compilationMode) {
|
|
||||||
is CompilationMode.Incremental -> ArrayList(compilationMode.dirtyFiles)
|
|
||||||
is CompilationMode.Rebuild -> allKotlinSources.toMutableList()
|
|
||||||
}
|
|
||||||
|
|
||||||
val currentBuildInfo = BuildInfo(startTS = System.currentTimeMillis())
|
|
||||||
BuildInfo.write(currentBuildInfo, lastBuildInfoFile)
|
|
||||||
val buildDirtyLookupSymbols = HashSet<LookupSymbol>()
|
|
||||||
val buildDirtyFqNames = HashSet<FqName>()
|
|
||||||
val allSourcesToCompile = HashSet<File>()
|
|
||||||
|
|
||||||
var exitCode = ExitCode.OK
|
|
||||||
val allGeneratedFiles = hashSetOf<GeneratedFile>()
|
|
||||||
|
|
||||||
while (dirtySources.any()) {
|
|
||||||
markOutputDirty(caches, dirtySources)
|
|
||||||
caches.inputsCache.removeOutputForSourceFiles(dirtySources)
|
|
||||||
|
|
||||||
val lookupTracker = LookupTrackerImpl(LookupTracker.DO_NOTHING)
|
|
||||||
val (sourcesToCompile, removedKotlinSources) = dirtySources.partition(File::exists)
|
|
||||||
|
|
||||||
// todo: more optimal to save only last iteration, but it will require adding standalone-ic specific logs
|
|
||||||
// (because jps rebuilds all files from last build if it failed and gradle rebuilds everything)
|
|
||||||
allSourcesToCompile.addAll(sourcesToCompile)
|
|
||||||
val text = allSourcesToCompile.joinToString(separator = System.getProperty("line.separator")) { it.canonicalPath }
|
|
||||||
dirtySourcesSinceLastTimeFile.writeText(text)
|
|
||||||
|
|
||||||
val services = Services.Builder().apply {
|
|
||||||
register(LookupTracker::class.java, lookupTracker)
|
|
||||||
register(CompilationCanceledStatus::class.java, EmptyCompilationCanceledStatus)
|
|
||||||
}
|
|
||||||
|
|
||||||
args.reportOutputFiles = true
|
|
||||||
val outputItemsCollector = OutputItemsCollectorImpl()
|
|
||||||
val messageCollectorAdapter = MessageCollectorToOutputItemsCollectorAdapter(messageCollector, outputItemsCollector)
|
|
||||||
|
|
||||||
exitCode = runCompiler(sourcesToCompile.toSet(), args, caches, services, messageCollectorAdapter)
|
|
||||||
postCompilationHook(exitCode)
|
|
||||||
|
|
||||||
if (exitCode != ExitCode.OK) break
|
|
||||||
|
|
||||||
dirtySourcesSinceLastTimeFile.delete()
|
|
||||||
val generatedFiles = outputItemsCollector.outputs.map(SimpleOutputItem::toGeneratedFile)
|
|
||||||
|
|
||||||
if (compilationMode is CompilationMode.Incremental) {
|
|
||||||
// todo: feels dirty, can this be refactored?
|
|
||||||
val dirtySourcesSet = dirtySources.toHashSet()
|
|
||||||
val additionalDirtyFiles = additionalDirtyFiles(caches, generatedFiles).filter { it !in dirtySourcesSet }
|
|
||||||
if (additionalDirtyFiles.isNotEmpty()) {
|
|
||||||
dirtySources.addAll(additionalDirtyFiles)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allGeneratedFiles.addAll(generatedFiles)
|
|
||||||
caches.inputsCache.registerOutputForSourceFiles(generatedFiles)
|
|
||||||
caches.lookupCache.update(lookupTracker, sourcesToCompile, removedKotlinSources)
|
|
||||||
val compilationResult = compareAndUpdateCache(caches, generatedFiles)
|
|
||||||
|
|
||||||
if (compilationMode is CompilationMode.Rebuild) break
|
|
||||||
|
|
||||||
val (dirtyLookupSymbols, dirtyClassFqNames) = compilationResult.getDirtyData(listOf(caches.platformCache), reporter)
|
|
||||||
val compiledInThisIterationSet = sourcesToCompile.toHashSet()
|
|
||||||
|
|
||||||
with (dirtySources) {
|
|
||||||
clear()
|
|
||||||
addAll(mapLookupSymbolsToFiles(caches.lookupCache, dirtyLookupSymbols, reporter, excludes = compiledInThisIterationSet))
|
|
||||||
addAll(mapClassesFqNamesToFiles(listOf(caches.platformCache), dirtyClassFqNames, reporter, excludes = compiledInThisIterationSet))
|
|
||||||
}
|
|
||||||
|
|
||||||
buildDirtyLookupSymbols.addAll(dirtyLookupSymbols)
|
|
||||||
buildDirtyFqNames.addAll(dirtyClassFqNames)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exitCode == ExitCode.OK && compilationMode is CompilationMode.Incremental) {
|
|
||||||
buildDirtyLookupSymbols.addAll(additionalDirtyLookupSymbols())
|
|
||||||
}
|
|
||||||
if (changesRegistry != null) {
|
|
||||||
if (compilationMode is CompilationMode.Incremental) {
|
|
||||||
val dirtyData = DirtyData(buildDirtyLookupSymbols, buildDirtyFqNames)
|
|
||||||
changesRegistry.registerChanges(currentBuildInfo.startTS, dirtyData)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
assert(compilationMode is CompilationMode.Rebuild) { "Unexpected compilation mode: ${compilationMode::class.java}" }
|
|
||||||
changesRegistry.unknownChanges(currentBuildInfo.startTS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exitCode == ExitCode.OK) {
|
|
||||||
cacheVersions.forEach { it.saveIfNeeded() }
|
|
||||||
}
|
|
||||||
|
|
||||||
return exitCode
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val DIRTY_SOURCES_FILE_NAME = "dirty-sources.txt"
|
|
||||||
const val LAST_BUILD_INFO_FILE_NAME = "last-build.bin"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IncrementalJvmCompilerRunner(
|
class IncrementalJvmCompilerRunner(
|
||||||
workingDir: File,
|
workingDir: File,
|
||||||
private val javaSourceRoots: Set<JvmSourceRoot>,
|
private val javaSourceRoots: Set<JvmSourceRoot>,
|
||||||
@@ -531,11 +301,6 @@ class IncrementalJvmCompilerRunner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private object EmptyCompilationCanceledStatus : CompilationCanceledStatus {
|
|
||||||
override fun checkCanceled() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var K2JVMCompilerArguments.destinationAsFile: File
|
var K2JVMCompilerArguments.destinationAsFile: File
|
||||||
get() = File(destination)
|
get() = File(destination)
|
||||||
set(value) { destination = value.path }
|
set(value) { destination = value.path }
|
||||||
|
|||||||
Reference in New Issue
Block a user