Remove global artifact history cache in Gradle
Each Kotlin task now writes build history to separate file.
A map of output directories to history files is used to get changes for
modified files.
#KT-22623 fixed
This commit is contained in:
+4
-5
@@ -60,13 +60,13 @@ class IncrementalCompilationOptions(
|
||||
reportSeverity: Int,
|
||||
/** @See [CompilationResultCategory]] */
|
||||
requestedCompilationResults: Array<Int>,
|
||||
val resultDifferenceFile: File? = null,
|
||||
val friendDifferenceFile: File? = null,
|
||||
val usePreciseJavaTracking: Boolean,
|
||||
/**
|
||||
* Directories that should be cleared when IC decides to rebuild
|
||||
*/
|
||||
val localStateDirs: List<File>
|
||||
val localStateDirs: List<File>,
|
||||
val buildHistoryFile: File,
|
||||
val modulesInfo: IncrementalModuleInfo
|
||||
) : CompilationOptions(compilerMode, targetPlatform, reportCategories, reportSeverity, requestedCompilationResults) {
|
||||
companion object {
|
||||
const val serialVersionUID: Long = 0
|
||||
@@ -81,8 +81,7 @@ class IncrementalCompilationOptions(
|
||||
"workingDir=$workingDir, " +
|
||||
"customCacheVersionFileName='$customCacheVersionFileName', " +
|
||||
"customCacheVersion=$customCacheVersion, " +
|
||||
"resultDifferenceFile=$resultDifferenceFile, " +
|
||||
"friendDifferenceFile=$friendDifferenceFile, " +
|
||||
"buildHistoryFile=$buildHistoryFile, " +
|
||||
"usePreciseJavaTracking=$usePreciseJavaTracking" +
|
||||
"localStateDirs=$localStateDirs" +
|
||||
")"
|
||||
|
||||
-11
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.daemon.common
|
||||
|
||||
import java.io.File
|
||||
import java.rmi.RemoteException
|
||||
|
||||
interface IncrementalCompilerServicesFacade : CompilerServicesFacadeBase {
|
||||
@@ -29,14 +28,4 @@ interface IncrementalCompilerServicesFacade : CompilerServicesFacadeBase {
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun revert()
|
||||
|
||||
// ChangesRegistry
|
||||
@Throws(RemoteException::class)
|
||||
fun registerChanges(timestamp: Long, dirtyData: SimpleDirtyData)
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun unknownChanges(timestamp: Long)
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun getChanges(artifact: File, sinceTS: Long): Iterable<SimpleDirtyData>?
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2000-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.daemon.common
|
||||
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
|
||||
data class IncrementalModuleEntry(
|
||||
private val projectPath: String,
|
||||
val name: String,
|
||||
val buildDir: File,
|
||||
val buildHistoryFile: File
|
||||
) : Serializable {
|
||||
companion object {
|
||||
private const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
|
||||
class IncrementalModuleInfo(
|
||||
val projectRoot: File,
|
||||
val dirToModule: Map<File, IncrementalModuleEntry>,
|
||||
val nameToModules: Map<String, Set<IncrementalModuleEntry>>
|
||||
) : Serializable {
|
||||
companion object {
|
||||
private const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
@@ -41,8 +41,6 @@ import org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler
|
||||
import org.jetbrains.kotlin.config.Services
|
||||
import org.jetbrains.kotlin.daemon.common.*
|
||||
import org.jetbrains.kotlin.daemon.incremental.RemoteAnnotationsFileUpdater
|
||||
import org.jetbrains.kotlin.daemon.incremental.RemoteArtifactChangesProvider
|
||||
import org.jetbrains.kotlin.daemon.incremental.RemoteChangesRegistry
|
||||
import org.jetbrains.kotlin.daemon.report.CompileServicesFacadeMessageCollector
|
||||
import org.jetbrains.kotlin.daemon.report.DaemonMessageReporter
|
||||
import org.jetbrains.kotlin.daemon.report.DaemonMessageReporterPrintStreamAdapter
|
||||
@@ -50,6 +48,8 @@ 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.load.kotlin.incremental.components.IncrementalCompilationComponents
|
||||
import org.jetbrains.kotlin.modules.Module
|
||||
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
|
||||
@@ -517,9 +517,6 @@ class CompileServiceImpl(
|
||||
ChangedFiles.Unknown()
|
||||
}
|
||||
|
||||
val artifactChanges = RemoteArtifactChangesProvider(servicesFacade)
|
||||
val changesRegistry = RemoteChangesRegistry(servicesFacade)
|
||||
|
||||
val workingDir = incrementalCompilationOptions.workingDir
|
||||
val versions = commonCacheVersions(workingDir) +
|
||||
customCacheVersion(incrementalCompilationOptions.customCacheVersion,
|
||||
@@ -527,13 +524,19 @@ class CompileServiceImpl(
|
||||
workingDir,
|
||||
enabled = true)
|
||||
|
||||
val compiler = IncrementalJvmCompilerRunner(workingDir, javaSourceRoots, versions,
|
||||
reporter, annotationFileUpdater,
|
||||
artifactChanges, changesRegistry,
|
||||
buildHistoryFile = incrementalCompilationOptions.resultDifferenceFile,
|
||||
friendBuildHistoryFile = incrementalCompilationOptions.friendDifferenceFile,
|
||||
usePreciseJavaTracking = incrementalCompilationOptions.usePreciseJavaTracking,
|
||||
localStateDirs = incrementalCompilationOptions.localStateDirs
|
||||
val modulesApiHistory = incrementalCompilationOptions.run {
|
||||
ModulesApiHistoryImpl(modulesInfo)
|
||||
}
|
||||
|
||||
val compiler = IncrementalJvmCompilerRunner(
|
||||
workingDir,
|
||||
javaSourceRoots,
|
||||
versions,
|
||||
reporter, annotationFileUpdater,
|
||||
buildHistoryFile = incrementalCompilationOptions.buildHistoryFile,
|
||||
localStateDirs = incrementalCompilationOptions.localStateDirs,
|
||||
usePreciseJavaTracking = incrementalCompilationOptions.usePreciseJavaTracking,
|
||||
modulesApiHistory = modulesApiHistory
|
||||
)
|
||||
return compiler.compile(allKotlinFiles, k2jvmArgs, compilerMessageCollector, changedFiles)
|
||||
}
|
||||
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.daemon.incremental
|
||||
|
||||
import org.jetbrains.kotlin.daemon.common.IncrementalCompilerServicesFacade
|
||||
import org.jetbrains.kotlin.incremental.DirtyData
|
||||
import org.jetbrains.kotlin.incremental.multiproject.ArtifactChangesProvider
|
||||
import java.io.File
|
||||
|
||||
class RemoteArtifactChangesProvider(private val servicesFacade: IncrementalCompilerServicesFacade) : ArtifactChangesProvider {
|
||||
override fun getChanges(artifact: File, sinceTS: Long): Iterable<DirtyData>? =
|
||||
servicesFacade.getChanges(artifact, sinceTS)?.map { it.toDirtyData() }
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.daemon.incremental
|
||||
|
||||
import org.jetbrains.kotlin.daemon.common.IncrementalCompilerServicesFacade
|
||||
import org.jetbrains.kotlin.incremental.DirtyData
|
||||
import org.jetbrains.kotlin.incremental.multiproject.ChangesRegistry
|
||||
|
||||
internal class RemoteChangesRegistry(private val servicesFacade: IncrementalCompilerServicesFacade) : ChangesRegistry {
|
||||
override fun unknownChanges(timestamp: Long) {
|
||||
servicesFacade.unknownChanges(timestamp)
|
||||
}
|
||||
|
||||
override fun registerChanges(timestamp: Long, dirtyData: DirtyData) {
|
||||
servicesFacade.registerChanges(timestamp, dirtyData.toSimpleDirtyData())
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ dependencies {
|
||||
compile(project(":compiler:frontend.java"))
|
||||
compile(project(":compiler:cli"))
|
||||
compile(project(":kotlin-build-common"))
|
||||
compile(project(":compiler:daemon-common"))
|
||||
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||
compileOnly(intellijDep()) { includeJars("annotations") }
|
||||
|
||||
|
||||
+6
-1
@@ -29,6 +29,11 @@ data class BuildDifference(val ts: Long, val isIncremental: Boolean, val dirtyDa
|
||||
data class BuildDiffsStorage(val buildDiffs: List<BuildDifference>) {
|
||||
companion object {
|
||||
fun readFromFile(file: File, reporter: ICReporter?): BuildDiffsStorage? {
|
||||
val diffs = readDiffsFromFile(file, reporter)
|
||||
return diffs?.let { BuildDiffsStorage(it) }
|
||||
}
|
||||
|
||||
fun readDiffsFromFile(file: File, reporter: ICReporter?): MutableList<BuildDifference>? {
|
||||
fun reportFail(reason: String) {
|
||||
reporter?.report { "Could not read diff from file $file: $reason" }
|
||||
}
|
||||
@@ -48,7 +53,7 @@ data class BuildDiffsStorage(val buildDiffs: List<BuildDifference>) {
|
||||
repeat(size) {
|
||||
result.add(input.readBuildDifference())
|
||||
}
|
||||
return BuildDiffsStorage(result)
|
||||
return result
|
||||
}
|
||||
}
|
||||
catch (e: IOException) {
|
||||
|
||||
+1
-1
@@ -23,5 +23,5 @@ internal sealed class ChangesEither {
|
||||
val lookupSymbols: Collection<LookupSymbol> = emptyList(),
|
||||
val fqNames: Collection<FqName> = emptyList()
|
||||
) : ChangesEither()
|
||||
internal class Unknown : ChangesEither()
|
||||
internal class Unknown(val reason: String? = null) : ChangesEither()
|
||||
}
|
||||
+5
-14
@@ -27,8 +27,6 @@ import org.jetbrains.kotlin.compilerRunner.toGeneratedFile
|
||||
import org.jetbrains.kotlin.config.Services
|
||||
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
|
||||
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.incremental.parsing.classesFqNames
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
|
||||
@@ -43,8 +41,6 @@ abstract class IncrementalCompilerRunner<
|
||||
cacheDirName: String,
|
||||
protected val cacheVersions: List<CacheVersion>,
|
||||
protected val reporter: ICReporter,
|
||||
protected val artifactChangesProvider: ArtifactChangesProvider?,
|
||||
protected val changesRegistry: ChangesRegistry?,
|
||||
private val localStateDirs: Collection<File> = emptyList()
|
||||
) {
|
||||
|
||||
@@ -307,16 +303,11 @@ abstract class IncrementalCompilerRunner<
|
||||
|
||||
open fun runWithNoDirtyKotlinSources(caches: CacheManager): Boolean = false
|
||||
|
||||
protected open fun processChangesAfterBuild(compilationMode: CompilationMode, currentBuildInfo: BuildInfo, dirtyData: DirtyData) {
|
||||
if (changesRegistry == null) return
|
||||
|
||||
if (compilationMode is CompilationMode.Incremental) {
|
||||
changesRegistry.registerChanges(currentBuildInfo.startTS, dirtyData)
|
||||
}
|
||||
else {
|
||||
assert(compilationMode is CompilationMode.Rebuild) { "Unexpected compilation mode: ${compilationMode::class.java}" }
|
||||
changesRegistry.unknownChanges(currentBuildInfo.startTS)
|
||||
}
|
||||
protected open fun processChangesAfterBuild(
|
||||
compilationMode: CompilationMode,
|
||||
currentBuildInfo: BuildInfo,
|
||||
dirtyData: DirtyData
|
||||
) {
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
+1
-3
@@ -67,9 +67,7 @@ class IncrementalJsCompilerRunner(
|
||||
workingDir,
|
||||
"caches-js",
|
||||
cacheVersions,
|
||||
reporter,
|
||||
artifactChangesProvider = null,
|
||||
changesRegistry = null
|
||||
reporter
|
||||
) {
|
||||
override fun isICEnabled(): Boolean =
|
||||
IncrementalCompilation.isEnabled() && IncrementalCompilation.isEnabledForJs()
|
||||
|
||||
+60
-71
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.incremental
|
||||
|
||||
import com.intellij.lang.java.JavaLanguage
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiFileFactory
|
||||
@@ -39,8 +38,9 @@ import org.jetbrains.kotlin.config.IncrementalCompilation
|
||||
import org.jetbrains.kotlin.config.Services
|
||||
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
|
||||
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.incremental.multiproject.EmptyModulesApiHistory
|
||||
import org.jetbrains.kotlin.incremental.multiproject.ModulesApiHistory
|
||||
import org.jetbrains.kotlin.incremental.util.Either
|
||||
import org.jetbrains.kotlin.load.java.JavaClassesTracker
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
|
||||
@@ -64,6 +64,7 @@ fun makeIncrementally(
|
||||
val rootsWalk = sourceRoots.asSequence().flatMap { it.walk() }
|
||||
val files = rootsWalk.filter(File::isFile)
|
||||
val sourceFiles = files.filter { it.extension.toLowerCase() in allExtensions }.toList()
|
||||
val buildHistoryFile = File(cachesDir, "build-history.bin")
|
||||
|
||||
withIC {
|
||||
val compiler = IncrementalJvmCompilerRunner(
|
||||
@@ -72,7 +73,9 @@ fun makeIncrementally(
|
||||
versions, reporter,
|
||||
// Use precise setting in case of non-Gradle build
|
||||
usePreciseJavaTracking = true,
|
||||
localStateDirs = emptyList()
|
||||
localStateDirs = emptyList(),
|
||||
buildHistoryFile = buildHistoryFile,
|
||||
modulesApiHistory = EmptyModulesApiHistory
|
||||
)
|
||||
compiler.compile(sourceFiles, args, messageCollector, providedChangedFiles = null)
|
||||
}
|
||||
@@ -101,19 +104,15 @@ class IncrementalJvmCompilerRunner(
|
||||
cacheVersions: List<CacheVersion>,
|
||||
reporter: ICReporter,
|
||||
private var kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null,
|
||||
artifactChangesProvider: ArtifactChangesProvider? = null,
|
||||
changesRegistry: ChangesRegistry? = null,
|
||||
private val buildHistoryFile: File? = null,
|
||||
private val friendBuildHistoryFile: File? = null,
|
||||
private val usePreciseJavaTracking: Boolean,
|
||||
localStateDirs: Collection<File>
|
||||
private val buildHistoryFile: File,
|
||||
localStateDirs: Collection<File>,
|
||||
private val modulesApiHistory: ModulesApiHistory
|
||||
) : IncrementalCompilerRunner<K2JVMCompilerArguments, IncrementalJvmCachesManager>(
|
||||
workingDir,
|
||||
"caches-jvm",
|
||||
cacheVersions,
|
||||
reporter,
|
||||
artifactChangesProvider,
|
||||
changesRegistry,
|
||||
localStateDirs = localStateDirs
|
||||
) {
|
||||
override fun isICEnabled(): Boolean =
|
||||
@@ -166,41 +165,18 @@ class IncrementalJvmCompilerRunner(
|
||||
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile) ?: return CompilationMode.Rebuild { "No information on previous build" }
|
||||
reporter.report { "Last Kotlin Build info -- $lastBuildInfo" }
|
||||
|
||||
val changesFromFriend by lazy {
|
||||
val myLastTS = lastBuildInfo.startTS
|
||||
val storage = friendBuildHistoryFile?.let { BuildDiffsStorage.readFromFile(it, reporter) } ?: return@lazy ChangesEither.Unknown()
|
||||
val classpathChanges = getClasspathChanges(args.classpathAsList, changedFiles, lastBuildInfo)
|
||||
|
||||
val (prevDiffs, newDiffs) = storage.buildDiffs.partition { it.ts < myLastTS }
|
||||
if (prevDiffs.isEmpty()) return@lazy ChangesEither.Unknown()
|
||||
|
||||
val dirtyLookupSymbols = HashSet<LookupSymbol>()
|
||||
val dirtyClassesFqNames = HashSet<FqName>()
|
||||
for ((_, isIncremental, dirtyData) in newDiffs) {
|
||||
if (!isIncremental) return@lazy ChangesEither.Unknown()
|
||||
|
||||
dirtyLookupSymbols.addAll(dirtyData.dirtyLookupSymbols)
|
||||
dirtyClassesFqNames.addAll(dirtyData.dirtyClassesFqNames)
|
||||
@Suppress("UNUSED_VARIABLE") // for sealed when
|
||||
val unused = when (classpathChanges) {
|
||||
is ChangesEither.Unknown -> return CompilationMode.Rebuild {
|
||||
// todo: we can recompile all files incrementally (not cleaning caches), so rebuild won't propagate
|
||||
"Could not get classpath's changes${classpathChanges.reason?.let { ": $it" }}"
|
||||
}
|
||||
is ChangesEither.Known -> {
|
||||
markDirtyBy(classpathChanges.lookupSymbols)
|
||||
markDirtyBy(classpathChanges.fqNames)
|
||||
}
|
||||
|
||||
markDirtyBy(dirtyLookupSymbols)
|
||||
markDirtyBy(dirtyClassesFqNames)
|
||||
ChangesEither.Known(dirtyLookupSymbols, dirtyClassesFqNames)
|
||||
}
|
||||
val friendDirs = args.friendPaths?.map { File(it) } ?: emptyList()
|
||||
for (file in changedFiles.removed.asSequence() + changedFiles.modified.asSequence()) {
|
||||
if (!file.isClassFile()) continue
|
||||
|
||||
val isFriendClassFile = friendDirs.any { FileUtil.isAncestor(it, file, false) }
|
||||
if (isFriendClassFile && changesFromFriend is ChangesEither.Known) continue
|
||||
|
||||
return CompilationMode.Rebuild { "Cannot get changes from modified or removed class file: ${reporter.pathsAsString(file)}" }
|
||||
}
|
||||
|
||||
val classpathSet = args.classpathAsList.toHashSet()
|
||||
val modifiedClasspathEntries = changedFiles.modified.filter { it in classpathSet }
|
||||
val classpathChanges = getClasspathChanges(modifiedClasspathEntries, lastBuildInfo)
|
||||
if (classpathChanges !is ChangesEither.Known) {
|
||||
return CompilationMode.Rebuild { "could not get changes from modified classpath entries: ${reporter.pathsAsString(modifiedClasspathEntries)}" }
|
||||
}
|
||||
|
||||
if (!usePreciseJavaTracking) {
|
||||
@@ -221,8 +197,6 @@ class IncrementalJvmCompilerRunner(
|
||||
val removedClassesChanges = getRemovedClassesChanges(caches, changedFiles)
|
||||
|
||||
markDirtyBy(androidLayoutChanges)
|
||||
markDirtyBy(classpathChanges.lookupSymbols)
|
||||
markDirtyBy(classpathChanges.fqNames)
|
||||
markDirtyBy(removedClassesChanges.dirtyLookupSymbols)
|
||||
markDirtyBy(removedClassesChanges.dirtyClassesFqNames)
|
||||
|
||||
@@ -285,33 +259,49 @@ class IncrementalJvmCompilerRunner(
|
||||
}
|
||||
|
||||
private fun getClasspathChanges(
|
||||
modifiedClasspath: List<File>,
|
||||
lastBuildInfo: BuildInfo?
|
||||
classpath: List<File>,
|
||||
changedFiles: ChangedFiles.Known,
|
||||
lastBuildInfo: BuildInfo
|
||||
): ChangesEither {
|
||||
if (modifiedClasspath.isEmpty()) {
|
||||
reporter.report {"No classpath changes"}
|
||||
return ChangesEither.Known()
|
||||
val classpathSet = HashSet<File>()
|
||||
for (file in classpath) {
|
||||
when {
|
||||
file.isFile -> classpathSet.add(file)
|
||||
file.isDirectory -> file.walk().filterTo(classpathSet) { it.isFile }
|
||||
}
|
||||
}
|
||||
|
||||
val lastBuildTS = lastBuildInfo?.startTS
|
||||
if (lastBuildTS == null) {
|
||||
reporter.report {"Could not determine last build timestamp"}
|
||||
return ChangesEither.Unknown()
|
||||
}
|
||||
val modifiedClasspath = changedFiles.modified.filterTo(HashSet()) { it in classpathSet }
|
||||
val removedClasspath = changedFiles.removed.filterTo(HashSet()) { it in classpathSet }
|
||||
|
||||
// todo: removed classes could be processed normally
|
||||
if (removedClasspath.isNotEmpty()) return ChangesEither.Unknown("Some files are removed from classpath $removedClasspath")
|
||||
|
||||
if (modifiedClasspath.isEmpty()) return ChangesEither.Known()
|
||||
|
||||
val lastBuildTS = lastBuildInfo.startTS
|
||||
|
||||
val symbols = HashSet<LookupSymbol>()
|
||||
val fqNames = HashSet<FqName>()
|
||||
for (file in modifiedClasspath) {
|
||||
val diffs = artifactChangesProvider?.getChanges(file, lastBuildTS)
|
||||
|
||||
if (diffs == null) {
|
||||
reporter.report {"Could not get changes for file: $file"}
|
||||
return ChangesEither.Unknown()
|
||||
val historyFilesEither = modulesApiHistory.historyFilesForChangedFiles(modifiedClasspath)
|
||||
val historyFiles = when (historyFilesEither) {
|
||||
is Either.Success<Set<File>> -> historyFilesEither.value
|
||||
is Either.Error -> return ChangesEither.Unknown(historyFilesEither.reason)
|
||||
}
|
||||
|
||||
for (historyFile in historyFiles) {
|
||||
val allBuilds = BuildDiffsStorage.readDiffsFromFile(historyFile, reporter = reporter)
|
||||
?: return ChangesEither.Unknown("Could not read diffs from $historyFile")
|
||||
val (knownBuilds, newBuilds) = allBuilds.partition { it.ts <= lastBuildTS }
|
||||
if (knownBuilds.isEmpty()) {
|
||||
return ChangesEither.Unknown("No previously known builds for $historyFile")
|
||||
}
|
||||
|
||||
diffs.forEach {
|
||||
symbols.addAll(it.dirtyLookupSymbols)
|
||||
fqNames.addAll(it.dirtyClassesFqNames)
|
||||
for (buildDiff in newBuilds) {
|
||||
val dirtyData = buildDiff.dirtyData
|
||||
symbols.addAll(dirtyData.dirtyLookupSymbols)
|
||||
fqNames.addAll(dirtyData.dirtyClassesFqNames)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,16 +390,15 @@ class IncrementalJvmCompilerRunner(
|
||||
override fun additionalDirtyLookupSymbols(): Iterable<LookupSymbol> =
|
||||
javaFilesProcessor?.allChangedSymbols ?: emptyList()
|
||||
|
||||
override fun processChangesAfterBuild(compilationMode: CompilationMode, currentBuildInfo: BuildInfo, dirtyData: DirtyData) {
|
||||
super.processChangesAfterBuild(compilationMode, currentBuildInfo, dirtyData)
|
||||
|
||||
if (buildHistoryFile == null) return
|
||||
|
||||
override fun processChangesAfterBuild(
|
||||
compilationMode: CompilationMode,
|
||||
currentBuildInfo: BuildInfo,
|
||||
dirtyData: DirtyData
|
||||
) {
|
||||
val prevDiffs = BuildDiffsStorage.readFromFile(buildHistoryFile, reporter)?.buildDiffs ?: emptyList()
|
||||
val newDiff = if (compilationMode is CompilationMode.Incremental) {
|
||||
BuildDifference(currentBuildInfo.startTS, true, dirtyData)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
val emptyDirtyData = DirtyData()
|
||||
BuildDifference(currentBuildInfo.startTS, false, emptyDirtyData)
|
||||
}
|
||||
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.multiproject
|
||||
|
||||
import org.jetbrains.kotlin.incremental.DirtyData
|
||||
import java.io.File
|
||||
|
||||
interface ArtifactChangesProvider {
|
||||
fun getChanges(artifact: File, sinceTS: Long): Iterable<DirtyData>?
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.multiproject
|
||||
|
||||
import org.jetbrains.kotlin.incremental.DirtyData
|
||||
|
||||
interface ChangesRegistry {
|
||||
fun registerChanges(timestamp: Long, dirtyData: DirtyData)
|
||||
fun unknownChanges(timestamp: Long)
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2000-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.incremental.multiproject
|
||||
|
||||
import org.jetbrains.kotlin.daemon.common.IncrementalModuleInfo
|
||||
import org.jetbrains.kotlin.incremental.util.Either
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
interface ModulesApiHistory {
|
||||
fun historyFilesForChangedFiles(changedFiles: Set<File>): Either<Set<File>>
|
||||
}
|
||||
|
||||
object EmptyModulesApiHistory : ModulesApiHistory {
|
||||
override fun historyFilesForChangedFiles(changedFiles: Set<File>): Either<Set<File>> =
|
||||
Either.Error("Multi-module IC is not configured")
|
||||
}
|
||||
|
||||
class ModulesApiHistoryImpl(
|
||||
private val modulesInfo: IncrementalModuleInfo
|
||||
) : ModulesApiHistory {
|
||||
private val projectRootPath = Paths.get(modulesInfo.projectRoot.absolutePath)
|
||||
private val dirToHistoryFileCache = HashMap<File, File?>()
|
||||
|
||||
override fun historyFilesForChangedFiles(changedFiles: Set<File>): Either<Set<File>> {
|
||||
val result = HashSet<File>()
|
||||
val jarFiles = ArrayList<File>()
|
||||
val classFiles = ArrayList<File>()
|
||||
|
||||
for (file in changedFiles) {
|
||||
val extension = file.extension
|
||||
|
||||
when {
|
||||
extension.equals("class", ignoreCase = true) -> {
|
||||
classFiles.add(file)
|
||||
}
|
||||
extension.equals("jar", ignoreCase = true) -> {
|
||||
jarFiles.add(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (jar in jarFiles) {
|
||||
val historyEither = getBuildHistoryForJar(jar)
|
||||
when (historyEither) {
|
||||
is Either.Success<File> -> result.add(historyEither.value)
|
||||
is Either.Error -> return historyEither
|
||||
}
|
||||
}
|
||||
|
||||
val classFileDirs = classFiles.groupBy { it.parentFile }
|
||||
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 getBuildHistoryForDir(file: File): File? =
|
||||
dirToHistoryFileCache.getOrPut(file) {
|
||||
val module = modulesInfo.dirToModule[file]
|
||||
val parent = file.parentFile
|
||||
|
||||
when {
|
||||
module != null ->
|
||||
module.buildHistoryFile
|
||||
parent != null && projectRootPath.isParentOf(parent) ->
|
||||
getBuildHistoryForDir(parent)
|
||||
else ->
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getBuildHistoryForJar(jar: File): Either<File> =
|
||||
Either.Error("Cannot get changes for jar $jar")
|
||||
|
||||
private fun Path.isParentOf(path: Path) = path.startsWith(this)
|
||||
private fun Path.isParentOf(file: File) = this.isParentOf(Paths.get(file.absolutePath))
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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.incremental.util
|
||||
|
||||
sealed class Either<out T> {
|
||||
class Success<T>(val value: T) : Either<T>()
|
||||
class Error(val reason: String) : Either<Nothing>()
|
||||
}
|
||||
-10
@@ -106,16 +106,6 @@ class Android25ProjectHandler(kotlinConfigurationTools: KotlinConfigurationTools
|
||||
override fun addJavaSourceDirectoryToVariantModel(variantData: BaseVariant, javaSourceDirectory: File) =
|
||||
variantData.addJavaSourceFoldersToModel(javaSourceDirectory)
|
||||
|
||||
override fun configureMultiProjectIc(project: Project,
|
||||
variantData: BaseVariant,
|
||||
javaTask: AbstractCompile,
|
||||
kotlinTask: KotlinCompile,
|
||||
kotlinAfterJavaTask: KotlinCompile?) {
|
||||
//todo: No easy solution because of the absence of the output information in library modules
|
||||
// Though it is affordable not to implement this for the first previews, because the impact is tolerable
|
||||
// to some degree -- the dependent projects will rebuild non-incrementally when a library project changes
|
||||
}
|
||||
|
||||
override fun getResDirectories(variantData: BaseVariant): List<File> {
|
||||
return variantData.mergeResources?.computeResourceSetList0() ?: emptyList()
|
||||
}
|
||||
|
||||
+10
-16
@@ -6,8 +6,6 @@ import org.jetbrains.kotlin.config.Services
|
||||
import org.jetbrains.kotlin.gradle.tasks.GradleMessageCollector
|
||||
import org.jetbrains.kotlin.gradle.tasks.findToolsJar
|
||||
import org.jetbrains.kotlin.incremental.ChangedFiles
|
||||
import org.jetbrains.kotlin.incremental.ICReporter
|
||||
import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifferenceRegistryProvider
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
|
||||
@@ -27,18 +25,14 @@ internal open class GradleCompilerEnvironment(
|
||||
}
|
||||
|
||||
internal class GradleIncrementalCompilerEnvironment(
|
||||
compilerClasspath: List<File>,
|
||||
val changedFiles: ChangedFiles,
|
||||
val reporter: ICReporter,
|
||||
val workingDir: File,
|
||||
messageCollector: GradleMessageCollector,
|
||||
outputItemsCollector: OutputItemsCollector,
|
||||
compilerArgs: CommonCompilerArguments,
|
||||
val kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null,
|
||||
val artifactDifferenceRegistryProvider: ArtifactDifferenceRegistryProvider? = null,
|
||||
val artifactFile: File? = null,
|
||||
val buildHistoryFile: File? = null,
|
||||
val friendBuildHistoryFile: File? = 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)
|
||||
|
||||
-34
@@ -7,14 +7,8 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.daemon.client.reportFromDaemon
|
||||
import org.jetbrains.kotlin.daemon.common.*
|
||||
import org.jetbrains.kotlin.daemon.incremental.toDirtyData
|
||||
import org.jetbrains.kotlin.daemon.incremental.toSimpleDirtyData
|
||||
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
|
||||
import org.jetbrains.kotlin.gradle.plugin.kotlinWarn
|
||||
import org.jetbrains.kotlin.incremental.DirtyData
|
||||
import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifference
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
import java.rmi.Remote
|
||||
import java.rmi.server.UnicastRemoteObject
|
||||
@@ -67,32 +61,4 @@ internal class GradleIncrementalCompilerServicesFacadeImpl(
|
||||
override fun revert() {
|
||||
environment.kaptAnnotationsFileUpdater!!.revert()
|
||||
}
|
||||
|
||||
override fun getChanges(artifact: File, sinceTS: Long): Iterable<SimpleDirtyData>? {
|
||||
val artifactChanges = environment.artifactDifferenceRegistryProvider?.withRegistry(environment.reporter) { registry ->
|
||||
registry[artifact]
|
||||
} ?: return null
|
||||
|
||||
val (beforeLastBuild, afterLastBuild) = artifactChanges.partition { it.buildTS < sinceTS }
|
||||
if (beforeLastBuild.isEmpty()) return null
|
||||
|
||||
return afterLastBuild.map { it.dirtyData.toSimpleDirtyData() }
|
||||
}
|
||||
|
||||
override fun registerChanges(timestamp: Long, dirtyData: SimpleDirtyData) {
|
||||
val artifactFile = environment.artifactFile ?: return
|
||||
|
||||
environment.artifactDifferenceRegistryProvider?.withRegistry(environment.reporter) { registry ->
|
||||
registry.add(artifactFile, ArtifactDifference(timestamp, dirtyData.toDirtyData()))
|
||||
}
|
||||
}
|
||||
|
||||
override fun unknownChanges(timestamp: Long) {
|
||||
val artifactFile = environment.artifactFile ?: return
|
||||
|
||||
environment.artifactDifferenceRegistryProvider?.withRegistry(environment.reporter) { registry ->
|
||||
registry.remove(artifactFile)
|
||||
registry.add(artifactFile, ArtifactDifference(timestamp, DirtyData()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+41
-3
@@ -28,14 +28,19 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.jetbrains.kotlin.config.Services
|
||||
import org.jetbrains.kotlin.daemon.client.CompileServiceSession
|
||||
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.KotlinCompile
|
||||
import org.jetbrains.kotlin.incremental.*
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.PrintStream
|
||||
import java.lang.ref.WeakReference
|
||||
import java.net.URLClassLoader
|
||||
import java.rmi.RemoteException
|
||||
|
||||
@@ -267,10 +272,10 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
|
||||
requestedCompilationResults = arrayOf(CompilationResultCategory.IC_COMPILE_ITERATION.code),
|
||||
compilerMode = CompilerMode.INCREMENTAL_COMPILER,
|
||||
targetPlatform = targetPlatform,
|
||||
resultDifferenceFile = environment.buildHistoryFile,
|
||||
friendDifferenceFile = environment.friendBuildHistoryFile,
|
||||
usePreciseJavaTracking = environment.usePreciseJavaTracking,
|
||||
localStateDirs = environment.localStateDirs
|
||||
localStateDirs = environment.localStateDirs,
|
||||
buildHistoryFile = environment.buildHistoryFile,
|
||||
modulesInfo = buildModulesInfo(project.gradle)
|
||||
)
|
||||
|
||||
log.info("Options for KOTLIN DAEMON: $compilationOptions")
|
||||
@@ -340,6 +345,39 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Volatile
|
||||
private var cachedGradle = WeakReference<Gradle>(null)
|
||||
@Volatile
|
||||
private var cachedModulesInfo: IncrementalModuleInfo? = null
|
||||
|
||||
@Synchronized
|
||||
private fun buildModulesInfo(gradle: Gradle): IncrementalModuleInfo {
|
||||
if (cachedGradle.get() === gradle && cachedModulesInfo != null) return cachedModulesInfo!!
|
||||
|
||||
val dirToModule = HashMap<File, IncrementalModuleEntry>()
|
||||
val nameToModules = HashMap<String, HashSet<IncrementalModuleEntry>>()
|
||||
|
||||
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
|
||||
nameToModules.getOrPut(module.name) { HashSet() }.add(module)
|
||||
}
|
||||
}
|
||||
|
||||
return IncrementalModuleInfo(gradle.rootProject.projectDir, dirToModule, nameToModules)
|
||||
.also {
|
||||
cachedGradle = WeakReference(gradle)
|
||||
cachedModulesInfo = it
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
internal fun clearBuildModulesInfo() {
|
||||
cachedGradle = WeakReference<Gradle>(null)
|
||||
cachedModulesInfo = null
|
||||
}
|
||||
|
||||
// created once per gradle instance
|
||||
// when gradle daemon dies, kotlin daemon should die too
|
||||
// however kotlin daemon (if it idles enough) can die before gradle daemon dies
|
||||
|
||||
+2
-46
@@ -27,17 +27,11 @@ import org.jetbrains.kotlin.compilerRunner.DELETED_SESSION_FILE_PREFIX
|
||||
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner
|
||||
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.utils.isWindows
|
||||
import org.jetbrains.kotlin.incremental.BuildCacheStorage
|
||||
import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifferenceRegistryProvider
|
||||
import org.jetbrains.kotlin.incremental.relativeToRoot
|
||||
import org.jetbrains.kotlin.incremental.stackTraceStr
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.sumByLong
|
||||
import java.io.File
|
||||
import java.lang.management.ManagementFactory
|
||||
|
||||
|
||||
|
||||
internal class KotlinGradleBuildServices private constructor(gradle: Gradle): BuildAdapter() {
|
||||
internal class KotlinGradleBuildServices private constructor(gradle: Gradle) : BuildAdapter() {
|
||||
companion object {
|
||||
private val CLASS_NAME = KotlinGradleBuildServices::class.java.simpleName
|
||||
const val FORCE_SYSTEM_GC_MESSAGE = "Forcing System.gc()"
|
||||
@@ -72,13 +66,8 @@ internal class KotlinGradleBuildServices private constructor(gradle: Gradle): Bu
|
||||
private val log = Logging.getLogger(this.javaClass)
|
||||
private val cleanup = CompilerServicesCleanup()
|
||||
private var startMemory: Long? = null
|
||||
internal val workingDir: File by lazy { File(gradle.rootProject.buildDir, "kotlin-build").apply { mkdirs() } }
|
||||
private val buildCacheStorage: BuildCacheStorage by lazy { BuildCacheStorage(workingDir) }
|
||||
private val shouldReportMemoryUsage = System.getProperty(SHOULD_REPORT_MEMORY_USAGE_PROPERTY) != null
|
||||
|
||||
internal val artifactDifferenceRegistryProvider: ArtifactDifferenceRegistryProvider
|
||||
get() = buildCacheStorage
|
||||
|
||||
// There is function with the same name in BuildAdapter,
|
||||
// but it is called before any plugin can attach build listener
|
||||
fun buildStarted() {
|
||||
@@ -99,6 +88,7 @@ internal class KotlinGradleBuildServices private constructor(gradle: Gradle): Bu
|
||||
else {
|
||||
log.kotlinDebug("Skipping kotlin cleanup since compiler wasn't called")
|
||||
}
|
||||
GradleCompilerRunner.clearBuildModulesInfo()
|
||||
|
||||
val rootProject = gradle.rootProject
|
||||
val sessionsDir = GradleCompilerRunner.sessionsDir(rootProject)
|
||||
@@ -125,45 +115,11 @@ internal class KotlinGradleBuildServices private constructor(gradle: Gradle): Bu
|
||||
log.lifecycle("[KOTLIN][PERF] Used memory after build: $endMem kb (difference since build start: ${"%+d".format(endMem - startMem)} kb)")
|
||||
}
|
||||
|
||||
closeArtifactDifferenceRegistry()
|
||||
gradle.removeListener(this)
|
||||
instance = null
|
||||
log.kotlinDebug(DISPOSE_MESSAGE)
|
||||
}
|
||||
|
||||
private fun closeArtifactDifferenceRegistry() {
|
||||
var caughtError = false
|
||||
try {
|
||||
if (workingDir.exists()) {
|
||||
// The working directory may have been removed by the clean task.
|
||||
// https://youtrack.jetbrains.com/issue/KT-16298
|
||||
buildCacheStorage.flush(memoryCachesOnly = false)
|
||||
}
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
log.kotlinDebug { "Error trying to flush artifact difference registry: ${e.stackTraceStr}" }
|
||||
caughtError = true
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
buildCacheStorage.close()
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
log.kotlinDebug { "Error trying to close artifact difference registry: ${e.stackTraceStr}" }
|
||||
caughtError = true
|
||||
}
|
||||
}
|
||||
|
||||
if (caughtError && workingDir.exists()) {
|
||||
try {
|
||||
workingDir.deleteRecursively()
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
log.kotlinDebug { "Error trying to delete kotlin-build $workingDir: ${e.stackTraceStr}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUsedMemoryKb(): Long? {
|
||||
if (!shouldReportMemoryUsage) return null
|
||||
|
||||
|
||||
+6
-47
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.gradle.internal.*
|
||||
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedClassesDir
|
||||
import org.jetbrains.kotlin.gradle.tasks.*
|
||||
import org.jetbrains.kotlin.gradle.utils.*
|
||||
import org.jetbrains.kotlin.incremental.configureMultiProjectIncrementalCompilation
|
||||
import org.jetbrains.kotlin.scripting.gradle.ScriptingGradleSubplugin
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
@@ -129,8 +128,7 @@ internal class Kotlin2JvmSourceSetProcessor(
|
||||
sourceSet: SourceSet,
|
||||
tasksProvider: KotlinTasksProvider,
|
||||
kotlinSourceSetProvider: KotlinSourceSetProvider,
|
||||
private val kotlinPluginVersion: String,
|
||||
private val kotlinGradleBuildServices: KotlinGradleBuildServices
|
||||
private val kotlinPluginVersion: String
|
||||
) : KotlinSourceSetProcessor<KotlinCompile>(
|
||||
project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider,
|
||||
dslExtensionName = KOTLIN_DSL_NAME,
|
||||
@@ -189,11 +187,6 @@ internal class Kotlin2JvmSourceSetProcessor(
|
||||
syncOutputTask = createSyncOutputTask(project, kotlinTask, javaTask, kotlinAfterJavaTask, sourceSetName)
|
||||
}
|
||||
|
||||
val artifactFile = project.tryGetSingleArtifact()
|
||||
configureMultiProjectIncrementalCompilation(project, kotlinTask, javaTask, kotlinAfterJavaTask,
|
||||
kotlinGradleBuildServices.artifactDifferenceRegistryProvider,
|
||||
artifactFile)
|
||||
|
||||
if (project.pluginManager.hasPlugin("java-library") && sourceSetName == SourceSet.MAIN_SOURCE_SET_NAME) {
|
||||
val (classesProviderTask, classesDirectory) = when {
|
||||
isSeparateClassesDirSupported -> (kotlinAfterJavaTask ?: kotlinTask).let { it to it.destinationDir }
|
||||
@@ -203,28 +196,10 @@ internal class Kotlin2JvmSourceSetProcessor(
|
||||
registerKotlinOutputForJavaLibrary(classesDirectory, classesProviderTask)
|
||||
}
|
||||
|
||||
// To make Gradle read this location from the task's @LocalState property and clean it on cache hit:
|
||||
kotlinTask.buildServicesWorkingDir = Callable { kotlinGradleBuildServices.workingDir }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.tryGetSingleArtifact(): File? {
|
||||
val log = logger
|
||||
log.kotlinDebug { "Trying to determine single artifact for project $path" }
|
||||
|
||||
val archives = configurations.findByName("archives")
|
||||
if (archives == null) {
|
||||
log.kotlinDebug { "Could not find 'archives' configuration for project $path" }
|
||||
return null
|
||||
}
|
||||
|
||||
val artifacts = archives.artifacts.files.files
|
||||
log.kotlinDebug { "All artifacts for project $path: [${artifacts.joinToString()}]" }
|
||||
|
||||
return if (artifacts.size == 1) artifacts.first() else null
|
||||
}
|
||||
|
||||
private fun registerKotlinOutputForJavaLibrary(outputDir: File, taskDependency: Task): Boolean {
|
||||
val configuration = project.configurations.getByName("apiElements")
|
||||
|
||||
@@ -420,11 +395,10 @@ internal abstract class AbstractKotlinPlugin(
|
||||
internal open class KotlinPlugin(
|
||||
tasksProvider: KotlinTasksProvider,
|
||||
kotlinSourceSetProvider: KotlinSourceSetProvider,
|
||||
kotlinPluginVersion: String,
|
||||
private val kotlinGradleBuildServices: KotlinGradleBuildServices
|
||||
kotlinPluginVersion: String
|
||||
) : AbstractKotlinPlugin(tasksProvider, kotlinSourceSetProvider, kotlinPluginVersion) {
|
||||
override fun buildSourceSetProcessor(project: Project, javaBasePlugin: JavaBasePlugin, sourceSet: SourceSet, kotlinPluginVersion: String) =
|
||||
Kotlin2JvmSourceSetProcessor(project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider, kotlinPluginVersion, kotlinGradleBuildServices)
|
||||
Kotlin2JvmSourceSetProcessor(project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider, kotlinPluginVersion)
|
||||
|
||||
override fun apply(project: Project) {
|
||||
project.createKaptExtension()
|
||||
@@ -454,8 +428,7 @@ internal open class Kotlin2JsPlugin(
|
||||
internal open class KotlinAndroidPlugin(
|
||||
val tasksProvider: KotlinTasksProvider,
|
||||
private val kotlinSourceSetProvider: KotlinSourceSetProvider,
|
||||
private val kotlinPluginVersion: String,
|
||||
private val kotlinGradleBuildServices: KotlinGradleBuildServices
|
||||
private val kotlinPluginVersion: String
|
||||
) : Plugin<Project> {
|
||||
|
||||
override fun apply(project: Project) {
|
||||
@@ -470,8 +443,7 @@ internal open class KotlinAndroidPlugin(
|
||||
val kotlinTools = KotlinConfigurationTools(
|
||||
kotlinSourceSetProvider,
|
||||
tasksProvider,
|
||||
kotlinPluginVersion,
|
||||
kotlinGradleBuildServices)
|
||||
kotlinPluginVersion)
|
||||
|
||||
val legacyVersionThreshold = "2.5.0"
|
||||
|
||||
@@ -492,17 +464,12 @@ internal open class KotlinAndroidPlugin(
|
||||
|
||||
class KotlinConfigurationTools internal constructor(val kotlinSourceSetProvider: KotlinSourceSetProvider,
|
||||
val kotlinTasksProvider: KotlinTasksProvider,
|
||||
val kotlinPluginVersion: String,
|
||||
val kotlinGradleBuildServices: KotlinGradleBuildServices)
|
||||
val kotlinPluginVersion: String)
|
||||
|
||||
/** Part of Android configuration, that works only with the old public API.
|
||||
* @see [LegacyAndroidAndroidProjectHandler] that is implemented with the old internal API and [AndroidGradle25VariantProcessor] that works
|
||||
* with the new public API */
|
||||
abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationTools: KotlinConfigurationTools) {
|
||||
|
||||
protected val artifactDifferenceRegistryProvider get() =
|
||||
kotlinConfigurationTools.kotlinGradleBuildServices.artifactDifferenceRegistryProvider
|
||||
|
||||
protected val logger = Logging.getLogger(this.javaClass)
|
||||
|
||||
abstract fun forEachVariant(project: Project, action: (V) -> Unit): Unit
|
||||
@@ -525,12 +492,6 @@ abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationT
|
||||
kotlinTask: KotlinCompile,
|
||||
kotlinAfterJavaTask: KotlinCompile?): Unit
|
||||
|
||||
protected abstract fun configureMultiProjectIc(project: Project,
|
||||
variantData: V,
|
||||
javaTask: AbstractCompile,
|
||||
kotlinTask: KotlinCompile,
|
||||
kotlinAfterJavaTask: KotlinCompile?)
|
||||
|
||||
protected abstract fun wrapVariantDataForKapt(variantData: V): KaptVariantData<V>
|
||||
|
||||
fun handleProject(project: Project) {
|
||||
@@ -648,8 +609,6 @@ abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationT
|
||||
wireKotlinTasks(project, androidPlugin, androidExt, variantData, javaTask,
|
||||
kotlinTask, kotlinAfterJavaTask)
|
||||
|
||||
configureMultiProjectIc(project, variantData, javaTask, kotlinTask, kotlinAfterJavaTask)
|
||||
|
||||
val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
|
||||
project, kotlinTask, javaTask, wrapVariantDataForKapt(variantData), this, null)
|
||||
|
||||
|
||||
+2
-2
@@ -56,7 +56,7 @@ abstract class KotlinBasePluginWrapper(protected val fileResolver: FileResolver)
|
||||
|
||||
open class KotlinPluginWrapper @Inject constructor(fileResolver: FileResolver): KotlinBasePluginWrapper(fileResolver) {
|
||||
override fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices) =
|
||||
KotlinPlugin(KotlinTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion, kotlinGradleBuildServices)
|
||||
KotlinPlugin(KotlinTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion)
|
||||
|
||||
override val projectExtensionClass: KClass<out KotlinJvmProjectExtension>
|
||||
get() = KotlinJvmProjectExtension::class
|
||||
@@ -69,7 +69,7 @@ open class KotlinCommonPluginWrapper @Inject constructor(fileResolver: FileResol
|
||||
|
||||
open class KotlinAndroidPluginWrapper @Inject constructor(fileResolver: FileResolver): KotlinBasePluginWrapper(fileResolver) {
|
||||
override fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices) =
|
||||
KotlinAndroidPlugin(AndroidTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion, kotlinGradleBuildServices)
|
||||
KotlinAndroidPlugin(AndroidTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion)
|
||||
}
|
||||
|
||||
open class Kotlin2JsPluginWrapper @Inject constructor(fileResolver: FileResolver): KotlinBasePluginWrapper(fileResolver) {
|
||||
|
||||
-27
@@ -16,8 +16,6 @@ import org.jetbrains.kotlin.gradle.internal.registerGeneratedJavaSource
|
||||
import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.utils.checkedReflection
|
||||
import org.jetbrains.kotlin.incremental.configureMultiProjectIncrementalCompilation
|
||||
import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifferenceRegistryProviderAndroidWrapper
|
||||
import java.io.File
|
||||
|
||||
internal class LegacyAndroidAndroidProjectHandler(kotlinConfigurationTools: KotlinConfigurationTools)
|
||||
@@ -99,18 +97,6 @@ internal class LegacyAndroidAndroidProjectHandler(kotlinConfigurationTools: Kotl
|
||||
javaSourceDirectory: File) =
|
||||
variantData.addJavaSourceFoldersToModel(javaSourceDirectory)
|
||||
|
||||
override fun configureMultiProjectIc(project: Project, variantData: BaseVariantData<out BaseVariantOutputData>, javaTask: AbstractCompile, kotlinTask: KotlinCompile, kotlinAfterJavaTask: KotlinCompile?) {
|
||||
if ((kotlinAfterJavaTask ?: kotlinTask).incremental) {
|
||||
val artifactFile = project.tryGetSingleArtifact(variantData)
|
||||
val artifactDifferenceRegistryProvider = ArtifactDifferenceRegistryProviderAndroidWrapper(
|
||||
artifactDifferenceRegistryProvider,
|
||||
{ AndroidGradleWrapper.getJarToAarMapping(variantData) }
|
||||
)
|
||||
configureMultiProjectIncrementalCompilation(project, kotlinTask, javaTask, kotlinAfterJavaTask,
|
||||
artifactDifferenceRegistryProvider, artifactFile)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getTestedVariantData(variantData: BaseVariantData<*>): BaseVariantData<*>? =
|
||||
((variantData as? TestVariantData)?.testedVariantData as? BaseVariantData<*>)
|
||||
|
||||
@@ -118,19 +104,6 @@ internal class LegacyAndroidAndroidProjectHandler(kotlinConfigurationTools: Kotl
|
||||
return variantData.mergeResourcesTask?.rawInputFolders?.toList() ?: emptyList()
|
||||
}
|
||||
|
||||
private fun Project.tryGetSingleArtifact(variantData: BaseVariantData<*>): File? {
|
||||
val log = logger
|
||||
log.kotlinDebug { "Trying to determine single artifact for project $path" }
|
||||
|
||||
val outputs = variantData.outputs
|
||||
if (outputs.size != 1) {
|
||||
log.kotlinDebug { "Output count != 1 for variant: ${outputs.map { it.outputFile.relativeTo(rootDir).path }.joinToString()}" }
|
||||
return null
|
||||
}
|
||||
|
||||
return variantData.outputs.first().outputFile
|
||||
}
|
||||
|
||||
private val BaseVariantData<*>.sourceProviders: List<SourceProvider>
|
||||
get() = variantConfiguration.sortedSourceProviders
|
||||
|
||||
|
||||
+10
-31
@@ -41,12 +41,9 @@ import org.jetbrains.kotlin.gradle.internal.prepareCompilerArguments
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.utils.ParsedGradleVersion
|
||||
import org.jetbrains.kotlin.incremental.*
|
||||
import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifferenceRegistry
|
||||
import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifferenceRegistryProvider
|
||||
import org.jetbrains.kotlin.utils.LibraryUtils
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import java.util.concurrent.Callable
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
const val ANNOTATIONS_PLUGIN_NAME = "org.jetbrains.kotlin.kapt"
|
||||
@@ -99,6 +96,9 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
|
||||
logger.kotlinDebug { "Set $this.incremental=$value" }
|
||||
}
|
||||
|
||||
@get:Internal
|
||||
internal val buildHistoryFile: File get() = File(taskBuildDirectory, "build-history.bin")
|
||||
|
||||
@get:Internal
|
||||
internal val pluginOptions = CompilerPluginOptions()
|
||||
|
||||
@@ -267,9 +267,6 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
|
||||
|
||||
private var kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null
|
||||
|
||||
@get:Internal
|
||||
val buildHistoryFile: File get() = File(taskBuildDirectory, "build-history.bin")
|
||||
|
||||
@get:Internal
|
||||
val kaptOptions = KaptOptions()
|
||||
|
||||
@@ -281,12 +278,6 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
|
||||
@get:Optional
|
||||
var javaPackagePrefix: String? = null
|
||||
|
||||
@get:Internal
|
||||
internal var artifactDifferenceRegistryProvider: ArtifactDifferenceRegistryProvider? = null
|
||||
|
||||
@get:Internal
|
||||
internal var artifactFile: File? = null
|
||||
|
||||
@get:Input
|
||||
var usePreciseJavaTracking: Boolean = true
|
||||
set(value) {
|
||||
@@ -294,9 +285,6 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
|
||||
logger.kotlinDebug { "Set $this.usePreciseJavaTracking=$value" }
|
||||
}
|
||||
|
||||
@get:LocalState @get:Optional
|
||||
internal var buildServicesWorkingDir: Callable<File>? = null
|
||||
|
||||
init {
|
||||
incremental = true
|
||||
}
|
||||
@@ -350,23 +338,19 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
|
||||
val messageCollector = GradleMessageCollector(logger)
|
||||
val outputItemCollector = OutputItemsCollectorImpl()
|
||||
val compilerRunner = GradleCompilerRunner(project)
|
||||
val reporter = GradleICReporter(project.rootProject.projectDir)
|
||||
|
||||
val environment = when {
|
||||
!incremental ->
|
||||
GradleCompilerEnvironment(computedCompilerClasspath, messageCollector, outputItemCollector, args)
|
||||
else -> {
|
||||
logger.info(USING_INCREMENTAL_COMPILATION_MESSAGE)
|
||||
val friendTask = friendTaskName?.let { project.tasks.findByName(it) as? KotlinCompile }
|
||||
GradleIncrementalCompilerEnvironment(
|
||||
computedCompilerClasspath,
|
||||
if (hasFilesInTaskBuildDirectory()) changedFiles else ChangedFiles.Unknown(),
|
||||
reporter, taskBuildDirectory,
|
||||
messageCollector, outputItemCollector, args, kaptAnnotationsFileUpdater,
|
||||
artifactDifferenceRegistryProvider,
|
||||
artifactFile = artifactFile,
|
||||
taskBuildDirectory,
|
||||
messageCollector, outputItemCollector, args,
|
||||
buildHistoryFile = buildHistoryFile,
|
||||
friendBuildHistoryFile = friendTask?.buildHistoryFile,
|
||||
kaptAnnotationsFileUpdater = kaptAnnotationsFileUpdater,
|
||||
usePreciseJavaTracking = usePreciseJavaTracking,
|
||||
localStateDirs = outputDirectories
|
||||
)
|
||||
@@ -386,18 +370,11 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
|
||||
environment)
|
||||
|
||||
processCompilerExitCode(exitCode)
|
||||
artifactDifferenceRegistryProvider?.withRegistry(reporter) {
|
||||
it.flush(true)
|
||||
}
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
cleanupOnError()
|
||||
artifactDifferenceRegistryProvider?.clean()
|
||||
throw e
|
||||
}
|
||||
finally {
|
||||
artifactDifferenceRegistryProvider?.withRegistry(reporter, ArtifactDifferenceRegistry::close)
|
||||
}
|
||||
anyClassesCompiled = true
|
||||
}
|
||||
|
||||
@@ -525,8 +502,10 @@ open class Kotlin2JsCompile() : AbstractKotlinCompile<K2JSCompilerArguments>(),
|
||||
GradleIncrementalCompilerEnvironment(
|
||||
computedCompilerClasspath,
|
||||
if (hasFilesInTaskBuildDirectory()) changedFiles else ChangedFiles.Unknown(),
|
||||
reporter, taskBuildDirectory,
|
||||
messageCollector, outputItemCollector, args)
|
||||
taskBuildDirectory,
|
||||
messageCollector, outputItemCollector, args,
|
||||
buildHistoryFile = buildHistoryFile
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
GradleCompilerEnvironment(computedCompilerClasspath, messageCollector, outputItemCollector, args)
|
||||
|
||||
+13
-13
@@ -23,19 +23,19 @@ import org.jetbrains.kotlin.gradle.plugin.mapKotlinTaskProperties
|
||||
|
||||
internal open class KotlinTasksProvider {
|
||||
fun createKotlinJVMTask(project: Project, name: String, sourceSetName: String): KotlinCompile =
|
||||
project.tasks.create(name, KotlinCompile::class.java).apply {
|
||||
configure(project, sourceSetName)
|
||||
}
|
||||
project.tasks.create(name, KotlinCompile::class.java).apply {
|
||||
configure(project, sourceSetName)
|
||||
}
|
||||
|
||||
fun createKotlinJSTask(project: Project, name: String, sourceSetName: String): Kotlin2JsCompile =
|
||||
project.tasks.create(name, Kotlin2JsCompile::class.java).apply {
|
||||
configure(project, sourceSetName)
|
||||
}
|
||||
project.tasks.create(name, Kotlin2JsCompile::class.java).apply {
|
||||
configure(project, sourceSetName)
|
||||
}
|
||||
|
||||
fun createKotlinCommonTask(project: Project, name: String, sourceSetName: String): KotlinCompileCommon =
|
||||
project.tasks.create(name, KotlinCompileCommon::class.java).apply {
|
||||
configure(project, sourceSetName)
|
||||
}
|
||||
project.tasks.create(name, KotlinCompileCommon::class.java).apply {
|
||||
configure(project, sourceSetName)
|
||||
}
|
||||
|
||||
private fun AbstractKotlinCompile<*>.configure(project: Project, sourceSetName: String) {
|
||||
this.sourceSetName = sourceSetName
|
||||
@@ -44,20 +44,20 @@ internal open class KotlinTasksProvider {
|
||||
}
|
||||
|
||||
protected open val taskToFriendTaskMapper: TaskToFriendTaskMapper =
|
||||
RegexTaskToFriendTaskMapper.Default()
|
||||
RegexTaskToFriendTaskMapper.Default()
|
||||
}
|
||||
|
||||
internal class KotlinCommonTasksProvider : KotlinTasksProvider() {
|
||||
override val taskToFriendTaskMapper: TaskToFriendTaskMapper =
|
||||
RegexTaskToFriendTaskMapper.Common()
|
||||
RegexTaskToFriendTaskMapper.Common()
|
||||
}
|
||||
|
||||
internal class Kotlin2JsTasksProvider : KotlinTasksProvider() {
|
||||
override val taskToFriendTaskMapper: TaskToFriendTaskMapper =
|
||||
RegexTaskToFriendTaskMapper.JavaScript()
|
||||
RegexTaskToFriendTaskMapper.JavaScript()
|
||||
}
|
||||
|
||||
internal class AndroidTasksProvider : KotlinTasksProvider() {
|
||||
override val taskToFriendTaskMapper: TaskToFriendTaskMapper =
|
||||
RegexTaskToFriendTaskMapper.Android()
|
||||
RegexTaskToFriendTaskMapper.Android()
|
||||
}
|
||||
-108
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.gradle.api.logging.Logging
|
||||
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
|
||||
import org.jetbrains.kotlin.incremental.CacheVersion
|
||||
import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifferenceRegistry
|
||||
import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifferenceRegistryImpl
|
||||
import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifferenceRegistryProvider
|
||||
import org.jetbrains.kotlin.incremental.stackTraceStr
|
||||
import org.jetbrains.kotlin.incremental.storage.BasicMapsOwner
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* "Global" cache holder. Should be created once per root project.
|
||||
*/
|
||||
internal class BuildCacheStorage(private val workingDir: File) : BasicMapsOwner(workingDir), ArtifactDifferenceRegistryProvider {
|
||||
companion object {
|
||||
private val OWN_VERSION = 0
|
||||
private val ARTIFACT_DIFFERENCE = "artifact-difference"
|
||||
}
|
||||
|
||||
private val log = Logging.getLogger(this.javaClass)
|
||||
private val versionFile = File(workingDir, "version.txt")
|
||||
private val version = CacheVersion(
|
||||
OWN_VERSION,
|
||||
versionFile,
|
||||
whenVersionChanged = CacheVersion.Action.REBUILD_ALL_KOTLIN,
|
||||
whenTurnedOff = CacheVersion.Action.REBUILD_ALL_KOTLIN,
|
||||
whenTurnedOn = CacheVersion.Action.REBUILD_ALL_KOTLIN,
|
||||
// assume it's always enabled for simplicity (if IC is not enabled, just don't write to cache)
|
||||
isEnabled = { true })
|
||||
|
||||
@Volatile
|
||||
private var artifactDifferenceRegistry: ArtifactDifferenceRegistryImpl? = null
|
||||
|
||||
@Synchronized
|
||||
override fun <T> withRegistry(report: (String)->Unit, fn: (ArtifactDifferenceRegistry)->T): T? {
|
||||
try {
|
||||
if (artifactDifferenceRegistry == null) {
|
||||
artifactDifferenceRegistry = registerMap(ArtifactDifferenceRegistryImpl(ARTIFACT_DIFFERENCE.storageFile))
|
||||
}
|
||||
|
||||
return fn(artifactDifferenceRegistry!!)
|
||||
}
|
||||
catch (e1: Throwable) {
|
||||
report("Error accessing artifact file difference registry: ${e1.stackTraceStr}}")
|
||||
report("Cleaning artifact difference storage and trying again")
|
||||
clean()
|
||||
|
||||
try {
|
||||
artifactDifferenceRegistry = registerMap(ArtifactDifferenceRegistryImpl(ARTIFACT_DIFFERENCE.storageFile))
|
||||
return fn(artifactDifferenceRegistry!!)
|
||||
}
|
||||
catch (e2: Throwable) {
|
||||
report("Second error accessing artifact file difference registry: ${e2.stackTraceStr}}")
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
init {
|
||||
if (version.checkVersion() != CacheVersion.Action.DO_NOTHING) {
|
||||
log.kotlinDebug { "Cache version is not up-to-date" }
|
||||
clean()
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun clean() {
|
||||
try {
|
||||
close()
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
log.kotlinDebug { "Exception while closing caches: ${e.stackTraceStr}" }
|
||||
}
|
||||
|
||||
workingDir.deleteRecursively()
|
||||
workingDir.mkdirs()
|
||||
versionFile.delete()
|
||||
}
|
||||
|
||||
override fun flush(memoryCachesOnly: Boolean) {
|
||||
super.flush(memoryCachesOnly)
|
||||
version.saveIfNeeded()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
super.close()
|
||||
artifactDifferenceRegistry = null
|
||||
}
|
||||
}
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.gradle.api.tasks.compile.AbstractCompile
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifferenceRegistryProvider
|
||||
import java.io.File
|
||||
|
||||
internal fun configureMultiProjectIncrementalCompilation(
|
||||
project: Project,
|
||||
kotlinTask: KotlinCompile,
|
||||
javaTask: AbstractCompile,
|
||||
kotlinAfterJavaTask: KotlinCompile?,
|
||||
artifactDifferenceRegistryProvider: ArtifactDifferenceRegistryProvider,
|
||||
artifactFile: File?
|
||||
) {
|
||||
val log: Logger = kotlinTask.logger
|
||||
log.kotlinDebug { "Configuring multi-project incremental compilation for project ${project.path}" }
|
||||
|
||||
fun cannotPerformMultiProjectIC(reason: String) {
|
||||
log.kotlinDebug {
|
||||
"Multi-project kotlin incremental compilation won't be performed for projects that depend on ${project.path}: $reason"
|
||||
}
|
||||
if (artifactFile != null) {
|
||||
artifactDifferenceRegistryProvider.withRegistry({log.kotlinDebug {it}}) {
|
||||
it.remove(artifactFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun isUnknownTaskOutputtingToJavaDestination(task: Task): Boolean {
|
||||
return task !is JavaCompile &&
|
||||
task !is KotlinCompile &&
|
||||
task is AbstractCompile &&
|
||||
FileUtil.isAncestor(javaTask.destinationDir, task.destinationDir, /* strict = */ false)
|
||||
}
|
||||
|
||||
if (!kotlinTask.incremental) {
|
||||
return cannotPerformMultiProjectIC(reason = "incremental compilation is not enabled")
|
||||
}
|
||||
|
||||
// todo: split registry for reading and writing changes
|
||||
val illegalTask = project.tasks.find(::isUnknownTaskOutputtingToJavaDestination)
|
||||
if (illegalTask != null) {
|
||||
return cannotPerformMultiProjectIC(reason = "unknown task outputs to java destination dir ${illegalTask.path} $(${illegalTask.javaClass})")
|
||||
}
|
||||
|
||||
val kotlinCompile = kotlinAfterJavaTask ?: kotlinTask
|
||||
kotlinCompile.artifactDifferenceRegistryProvider = artifactDifferenceRegistryProvider
|
||||
kotlinCompile.artifactFile = artifactFile
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.multiproject
|
||||
|
||||
import org.jetbrains.kotlin.incremental.DirtyData
|
||||
import org.jetbrains.kotlin.incremental.ICReporter
|
||||
import java.io.File
|
||||
|
||||
interface ArtifactDifferenceRegistry {
|
||||
operator fun get(artifact: File): Iterable<ArtifactDifference>?
|
||||
fun add(artifact: File, difference: ArtifactDifference)
|
||||
fun remove(artifact: File)
|
||||
fun flush(memoryCachesOnly: Boolean)
|
||||
fun close()
|
||||
}
|
||||
|
||||
class ArtifactDifference(val buildTS: Long, val dirtyData: DirtyData)
|
||||
|
||||
interface ArtifactDifferenceRegistryProvider {
|
||||
fun <T> withRegistry(
|
||||
report: (String) -> Unit,
|
||||
fn: (ArtifactDifferenceRegistry) -> T
|
||||
): T?
|
||||
|
||||
fun <T> withRegistry(
|
||||
reporter: ICReporter,
|
||||
fn: (ArtifactDifferenceRegistry) -> T
|
||||
): T? {
|
||||
return withRegistry({reporter.report {it}}, fn)
|
||||
}
|
||||
|
||||
fun clean()
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.multiproject
|
||||
|
||||
import java.io.File
|
||||
|
||||
internal class ArtifactDifferenceRegistryProviderAndroidWrapper(
|
||||
private val provider: ArtifactDifferenceRegistryProvider,
|
||||
private val jarToAarMapping: () -> Map<File, File>
|
||||
) : ArtifactDifferenceRegistryProvider {
|
||||
override fun <T> withRegistry(report: (String)->Unit, fn: (ArtifactDifferenceRegistry)->T): T? {
|
||||
return provider.withRegistry(report) { originalRegistry ->
|
||||
val wrapped = ArtifactDifferenceRegistryAndroidWrapper(originalRegistry, jarToAarMapping())
|
||||
fn(wrapped)
|
||||
}
|
||||
}
|
||||
|
||||
override fun clean() {
|
||||
provider.clean()
|
||||
}
|
||||
}
|
||||
|
||||
// When lib is compiled, changes are associated with .aar files.
|
||||
// However when app is compiled, there is just .jar in classpath.
|
||||
private class ArtifactDifferenceRegistryAndroidWrapper(
|
||||
private val registry: ArtifactDifferenceRegistry,
|
||||
private val jarToAarMapping: Map<File, File>
|
||||
) : ArtifactDifferenceRegistry by registry {
|
||||
override fun get(artifact: File): Iterable<ArtifactDifference>? {
|
||||
val mappedFile = jarToAarMapping[artifact] ?: return null
|
||||
return registry[mappedFile]
|
||||
}
|
||||
}
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.multiproject
|
||||
|
||||
import com.intellij.util.io.DataExternalizer
|
||||
import org.jetbrains.kotlin.incremental.DirtyData
|
||||
import org.jetbrains.kotlin.incremental.LookupSymbol
|
||||
import org.jetbrains.kotlin.incremental.storage.BasicStringMap
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import java.io.DataInput
|
||||
import java.io.DataOutput
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
internal class ArtifactDifferenceRegistryImpl(
|
||||
storageFile: File
|
||||
) : ArtifactDifferenceRegistry, BasicStringMap<Collection<ArtifactDifference>>(
|
||||
storageFile,
|
||||
ArtifactDifferenceCollectionExternalizer
|
||||
) {
|
||||
companion object {
|
||||
private val MAX_BUILDS_PER_ARTIFACT = 10
|
||||
}
|
||||
|
||||
override fun get(artifact: File): Iterable<ArtifactDifference>? =
|
||||
storage[artifact.canonicalPath]
|
||||
|
||||
override fun add(artifact: File, difference: ArtifactDifference) {
|
||||
val key = artifact.canonicalPath
|
||||
val oldVal = storage[key] ?: emptyList()
|
||||
val newVal = ArrayList(oldVal)
|
||||
newVal.add(difference)
|
||||
newVal.sortBy { it.buildTS }
|
||||
storage[key] = newVal.takeLast(MAX_BUILDS_PER_ARTIFACT)
|
||||
}
|
||||
|
||||
override fun remove(artifact: File) {
|
||||
storage.remove(artifact.canonicalPath)
|
||||
}
|
||||
|
||||
override fun dumpValue(value: Collection<ArtifactDifference>): String =
|
||||
value.sortedBy { it.buildTS }.joinToString(separator = ",\n\t") { diff ->
|
||||
"{ " +
|
||||
"timestamp: ${diff.buildTS}, " +
|
||||
"lookup symbols: [${diff.dirtyData.dirtyLookupSymbols.dumpLookupSymbols()}], " +
|
||||
"fq names: [${diff.dirtyData.dirtyClassesFqNames.dumpFqNames()}]"
|
||||
"}"
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> Collection<T>.takeLast(n: Int): Collection<T> =
|
||||
drop(Math.max(size - n, 0))
|
||||
|
||||
private fun Collection<LookupSymbol>.dumpLookupSymbols(): String =
|
||||
map { "${it.scope}#${it.name}" }.sorted().joinToString()
|
||||
|
||||
private fun Collection<FqName>.dumpFqNames(): String =
|
||||
map(FqName::asString).sorted().joinToString()
|
||||
|
||||
private object ArtifactDifferenceCollectionExternalizer : DataExternalizer<Collection<ArtifactDifference>> {
|
||||
override fun read(input: DataInput): Collection<ArtifactDifference> =
|
||||
input.readCollectionTo(ArrayList()) {
|
||||
val buildTS = readLong()
|
||||
val dirtyLookupSymbols = readCollectionTo(HashSet()) {
|
||||
val scope = readUTF()
|
||||
val name = readUTF()
|
||||
LookupSymbol(name, scope)
|
||||
}
|
||||
val dirtyFqNames = readCollectionTo(HashSet()) {
|
||||
FqName(readUTF())
|
||||
}
|
||||
ArtifactDifference(buildTS, DirtyData(dirtyLookupSymbols, dirtyFqNames))
|
||||
}
|
||||
|
||||
override fun save(output: DataOutput, value: Collection<ArtifactDifference>) {
|
||||
output.writeCollection(value) { diff ->
|
||||
writeLong(diff.buildTS)
|
||||
writeCollection(diff.dirtyData.dirtyLookupSymbols) {
|
||||
writeUTF(it.scope)
|
||||
writeUTF(it.name)
|
||||
}
|
||||
writeCollection(diff.dirtyData.dirtyClassesFqNames) {
|
||||
writeUTF(it.asString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T> DataInput.readCollectionTo(col: MutableCollection<T>, readT: DataInput.() -> T): Collection<T> {
|
||||
val size = readInt()
|
||||
|
||||
repeat(size) {
|
||||
col.add(readT())
|
||||
}
|
||||
|
||||
return col
|
||||
}
|
||||
|
||||
private inline fun <T> DataOutput.writeCollection(col: Collection<T>, writeT: DataOutput.(T) -> Unit) {
|
||||
writeInt(col.size)
|
||||
col.forEach { writeT(it) }
|
||||
}
|
||||
Reference in New Issue
Block a user