JPS: Refactor cache compatibility checking and build targets loading/dependency analysis.

CacheVersion class refactoring:

Responsibilities of class CacheVersion are splitted into:
	- interface CacheAttributesManager<Attrs>, that should:
	    - load actual cache attribute values from FS
	    - provide expected attribute values (that is required for current build)
	    - checks when the existed cache (with actual attributes) values is suitable for current build (expected atribute values)
	    - write new values to FS for next build
	- CacheAttributesDiff is created by calling CacheAttributesManager.loadDiff extension method. This is just pair of actual and expected cache attributes values, with reference to manager. Result of loadDiff can be saved.

CacheAttributesDiff are designed to be used as facade of attributes operations: CacheAttributesDiff.status are calculated based on actual and expected attribute values. Based on that status system may perform required actions (i.e. rebuild something, clearing caches, etc...).

Methods of CacheAttributesManager other then loadDiff should be used only through CacheAttributesDiff.
Build system should work in this order:
    - get implementation of CacheAttributesManager for particular compiler and cache
    - call loadDiff __once__ and save it result
    - perform actions based on `diff.status`
    - save new cache attribute values by calling `diff.saveExpectedIfNeeded()`

There are 2 implementation of CacheAttributesManager:
    - CacheVersionManager that simple checks cache version number.
    - CompositeLookupsCacheAttributesManager - manager for global lookups cache that may contain lookups for several compilers (jvm, js).

Gradle:

Usages of CacheVersion in gradle are kept as is. For compatibility this methods are added: CacheAttributesManager.saveIfNeeded, CacheAttributesManager.clean. This methods should not be used in new code.

JPS:

All JPS logic that was responsible for cache version checking completely rewritten.

To write proper implementation for version checking, this things also changed:
	- KotlinCompileContext introduced. This context lives between first calling build of kotlin target until build finish. As of now all kotlin targets are loaded on KotlinCompileContext initialization. This is required to collect kotlin target types used in this build (jvm/js). Also all build-wide logic are moved from KotlinBuilder to KotlinCompileContext. Chunk dependency calculation also moved to build start which improves performance for big projects #KT-26113
	- Kotlin bindings to JPS build targets also stored in KotlinCompileContext, and binding is fixed. Previously it is stored in local Context and reacreated for each chunk, now they stored in KotlinCompileContext which is binded by GlobalContextKey with this exception: source roots are calculated for each round, since temporary source roots with groovy stubs are created at build time and visible only in local compile context.
	- KotlinChunk introduced. All chunk-wide logic are moved from KotlinModuleBuildTarget (i.e compiler, language, cache version checking and dependent cache loading)
	- Fix legacy MPP common dependent modules

Cache version checking logic now works as following:
- At first chunk building all targets are loaded and used platforms are collected. Lookups cache manger is created based on this set. Actual cache attributes are loaded from FS. Based on CacheAttributesDiff.status this actions are performed: if cache is invalid all kotlin will be rebuilt. If cache is not required anymore it will be cleaned.
- Before build of each chunk local chunk cache attributes will be checked. If cache is invalid, chunk will be rebuilt. If cache is not required anymore it will be cleaned.

#KT-26113 Fixed
#KT-26072 Fixed
This commit is contained in:
Sergey Rostov
2018-08-16 13:35:32 +03:00
parent 3bdfd811e8
commit 437fc9d749
71 changed files with 2233 additions and 1239 deletions
@@ -64,6 +64,9 @@ abstract class BuildMetaInfoFactory<T : BuildMetaInfo>(private val metaInfoClass
) )
} }
fun serializeToString(args: CommonCompilerArguments): String =
serializeToString(create(args))
fun serializeToString(info: T): String = fun serializeToString(info: T): String =
serializeToPlainText(info, metaInfoClass) serializeToPlainText(info, metaInfoClass)
@@ -1,111 +0,0 @@
/*
* Copyright 2010-2015 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.annotations.TestOnly
import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmBytecodeBinaryVersion
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
import java.io.File
import java.io.IOException
private val NORMAL_VERSION = 10
private val DATA_CONTAINER_VERSION = 3
private val NORMAL_VERSION_FILE_NAME = "format-version.txt"
private val DATA_CONTAINER_VERSION_FILE_NAME = "data-container-format-version.txt"
class CacheVersion(
private val ownVersion: Int,
private val versionFile: File,
private val whenVersionChanged: CacheVersion.Action,
private val whenTurnedOn: CacheVersion.Action,
private val whenTurnedOff: CacheVersion.Action,
private val isEnabled: Boolean
) {
private val actualVersion: Int?
get() = try {
versionFile.readText().toInt()
}
catch (e: NumberFormatException) {
null
}
catch (e: IOException) {
null
}
private val expectedVersion: Int
get() {
val metadata = JvmMetadataVersion.INSTANCE
val bytecode = JvmBytecodeBinaryVersion.INSTANCE
return ownVersion * 1000000 +
bytecode.major * 10000 + bytecode.minor * 100 +
metadata.major * 1000 + metadata.minor
}
fun checkVersion(): Action =
when (versionFile.exists() to isEnabled) {
true to true -> if (actualVersion != expectedVersion) whenVersionChanged else Action.DO_NOTHING
false to true -> whenTurnedOn
true to false -> whenTurnedOff
else -> Action.DO_NOTHING
}
fun saveIfNeeded() {
if (!isEnabled) return
if (!versionFile.parentFile.exists()) {
versionFile.parentFile.mkdirs()
}
versionFile.writeText(expectedVersion.toString())
}
fun clean() {
versionFile.delete()
}
@get:TestOnly
val formatVersionFile: File
get() = versionFile
// Order of entries is important, because actions are sorted in KotlinBuilder::checkVersions
enum class Action {
REBUILD_ALL_KOTLIN,
REBUILD_CHUNK,
CLEAN_NORMAL_CACHES,
CLEAN_DATA_CONTAINER,
DO_NOTHING
}
}
fun normalCacheVersion(dataRoot: File, enabled: Boolean): CacheVersion =
CacheVersion(ownVersion = NORMAL_VERSION,
versionFile = File(dataRoot, NORMAL_VERSION_FILE_NAME),
whenVersionChanged = CacheVersion.Action.REBUILD_CHUNK,
whenTurnedOn = CacheVersion.Action.REBUILD_CHUNK,
whenTurnedOff = CacheVersion.Action.CLEAN_NORMAL_CACHES,
isEnabled = enabled)
fun dataContainerCacheVersion(dataRoot: File, enabled: Boolean): CacheVersion =
CacheVersion(ownVersion = DATA_CONTAINER_VERSION,
versionFile = File(dataRoot, DATA_CONTAINER_VERSION_FILE_NAME),
whenVersionChanged = CacheVersion.Action.REBUILD_ALL_KOTLIN,
whenTurnedOn = CacheVersion.Action.REBUILD_ALL_KOTLIN,
whenTurnedOff = CacheVersion.Action.CLEAN_DATA_CONTAINER,
isEnabled = enabled)
@@ -25,6 +25,8 @@ import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.build.GeneratedJvmClass import org.jetbrains.kotlin.build.GeneratedJvmClass
import org.jetbrains.kotlin.config.IncrementalCompilation import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.incremental.storage.* import org.jetbrains.kotlin.incremental.storage.*
import org.jetbrains.kotlin.incremental.storage.version.clean
import org.jetbrains.kotlin.incremental.storage.version.localCacheVersionManager
import org.jetbrains.kotlin.inline.inlineFunctionsJvmNames import org.jetbrains.kotlin.inline.inlineFunctionsJvmNames
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
@@ -43,8 +45,8 @@ import java.util.*
val KOTLIN_CACHE_DIRECTORY_NAME = "kotlin" val KOTLIN_CACHE_DIRECTORY_NAME = "kotlin"
open class IncrementalJvmCache( open class IncrementalJvmCache(
private val targetDataRoot: File, private val targetDataRoot: File,
targetOutputDir: File? targetOutputDir: File?
) : AbstractIncrementalCache<JvmClassName>(File(targetDataRoot, KOTLIN_CACHE_DIRECTORY_NAME)), IncrementalCache { ) : AbstractIncrementalCache<JvmClassName>(File(targetDataRoot, KOTLIN_CACHE_DIRECTORY_NAME)), IncrementalCache {
companion object { companion object {
private val PROTO_MAP = "proto" private val PROTO_MAP = "proto"
@@ -81,16 +83,16 @@ open class IncrementalJvmCache(
// used in gradle // used in gradle
@Suppress("unused") @Suppress("unused")
fun classesBySources(sources: Iterable<File>): Iterable<JvmClassName> = fun classesBySources(sources: Iterable<File>): Iterable<JvmClassName> =
sources.flatMap { sourceToClassesMap[it] } sources.flatMap { sourceToClassesMap[it] }
fun sourceInCache(file: File): Boolean = fun sourceInCache(file: File): Boolean =
sourceToClassesMap.contains(file) sourceToClassesMap.contains(file)
fun sourcesByInternalName(internalName: String): Collection<File> = fun sourcesByInternalName(internalName: String): Collection<File> =
internalNameToSource[internalName] internalNameToSource[internalName]
fun isMultifileFacade(className: JvmClassName): Boolean = fun isMultifileFacade(className: JvmClassName): Boolean =
className in multifileFacadeToParts className in multifileFacadeToParts
override fun getClassFilePath(internalClassName: String): String { override fun getClassFilePath(internalClassName: String): String {
return toSystemIndependentName(File(outputDir, "$internalClassName.class").canonicalPath) return toSystemIndependentName(File(outputDir, "$internalClassName.class").canonicalPath)
@@ -129,7 +131,7 @@ open class IncrementalJvmCache(
} }
KotlinClassHeader.Kind.MULTIFILE_CLASS -> { KotlinClassHeader.Kind.MULTIFILE_CLASS -> {
val partNames = kotlinClass.classHeader.data?.toList() val partNames = kotlinClass.classHeader.data?.toList()
?: throw AssertionError("Multifile class has no parts: ${kotlinClass.className}") ?: throw AssertionError("Multifile class has no parts: ${kotlinClass.className}")
multifileFacadeToParts[className] = partNames multifileFacadeToParts[className] = partNames
// When a class is replaced with a facade with the same name, // When a class is replaced with a facade with the same name,
// the class' proto wouldn't ever be deleted, // the class' proto wouldn't ever be deleted,
@@ -177,15 +179,15 @@ open class IncrementalJvmCache(
} }
fun getObsoleteJavaClasses(): Collection<ClassId> = fun getObsoleteJavaClasses(): Collection<ClassId> =
dirtyOutputClassesMap.getDirtyOutputClasses() dirtyOutputClassesMap.getDirtyOutputClasses()
.mapNotNull { .mapNotNull {
javaSourcesProtoMap[it]?.classId javaSourcesProtoMap[it]?.classId
} }
fun isJavaClassToTrack(classId: ClassId): Boolean { fun isJavaClassToTrack(classId: ClassId): Boolean {
val jvmClassName = JvmClassName.byClassId(classId) val jvmClassName = JvmClassName.byClassId(classId)
return dirtyOutputClassesMap.isDirty(jvmClassName) || return dirtyOutputClassesMap.isDirty(jvmClassName) ||
jvmClassName !in javaSourcesProtoMap jvmClassName !in javaSourcesProtoMap
} }
fun isJavaClassAlreadyInCache(classId: ClassId): Boolean { fun isJavaClassAlreadyInCache(classId: ClassId): Boolean {
@@ -210,8 +212,7 @@ open class IncrementalJvmCache(
if (notRemovedParts.isEmpty()) { if (notRemovedParts.isEmpty()) {
multifileFacadeToParts.remove(facade) multifileFacadeToParts.remove(facade)
} } else {
else {
multifileFacadeToParts[facade] = notRemovedParts multifileFacadeToParts[facade] = notRemovedParts
} }
} }
@@ -265,7 +266,7 @@ open class IncrementalJvmCache(
override fun clean() { override fun clean() {
super.clean() super.clean()
normalCacheVersion(targetDataRoot, IncrementalCompilation.isEnabledForJvm()).clean() localCacheVersionManager(targetDataRoot, IncrementalCompilation.isEnabledForJvm()).clean()
} }
private inner class ProtoMap(storageFile: File) : BasicStringMap<ProtoMapValue>(storageFile, ProtoMapValueExternalizer) { private inner class ProtoMap(storageFile: File) : BasicStringMap<ProtoMapValue>(storageFile, ProtoMapValueExternalizer) {
@@ -290,9 +291,11 @@ open class IncrementalJvmCache(
val key = kotlinClass.className.internalName val key = kotlinClass.className.internalName
val oldData = storage[key] val oldData = storage[key]
val newData = ProtoMapValue(header.kind != KotlinClassHeader.Kind.CLASS, val newData = ProtoMapValue(
BitEncoding.decodeBytes(header.data!!), header.kind != KotlinClassHeader.Kind.CLASS,
header.strings!!) BitEncoding.decodeBytes(header.data!!),
header.strings!!
)
storage[key] = newData storage[key] = newData
val packageFqName = kotlinClass.className.packageFqName val packageFqName = kotlinClass.className.packageFqName
@@ -300,10 +303,10 @@ open class IncrementalJvmCache(
} }
operator fun contains(className: JvmClassName): Boolean = operator fun contains(className: JvmClassName): Boolean =
className.internalName in storage className.internalName in storage
operator fun get(className: JvmClassName): ProtoMapValue? = operator fun get(className: JvmClassName): ProtoMapValue? =
storage[className.internalName] storage[className.internalName]
fun remove(className: JvmClassName, changesCollector: ChangesCollector) { fun remove(className: JvmClassName, changesCollector: ChangesCollector) {
val key = className.internalName val key = className.internalName
@@ -319,15 +322,16 @@ open class IncrementalJvmCache(
} }
} }
private inner class JavaSourcesProtoMap(storageFile: File) : BasicStringMap<SerializedJavaClass>(storageFile, JavaClassProtoMapValueExternalizer) { private inner class JavaSourcesProtoMap(storageFile: File) :
BasicStringMap<SerializedJavaClass>(storageFile, JavaClassProtoMapValueExternalizer) {
fun process(jvmClassName: JvmClassName, newData: SerializedJavaClass, changesCollector: ChangesCollector) { fun process(jvmClassName: JvmClassName, newData: SerializedJavaClass, changesCollector: ChangesCollector) {
val key = jvmClassName.internalName val key = jvmClassName.internalName
val oldData = storage[key] val oldData = storage[key]
storage[key] = newData storage[key] = newData
changesCollector.collectProtoChanges( changesCollector.collectProtoChanges(
oldData?.toProtoData(), newData.toProtoData(), oldData?.toProtoData(), newData.toProtoData(),
collectAllMembersForNewClass = true collectAllMembersForNewClass = true
) )
} }
@@ -340,13 +344,13 @@ open class IncrementalJvmCache(
} }
operator fun get(className: JvmClassName): SerializedJavaClass? = operator fun get(className: JvmClassName): SerializedJavaClass? =
storage[className.internalName] storage[className.internalName]
operator fun contains(className: JvmClassName): Boolean = operator fun contains(className: JvmClassName): Boolean =
className.internalName in storage className.internalName in storage
override fun dumpValue(value: SerializedJavaClass): String = override fun dumpValue(value: SerializedJavaClass): String =
java.lang.Long.toHexString(value.proto.toByteArray().md5()) java.lang.Long.toHexString(value.proto.toByteArray().md5())
} }
// todo: reuse code with InlineFunctionsMap? // todo: reuse code with InlineFunctionsMap?
@@ -368,7 +372,7 @@ open class IncrementalJvmCache(
} }
operator fun contains(className: JvmClassName): Boolean = operator fun contains(className: JvmClassName): Boolean =
className.internalName in storage className.internalName in storage
fun process(kotlinClass: LocalFileKotlinClass, changesCollector: ChangesCollector) { fun process(kotlinClass: LocalFileKotlinClass, changesCollector: ChangesCollector) {
val key = kotlinClass.className.internalName val key = kotlinClass.className.internalName
@@ -377,8 +381,7 @@ open class IncrementalJvmCache(
val newMap = getConstantsMap(kotlinClass.fileContents) val newMap = getConstantsMap(kotlinClass.fileContents)
if (newMap.isNotEmpty()) { if (newMap.isNotEmpty()) {
storage[key] = newMap storage[key] = newMap
} } else {
else {
storage.remove(key) storage.remove(key)
} }
@@ -392,7 +395,7 @@ open class IncrementalJvmCache(
} }
override fun dumpValue(value: Map<String, Any>): String = override fun dumpValue(value: Map<String, Any>): String =
value.dumpMap(Any::toString) value.dumpMap(Any::toString)
} }
private inner class PackagePartMap(storageFile: File) : BasicStringMap<Boolean>(storageFile, BooleanDataDescriptor.INSTANCE) { private inner class PackagePartMap(storageFile: File) : BasicStringMap<Boolean>(storageFile, BooleanDataDescriptor.INSTANCE) {
@@ -405,21 +408,22 @@ open class IncrementalJvmCache(
} }
fun isPackagePart(className: JvmClassName): Boolean = fun isPackagePart(className: JvmClassName): Boolean =
className.internalName in storage className.internalName in storage
override fun dumpValue(value: Boolean) = "" override fun dumpValue(value: Boolean) = ""
} }
private inner class MultifileClassFacadeMap(storageFile: File) : BasicStringMap<Collection<String>>(storageFile, StringCollectionExternalizer) { private inner class MultifileClassFacadeMap(storageFile: File) :
BasicStringMap<Collection<String>>(storageFile, StringCollectionExternalizer) {
operator fun set(className: JvmClassName, partNames: Collection<String>) { operator fun set(className: JvmClassName, partNames: Collection<String>) {
storage[className.internalName] = partNames storage[className.internalName] = partNames
} }
operator fun get(className: JvmClassName): Collection<String>? = operator fun get(className: JvmClassName): Collection<String>? =
storage[className.internalName] storage[className.internalName]
operator fun contains(className: JvmClassName): Boolean = operator fun contains(className: JvmClassName): Boolean =
className.internalName in storage className.internalName in storage
fun remove(className: JvmClassName) { fun remove(className: JvmClassName) {
storage.remove(className.internalName) storage.remove(className.internalName)
@@ -428,13 +432,14 @@ open class IncrementalJvmCache(
override fun dumpValue(value: Collection<String>): String = value.dumpCollection() override fun dumpValue(value: Collection<String>): String = value.dumpCollection()
} }
private inner class MultifileClassPartMap(storageFile: File) : BasicStringMap<String>(storageFile, EnumeratorStringDescriptor.INSTANCE) { private inner class MultifileClassPartMap(storageFile: File) :
BasicStringMap<String>(storageFile, EnumeratorStringDescriptor.INSTANCE) {
fun set(partName: String, facadeName: String) { fun set(partName: String, facadeName: String) {
storage[partName] = facadeName storage[partName] = facadeName
} }
fun get(partName: JvmClassName): String? = fun get(partName: JvmClassName): String? =
storage[partName.internalName] storage[partName.internalName]
fun remove(className: JvmClassName) { fun remove(className: JvmClassName) {
storage.remove(className.internalName) storage.remove(className.internalName)
@@ -443,20 +448,21 @@ open class IncrementalJvmCache(
override fun dumpValue(value: String): String = value override fun dumpValue(value: String): String = value
} }
inner class InternalNameToSourcesMap(storageFile: File) : BasicStringMap<Collection<String>>(storageFile, EnumeratorStringDescriptor(), PathCollectionExternalizer) { inner class InternalNameToSourcesMap(storageFile: File) :
BasicStringMap<Collection<String>>(storageFile, EnumeratorStringDescriptor(), PathCollectionExternalizer) {
operator fun set(internalName: String, sourceFiles: Iterable<File>) { operator fun set(internalName: String, sourceFiles: Iterable<File>) {
storage[internalName] = sourceFiles.map { it.canonicalPath } storage[internalName] = sourceFiles.map { it.canonicalPath }
} }
operator fun get(internalName: String): Collection<File> = operator fun get(internalName: String): Collection<File> =
(storage[internalName] ?: emptyList()).map(::File) (storage[internalName] ?: emptyList()).map(::File)
fun remove(internalName: String) { fun remove(internalName: String) {
storage.remove(internalName) storage.remove(internalName)
} }
override fun dumpValue(value: Collection<String>): String = override fun dumpValue(value: Collection<String>): String =
value.dumpCollection() value.dumpCollection()
} }
private fun addToClassStorage(kotlinClass: LocalFileKotlinClass, srcFile: File) { private fun addToClassStorage(kotlinClass: LocalFileKotlinClass, srcFile: File) {
@@ -464,7 +470,8 @@ open class IncrementalJvmCache(
addToClassStorage(proto, nameResolver, srcFile) addToClassStorage(proto, nameResolver, srcFile)
} }
private inner class InlineFunctionsMap(storageFile: File) : BasicStringMap<Map<String, Long>>(storageFile, StringToLongMapExternalizer) { private inner class InlineFunctionsMap(storageFile: File) :
BasicStringMap<Map<String, Long>>(storageFile, StringToLongMapExternalizer) {
private fun getInlineFunctionsMap(header: KotlinClassHeader, bytes: ByteArray): Map<String, Long> { private fun getInlineFunctionsMap(header: KotlinClassHeader, bytes: ByteArray): Map<String, Long> {
val inlineFunctions = inlineFunctionsJvmNames(header) val inlineFunctions = inlineFunctionsJvmNames(header)
if (inlineFunctions.isEmpty()) return emptyMap() if (inlineFunctions.isEmpty()) return emptyMap()
@@ -472,7 +479,13 @@ open class IncrementalJvmCache(
val result = HashMap<String, Long>() val result = HashMap<String, Long>()
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) { ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) {
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? { override fun visitMethod(
access: Int,
name: String,
desc: String,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor? {
val dummyClassWriter = ClassWriter(Opcodes.ASM5) val dummyClassWriter = ClassWriter(Opcodes.ASM5)
return object : MethodVisitor(Opcodes.ASM5, dummyClassWriter.visitMethod(0, name, desc, null, exceptions)) { return object : MethodVisitor(Opcodes.ASM5, dummyClassWriter.visitMethod(0, name, desc, null, exceptions)) {
@@ -499,30 +512,35 @@ open class IncrementalJvmCache(
val newMap = getInlineFunctionsMap(kotlinClass.classHeader, kotlinClass.fileContents) val newMap = getInlineFunctionsMap(kotlinClass.classHeader, kotlinClass.fileContents)
if (newMap.isNotEmpty()) { if (newMap.isNotEmpty()) {
storage[key] = newMap storage[key] = newMap
} } else {
else {
storage.remove(key) storage.remove(key)
} }
for (fn in oldMap.keys + newMap.keys) { for (fn in oldMap.keys + newMap.keys) {
changesCollector.collectMemberIfValueWasChanged(kotlinClass.scopeFqName(), functionNameBySignature(fn), oldMap[fn], newMap[fn]) changesCollector.collectMemberIfValueWasChanged(
kotlinClass.scopeFqName(),
functionNameBySignature(fn),
oldMap[fn],
newMap[fn]
)
} }
} }
// TODO get name in better way instead of using substringBefore // TODO get name in better way instead of using substringBefore
private fun functionNameBySignature(signature: String): String = private fun functionNameBySignature(signature: String): String =
signature.substringBefore("(") signature.substringBefore("(")
fun remove(className: JvmClassName) { fun remove(className: JvmClassName) {
storage.remove(className.internalName) storage.remove(className.internalName)
} }
override fun dumpValue(value: Map<String, Long>): String = override fun dumpValue(value: Map<String, Long>): String =
value.dumpMap { java.lang.Long.toHexString(it) } value.dumpMap { java.lang.Long.toHexString(it) }
} }
} }
private object PathCollectionExternalizer : CollectionExternalizer<String>(PathStringDescriptor, { THashSet(FileUtil.PATH_HASHING_STRATEGY) }) private object PathCollectionExternalizer :
CollectionExternalizer<String>(PathStringDescriptor, { THashSet(FileUtil.PATH_HASHING_STRATEGY) })
sealed class ChangeInfo(val fqName: FqName) { sealed class ChangeInfo(val fqName: FqName) {
open class MembersChanged(fqName: FqName, val names: Collection<String>) : ChangeInfo(fqName) { open class MembersChanged(fqName: FqName, val names: Collection<String>) : ChangeInfo(fqName) {
@@ -542,10 +560,10 @@ sealed class ChangeInfo(val fqName: FqName) {
} }
private fun LocalFileKotlinClass.scopeFqName() = private fun LocalFileKotlinClass.scopeFqName() =
when (classHeader.kind) { when (classHeader.kind) {
KotlinClassHeader.Kind.CLASS -> className.fqNameForClassNameWithoutDollars KotlinClassHeader.Kind.CLASS -> className.fqNameForClassNameWithoutDollars
else -> className.packageFqName else -> className.packageFqName
} }
fun ByteArray.md5(): Long { fun ByteArray.md5(): Long {
val d = MessageDigest.getInstance("MD5").digest(this)!! val d = MessageDigest.getInstance("MD5").digest(this)!!
@@ -557,23 +575,24 @@ fun ByteArray.md5(): Long {
or ((d[5].toLong() and 0xFFL) shl 40) or ((d[5].toLong() and 0xFFL) shl 40)
or ((d[6].toLong() and 0xFFL) shl 48) or ((d[6].toLong() and 0xFFL) shl 48)
or ((d[7].toLong() and 0xFFL) shl 56) or ((d[7].toLong() and 0xFFL) shl 56)
) )
} }
@TestOnly @TestOnly
fun <K : Comparable<K>, V> Map<K, V>.dumpMap(dumpValue: (V)->String): String = fun <K : Comparable<K>, V> Map<K, V>.dumpMap(dumpValue: (V) -> String): String =
buildString { buildString {
append("{") append("{")
for (key in keys.sorted()) { for (key in keys.sorted()) {
if (length != 1) { if (length != 1) {
append(", ") append(", ")
}
val value = get(key)?.let(dumpValue) ?: "null"
append("$key -> $value")
} }
append("}")
}
@TestOnly fun <T : Comparable<T>> Collection<T>.dumpCollection(): String = val value = get(key)?.let(dumpValue) ?: "null"
"[${sorted().joinToString(", ", transform = Any::toString)}]" append("$key -> $value")
}
append("}")
}
@TestOnly
fun <T : Comparable<T>> Collection<T>.dumpCollection(): String =
"[${sorted().joinToString(", ", transform = Any::toString)}]"
@@ -0,0 +1,37 @@
/*
* 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.storage.version
/**
* Diff between actual and expected cache attributes.
* [status] are calculated based on this diff (see [CacheStatus]).
* Based on that [status] system may perform required actions (i.e. rebuild something, clearing caches, etc...).
*
* [CacheAttributesDiff] can be used to cache current attribute values and as facade for version operations.
*/
data class CacheAttributesDiff<Attrs: Any>(
val manager: CacheAttributesManager<Attrs>,
val actual: Attrs?,
val expected: Attrs?
) {
val status: CacheStatus
get() =
if (expected != null) {
if (actual != null && manager.isCompatible(actual, expected)) CacheStatus.VALID
else CacheStatus.INVALID
} else {
if (actual != null) CacheStatus.SHOULD_BE_CLEARED
else CacheStatus.CLEARED
}
fun saveExpectedIfNeeded() {
if (expected != actual) manager.writeActualVersion(expected)
}
override fun toString(): String {
return "$status: actual=$actual -> expected=$expected"
}
}
@@ -0,0 +1,79 @@
/*
* 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.storage.version
/**
* Manages cache attributes values.
*
* Attribute values can be loaded by calling [loadActual].
* Based on loaded actual and fixed [expected] values [CacheAttributesDiff] can be constructed which can calculate [CacheStatus].
* Build system may perform required actions based on that (i.e. rebuild something, clearing caches, etc...).
*
* [CacheAttributesDiff] can be used to cache current attribute values and then can be used as facade for cache version operations.
*/
interface CacheAttributesManager<Attrs : Any> {
/**
* Cache attribute values expected by the current version of build system and compiler.
* `null` means that cache is not required (incremental compilation is disabled).
*/
val expected: Attrs?
/**
* Load actual cache attribute values.
* `null` means that cache is not yet created.
*
* This is internal operation that should be implemented by particular implementation of CacheAttributesManager.
* Consider using `loadDiff().actual` for getting actual values.
*/
fun loadActual(): Attrs?
/**
* Write [values] as cache attributes for next build execution.
*
* This is internal operation that should be implemented by particular implementation of CacheAttributesManager.
* Consider using `loadDiff().saveExpectedIfNeeded()` for saving attributes values for next build.
*/
fun writeActualVersion(values: Attrs?)
/**
* Check if cache with [actual] attributes values can be used when [expected] attributes are required.
*/
fun isCompatible(actual: Attrs, expected: Attrs): Boolean = actual == expected
}
fun <Attrs : Any> CacheAttributesManager<Attrs>.loadDiff(
actual: Attrs? = this.loadActual(),
expected: Attrs? = this.expected
) = CacheAttributesDiff(this, actual, expected)
fun <Attrs : Any> CacheAttributesManager<Attrs>.loadAndCheckStatus() =
loadDiff().status
/**
* This method is kept only for compatibility.
* Save [expected] cache attributes values if it is enabled and not equals to [actual].
*/
@Deprecated(
message = "Consider using `this.loadDiff().saveExpectedIfNeeded()` and cache `loadDiff()` result.",
replaceWith = ReplaceWith("loadDiff().saveExpectedIfNeeded()")
)
fun <Attrs : Any> CacheAttributesManager<Attrs>.saveIfNeeded(
actual: Attrs? = this.loadActual(),
expected: Attrs = this.expected
?: error("To save disabled cache status [delete] should be called (this behavior is kept for compatibility)")
) = loadDiff(actual, expected).saveExpectedIfNeeded()
/**
* This method is kept only for compatibility.
* Delete actual cache attributes values if it existed.
*/
@Deprecated(
message = "Consider using `this.loadDiff().saveExpectedIfNeeded()` and cache `loadDiff()` result.",
replaceWith = ReplaceWith("writeActualVersion(null)")
)
fun CacheAttributesManager<*>.clean() {
writeActualVersion(null)
}
@@ -0,0 +1,31 @@
/*
* 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.storage.version
/**
* Status that is used by system to perform required actions (i.e. rebuild something, clearing caches, etc...).
*/
enum class CacheStatus {
/**
* Cache is valid and ready to use.
*/
VALID,
/**
* Cache is not exists or have outdated versions and/or other attributes.
*/
INVALID,
/**
* Cache is exists, but not required anymore.
*/
SHOULD_BE_CLEARED,
/**
* Cache is not exists and not required.
*/
CLEARED
}
@@ -0,0 +1,60 @@
/*
* 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.storage.version
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmBytecodeBinaryVersion
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
import java.io.File
import java.io.IOException
/**
* Manages files with actual version [loadActual] and provides expected version [expected].
* Based on that actual and expected versions [CacheStatus] can be calculated.
* This can be done by constructing [CacheAttributesDiff] and calling [CacheAttributesDiff.status].
* Based on that status system may perform required actions (i.e. rebuild something, clearing caches, etc...).
*/
class CacheVersionManager(
private val versionFile: File,
expectedOwnVersion: Int?
) : CacheAttributesManager<CacheVersion> {
override val expected: CacheVersion? =
if (expectedOwnVersion == null) null
else {
val metadata = JvmMetadataVersion.INSTANCE
val bytecode = JvmBytecodeBinaryVersion.INSTANCE
CacheVersion(
expectedOwnVersion * 1000000 +
bytecode.major * 10000 + bytecode.minor * 100 +
metadata.major * 1000 + metadata.minor
)
}
override fun loadActual(): CacheVersion? =
if (!versionFile.exists()) null
else try {
CacheVersion(versionFile.readText().toInt())
} catch (e: NumberFormatException) {
null
} catch (e: IOException) {
null
}
override fun writeActualVersion(values: CacheVersion?) {
if (values == null) versionFile.delete()
else {
versionFile.parentFile.mkdirs()
versionFile.writeText(values.version.toString())
}
}
@get:TestOnly
val versionFileForTesting: File
get() = versionFile
}
data class CacheVersion(val version: Int)
@@ -0,0 +1,17 @@
/*
* 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.storage.version
import java.io.File
private val NORMAL_VERSION = 9
private val NORMAL_VERSION_FILE_NAME = "format-version.txt"
fun localCacheVersionManager(dataRoot: File, isCachesEnabled: Boolean) =
CacheVersionManager(
File(dataRoot, NORMAL_VERSION_FILE_NAME),
if (isCachesEnabled) NORMAL_VERSION else null
)
@@ -0,0 +1,20 @@
/*
* 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.storage.version
import java.io.File
private val DATA_CONTAINER_VERSION_FILE_NAME = "data-container-format-version.txt"
private val DATA_CONTAINER_VERSION = 3
fun lookupsCacheVersionManager(dataRoot: File, isEnabled: Boolean) =
CacheVersionManager(
File(dataRoot, DATA_CONTAINER_VERSION_FILE_NAME),
if (isEnabled) DATA_CONTAINER_VERSION else null
)
fun readLookupsCacheStatus(dataRoot: File, isEnabled: Boolean): CacheStatus =
lookupsCacheVersionManager(dataRoot, isEnabled).loadAndCheckStatus()
@@ -83,7 +83,7 @@ interface CompilerSelector {
} }
interface EventManager { interface EventManager {
fun onCompilationFinished(f : () -> Unit) fun onCompilationFinished(f: () -> Unit)
} }
private class EventManagerImpl : EventManager { private class EventManagerImpl : EventManager {
@@ -99,14 +99,14 @@ private class EventManagerImpl : EventManager {
} }
class CompileServiceImpl( class CompileServiceImpl(
val registry: Registry, val registry: Registry,
val compiler: CompilerSelector, val compiler: CompilerSelector,
val compilerId: CompilerId, val compilerId: CompilerId,
val daemonOptions: DaemonOptions, val daemonOptions: DaemonOptions,
val daemonJVMOptions: DaemonJVMOptions, val daemonJVMOptions: DaemonJVMOptions,
val port: Int, val port: Int,
val timer: Timer, val timer: Timer,
val onShutdown: () -> Unit val onShutdown: () -> Unit
) : CompileService { ) : CompileService {
private val log by lazy { Logger.getLogger("compiler") } private val log by lazy { Logger.getLogger("compiler") }
@@ -116,8 +116,14 @@ class CompileServiceImpl(
} }
// wrapped in a class to encapsulate alive check logic // wrapped in a class to encapsulate alive check logic
private class ClientOrSessionProxy<out T: Any>(val aliveFlagPath: String?, val data: T? = null, private var disposable: Disposable? = null) { private class ClientOrSessionProxy<out T : Any>(
val isAlive: Boolean get() = aliveFlagPath?.let { File(it).exists() } ?: true // assuming that if no file was given, the client is alive val aliveFlagPath: String?,
val data: T? = null,
private var disposable: Disposable? = null
) {
val isAlive: Boolean
get() = aliveFlagPath?.let { File(it).exists() } ?: true // assuming that if no file was given, the client is alive
fun dispose() { fun dispose() {
disposable?.let { disposable?.let {
Disposer.dispose(it) Disposer.dispose(it)
@@ -132,7 +138,8 @@ class CompileServiceImpl(
enum class Aliveness { enum class Aliveness {
// !!! ordering of values is used in state comparison // !!! ordering of values is used in state comparison
Dying, LastSession, Alive Dying,
LastSession, Alive
} }
private class SessionsContainer { private class SessionsContainer {
@@ -143,7 +150,7 @@ class CompileServiceImpl(
val lastSessionId get() = sessionsIdCounter.get() val lastSessionId get() = sessionsIdCounter.get()
fun<T: Any> leaseSession(session: ClientOrSessionProxy<T>): Int = lock.write { fun <T : Any> leaseSession(session: ClientOrSessionProxy<T>): Int = lock.write {
val newId = getValidId(sessionsIdCounter) { val newId = getValidId(sessionsIdCounter) {
it != CompileService.NO_SESSION && !sessions.containsKey(it) it != CompileService.NO_SESSION && !sessions.containsKey(it)
} }
@@ -203,18 +210,22 @@ class CompileServiceImpl(
clientProxies.mapNotNull { it.aliveFlagPath } clientProxies.mapNotNull { it.aliveFlagPath }
} }
fun cleanDeadClients(): Boolean = clientProxies.cleanMatching(clientsLock, { !it.isAlive }, { if (clientProxies.remove(it)) it.dispose() }) fun cleanDeadClients(): Boolean =
clientProxies.cleanMatching(clientsLock, { !it.isAlive }, { if (clientProxies.remove(it)) it.dispose() })
} }
private fun Int.toAlivenessName(): String = private fun Int.toAlivenessName(): String =
try { try {
Aliveness.values()[this].name Aliveness.values()[this].name
} } catch (_: Throwable) {
catch (_: Throwable) { "invalid($this)"
"invalid($this)" }
}
private inline fun<T> Iterable<T>.cleanMatching(lock: ReentrantReadWriteLock, crossinline pred: (T) -> Boolean, crossinline clean: (T) -> Unit): Boolean { private inline fun <T> Iterable<T>.cleanMatching(
lock: ReentrantReadWriteLock,
crossinline pred: (T) -> Boolean,
crossinline clean: (T) -> Unit
): Boolean {
var anyDead = false var anyDead = false
lock.read { lock.read {
val toRemove = filter(pred) val toRemove = filter(pred)
@@ -228,7 +239,8 @@ class CompileServiceImpl(
return anyDead return anyDead
} }
@Volatile private var _lastUsedSeconds = nowSeconds() @Volatile
private var _lastUsedSeconds = nowSeconds()
val lastUsedSeconds: Long get() = if (rwlock.isWriteLocked || rwlock.readLockCount - rwlock.readHoldCount > 0) nowSeconds() else _lastUsedSeconds val lastUsedSeconds: Long get() = if (rwlock.isWriteLocked || rwlock.readLockCount - rwlock.readHoldCount > 0) nowSeconds() else _lastUsedSeconds
private val rwlock = ReentrantReadWriteLock() private val rwlock = ReentrantReadWriteLock()
@@ -238,10 +250,14 @@ class CompileServiceImpl(
init { init {
val runFileDir = File(daemonOptions.runFilesPathOrDefault) val runFileDir = File(daemonOptions.runFilesPathOrDefault)
runFileDir.mkdirs() runFileDir.mkdirs()
runFile = File(runFileDir, runFile = File(
makeRunFilenameString(timestamp = "%tFT%<tH-%<tM-%<tS.%<tLZ".format(Calendar.getInstance(TimeZone.getTimeZone("Z"))), runFileDir,
digest = compilerId.compilerClasspath.map { File(it).absolutePath }.distinctStringsDigest().toHexString(), makeRunFilenameString(
port = port.toString())) timestamp = "%tFT%<tH-%<tM-%<tS.%<tLZ".format(Calendar.getInstance(TimeZone.getTimeZone("Z"))),
digest = compilerId.compilerClasspath.map { File(it).absolutePath }.distinctStringsDigest().toHexString(),
port = port.toString()
)
)
try { try {
if (!runFile.createNewFile()) throw Exception("createNewFile returned false") if (!runFile.createNewFile()) throw Exception("createNewFile returned false")
} catch (e: Throwable) { } catch (e: Throwable) {
@@ -279,9 +295,9 @@ class CompileServiceImpl(
// TODO: consider tying a session to a client and use this info to cleanup // TODO: consider tying a session to a client and use this info to cleanup
override fun leaseCompileSession(aliveFlagPath: String?): CompileService.CallResult<Int> = ifAlive(minAliveness = Aliveness.Alive) { override fun leaseCompileSession(aliveFlagPath: String?): CompileService.CallResult<Int> = ifAlive(minAliveness = Aliveness.Alive) {
CompileService.CallResult.Good( CompileService.CallResult.Good(
state.sessions.leaseSession(ClientOrSessionProxy<Any>(aliveFlagPath)).apply { state.sessions.leaseSession(ClientOrSessionProxy<Any>(aliveFlagPath)).apply {
log.info("leased a new session $this, session alive file: $aliveFlagPath") log.info("leased a new session $this, session alive file: $aliveFlagPath")
}) })
} }
@@ -301,12 +317,12 @@ class CompileServiceImpl(
} }
override fun checkCompilerId(expectedCompilerId: CompilerId): Boolean = override fun checkCompilerId(expectedCompilerId: CompilerId): Boolean =
(compilerId.compilerVersion.isEmpty() || compilerId.compilerVersion == expectedCompilerId.compilerVersion) && (compilerId.compilerVersion.isEmpty() || compilerId.compilerVersion == expectedCompilerId.compilerVersion) &&
(compilerId.compilerClasspath.all { expectedCompilerId.compilerClasspath.contains(it) }) && (compilerId.compilerClasspath.all { expectedCompilerId.compilerClasspath.contains(it) }) &&
!classpathWatcher.isChanged !classpathWatcher.isChanged
override fun getUsedMemory(): CompileService.CallResult<Long> = override fun getUsedMemory(): CompileService.CallResult<Long> =
ifAlive { CompileService.CallResult.Good(usedMemory(withGC = true)) } ifAlive { CompileService.CallResult.Good(usedMemory(withGC = true)) }
override fun shutdown(): CompileService.CallResult<Nothing> = ifAliveExclusive(minAliveness = Aliveness.LastSession) { override fun shutdown(): CompileService.CallResult<Nothing> = ifAliveExclusive(minAliveness = Aliveness.LastSession) {
shutdownWithDelay() shutdownWithDelay()
@@ -324,37 +340,55 @@ class CompileServiceImpl(
CompileService.CallResult.Good(res) CompileService.CallResult.Good(res)
} }
override fun remoteCompile(sessionId: Int, override fun remoteCompile(
targetPlatform: CompileService.TargetPlatform, sessionId: Int,
args: Array<out String>, targetPlatform: CompileService.TargetPlatform,
servicesFacade: CompilerCallbackServicesFacade, args: Array<out String>,
compilerOutputStream: RemoteOutputStream, servicesFacade: CompilerCallbackServicesFacade,
outputFormat: CompileService.OutputFormat, compilerOutputStream: RemoteOutputStream,
serviceOutputStream: RemoteOutputStream, outputFormat: CompileService.OutputFormat,
operationsTracer: RemoteOperationsTracer? serviceOutputStream: RemoteOutputStream,
operationsTracer: RemoteOperationsTracer?
): CompileService.CallResult<Int> = ): CompileService.CallResult<Int> =
doCompile(sessionId, args, compilerOutputStream, serviceOutputStream, operationsTracer) { printStream, eventManager, profiler -> doCompile(sessionId, args, compilerOutputStream, serviceOutputStream, operationsTracer) { printStream, eventManager, profiler ->
when (outputFormat) { when (outputFormat) {
CompileService.OutputFormat.PLAIN -> compiler[targetPlatform].exec(printStream, *args) CompileService.OutputFormat.PLAIN -> compiler[targetPlatform].exec(printStream, *args)
CompileService.OutputFormat.XML -> compiler[targetPlatform].execAndOutputXml(printStream, createCompileServices(servicesFacade, eventManager, profiler), *args) CompileService.OutputFormat.XML -> compiler[targetPlatform].execAndOutputXml(
} printStream,
createCompileServices(
servicesFacade,
eventManager,
profiler
),
*args
)
} }
}
override fun remoteIncrementalCompile(sessionId: Int, override fun remoteIncrementalCompile(
targetPlatform: CompileService.TargetPlatform, sessionId: Int,
args: Array<out String>, targetPlatform: CompileService.TargetPlatform,
servicesFacade: CompilerCallbackServicesFacade, args: Array<out String>,
compilerOutputStream: RemoteOutputStream, servicesFacade: CompilerCallbackServicesFacade,
compilerOutputFormat: CompileService.OutputFormat, compilerOutputStream: RemoteOutputStream,
serviceOutputStream: RemoteOutputStream, compilerOutputFormat: CompileService.OutputFormat,
operationsTracer: RemoteOperationsTracer? serviceOutputStream: RemoteOutputStream,
operationsTracer: RemoteOperationsTracer?
): CompileService.CallResult<Int> = ): CompileService.CallResult<Int> =
doCompile(sessionId, args, compilerOutputStream, serviceOutputStream, operationsTracer) { printStream, eventManager, profiler -> doCompile(sessionId, args, compilerOutputStream, serviceOutputStream, operationsTracer) { printStream, eventManager, profiler ->
when (compilerOutputFormat) { when (compilerOutputFormat) {
CompileService.OutputFormat.PLAIN -> throw NotImplementedError("Only XML output is supported in remote incremental compilation") CompileService.OutputFormat.PLAIN -> throw NotImplementedError("Only XML output is supported in remote incremental compilation")
CompileService.OutputFormat.XML -> compiler[targetPlatform].execAndOutputXml(printStream, createCompileServices(servicesFacade, eventManager, profiler), *args) CompileService.OutputFormat.XML -> compiler[targetPlatform].execAndOutputXml(
} printStream,
createCompileServices(
servicesFacade,
eventManager,
profiler
),
*args
)
} }
}
override fun classesFqNamesByFiles( override fun classesFqNamesByFiles(
sessionId: Int, sourceFiles: Set<File> sessionId: Int, sourceFiles: Set<File>
@@ -366,11 +400,11 @@ class CompileServiceImpl(
} }
override fun compile( override fun compile(
sessionId: Int, sessionId: Int,
compilerArguments: Array<out String>, compilerArguments: Array<out String>,
compilationOptions: CompilationOptions, compilationOptions: CompilationOptions,
servicesFacade: CompilerServicesFacadeBase, servicesFacade: CompilerServicesFacadeBase,
compilationResults: CompilationResults? compilationResults: CompilationResults?
): CompileService.CallResult<Int> = ifAlive { ): CompileService.CallResult<Int> = ifAlive {
withValidClientOrSessionProxy(sessionId) { withValidClientOrSessionProxy(sessionId) {
val messageCollector = CompileServicesFacadeMessageCollector(servicesFacade, compilationOptions) val messageCollector = CompileServicesFacadeMessageCollector(servicesFacade, compilationOptions)
@@ -391,8 +425,7 @@ class CompileServiceImpl(
if (argumentParseError != null) { if (argumentParseError != null) {
messageCollector.report(CompilerMessageSeverity.ERROR, argumentParseError) messageCollector.report(CompilerMessageSeverity.ERROR, argumentParseError)
CompileService.CallResult.Good(ExitCode.COMPILATION_ERROR.code) CompileService.CallResult.Good(ExitCode.COMPILATION_ERROR.code)
} } else when (compilationOptions.compilerMode) {
else when (compilationOptions.compilerMode) {
CompilerMode.JPS_COMPILER -> { CompilerMode.JPS_COMPILER -> {
val jpsServicesFacade = servicesFacade as JpsCompilerServicesFacade val jpsServicesFacade = servicesFacade as JpsCompilerServicesFacade
@@ -418,8 +451,10 @@ class CompileServiceImpl(
withIC { withIC {
doCompile(sessionId, daemonReporter, tracer = null) { _, _ -> doCompile(sessionId, daemonReporter, tracer = null) { _, _ ->
execIncrementalCompiler(k2jvmArgs, gradleIncrementalArgs, gradleIncrementalServicesFacade, compilationResults!!, execIncrementalCompiler(
messageCollector, daemonReporter) k2jvmArgs, gradleIncrementalArgs, gradleIncrementalServicesFacade, compilationResults!!,
messageCollector, daemonReporter
)
} }
} }
} }
@@ -428,7 +463,13 @@ class CompileServiceImpl(
withJsIC { withJsIC {
doCompile(sessionId, daemonReporter, tracer = null) { _, _ -> doCompile(sessionId, daemonReporter, tracer = null) { _, _ ->
execJsIncrementalCompiler(k2jsArgs, gradleIncrementalArgs, gradleIncrementalServicesFacade, compilationResults!!, messageCollector) execJsIncrementalCompiler(
k2jsArgs,
gradleIncrementalArgs,
gradleIncrementalServicesFacade,
compilationResults!!,
messageCollector
)
} }
} }
} }
@@ -451,10 +492,9 @@ class CompileServiceImpl(
val allKotlinFiles = arrayListOf<File>() val allKotlinFiles = arrayListOf<File>()
val freeArgsWithoutKotlinFiles = arrayListOf<String>() val freeArgsWithoutKotlinFiles = arrayListOf<String>()
args.freeArgs.forEach { args.freeArgs.forEach {
if (it.endsWith(".kt") && File(it).exists()) { if (it.endsWith(".kt") && File(it).exists()) {
allKotlinFiles.add(File(it)) allKotlinFiles.add(File(it))
} } else {
else {
freeArgsWithoutKotlinFiles.add(it) freeArgsWithoutKotlinFiles.add(it)
} }
} }
@@ -464,21 +504,22 @@ class CompileServiceImpl(
val changedFiles = if (incrementalCompilationOptions.areFileChangesKnown) { val changedFiles = if (incrementalCompilationOptions.areFileChangesKnown) {
ChangedFiles.Known(incrementalCompilationOptions.modifiedFiles!!, incrementalCompilationOptions.deletedFiles!!) ChangedFiles.Known(incrementalCompilationOptions.modifiedFiles!!, incrementalCompilationOptions.deletedFiles!!)
} } else {
else {
ChangedFiles.Unknown() ChangedFiles.Unknown()
} }
val workingDir = incrementalCompilationOptions.workingDir val workingDir = incrementalCompilationOptions.workingDir
val versions = commonCacheVersions(workingDir, enabled = true) + val versionManagers = commonCacheVersionsManagers(workingDir, enabled = true) +
customCacheVersion(incrementalCompilationOptions.customCacheVersion, customCacheVersionManager(
incrementalCompilationOptions.customCacheVersionFileName, incrementalCompilationOptions.customCacheVersion,
workingDir, incrementalCompilationOptions.customCacheVersionFileName,
enabled = true) workingDir,
enabled = true
)
val modulesApiHistory = ModulesApiHistoryJs(incrementalCompilationOptions.modulesInfo) val modulesApiHistory = ModulesApiHistoryJs(incrementalCompilationOptions.modulesInfo)
val compiler = IncrementalJsCompilerRunner( val compiler = IncrementalJsCompilerRunner(
workingDir = workingDir, workingDir = workingDir,
cacheVersions = versions, cachesVersionManagers = versionManagers,
reporter = reporter, reporter = reporter,
buildHistoryFile = incrementalCompilationOptions.multiModuleICSettings.buildHistoryFile, buildHistoryFile = incrementalCompilationOptions.multiModuleICSettings.buildHistoryFile,
modulesApiHistory = modulesApiHistory modulesApiHistory = modulesApiHistory
@@ -487,12 +528,12 @@ class CompileServiceImpl(
} }
private fun execIncrementalCompiler( private fun execIncrementalCompiler(
k2jvmArgs: K2JVMCompilerArguments, k2jvmArgs: K2JVMCompilerArguments,
incrementalCompilationOptions: IncrementalCompilationOptions, incrementalCompilationOptions: IncrementalCompilationOptions,
servicesFacade: IncrementalCompilerServicesFacade, servicesFacade: IncrementalCompilerServicesFacade,
compilationResults: CompilationResults, compilationResults: CompilationResults,
compilerMessageCollector: MessageCollector, compilerMessageCollector: MessageCollector,
daemonMessageReporter: DaemonMessageReporter daemonMessageReporter: DaemonMessageReporter
): ExitCode { ): ExitCode {
val reporter = RemoteICReporter(servicesFacade, compilationResults, incrementalCompilationOptions) val reporter = RemoteICReporter(servicesFacade, compilationResults, incrementalCompilationOptions)
@@ -522,17 +563,18 @@ class CompileServiceImpl(
val changedFiles = if (incrementalCompilationOptions.areFileChangesKnown) { val changedFiles = if (incrementalCompilationOptions.areFileChangesKnown) {
ChangedFiles.Known(incrementalCompilationOptions.modifiedFiles!!, incrementalCompilationOptions.deletedFiles!!) ChangedFiles.Known(incrementalCompilationOptions.modifiedFiles!!, incrementalCompilationOptions.deletedFiles!!)
} } else {
else {
ChangedFiles.Unknown() ChangedFiles.Unknown()
} }
val workingDir = incrementalCompilationOptions.workingDir val workingDir = incrementalCompilationOptions.workingDir
val versions = commonCacheVersions(workingDir, enabled = true) + val versions = commonCacheVersionsManagers(workingDir, enabled = true) +
customCacheVersion(incrementalCompilationOptions.customCacheVersion, customCacheVersionManager(
incrementalCompilationOptions.customCacheVersionFileName, incrementalCompilationOptions.customCacheVersion,
workingDir, incrementalCompilationOptions.customCacheVersionFileName,
enabled = true) workingDir,
enabled = true
)
val modulesApiHistory = incrementalCompilationOptions.run { val modulesApiHistory = incrementalCompilationOptions.run {
if (!multiModuleICSettings.useModuleDetection) { if (!multiModuleICSettings.useModuleDetection) {
@@ -556,27 +598,34 @@ class CompileServiceImpl(
} }
override fun leaseReplSession( override fun leaseReplSession(
aliveFlagPath: String?, aliveFlagPath: String?,
targetPlatform: CompileService.TargetPlatform, targetPlatform: CompileService.TargetPlatform,
servicesFacade: CompilerCallbackServicesFacade, servicesFacade: CompilerCallbackServicesFacade,
templateClasspath: List<File>, templateClasspath: List<File>,
templateClassName: String, templateClassName: String,
scriptArgs: Array<out Any?>?, scriptArgs: Array<out Any?>?,
scriptArgsTypes: Array<out Class<out Any>>?, scriptArgsTypes: Array<out Class<out Any>>?,
compilerMessagesOutputStream: RemoteOutputStream, compilerMessagesOutputStream: RemoteOutputStream,
evalOutputStream: RemoteOutputStream?, evalOutputStream: RemoteOutputStream?,
evalErrorStream: RemoteOutputStream?, evalErrorStream: RemoteOutputStream?,
evalInputStream: RemoteInputStream?, evalInputStream: RemoteInputStream?,
operationsTracer: RemoteOperationsTracer? operationsTracer: RemoteOperationsTracer?
): CompileService.CallResult<Int> = ifAlive(minAliveness = Aliveness.Alive) { ): CompileService.CallResult<Int> = ifAlive(minAliveness = Aliveness.Alive) {
if (targetPlatform != CompileService.TargetPlatform.JVM) if (targetPlatform != CompileService.TargetPlatform.JVM)
CompileService.CallResult.Error("Sorry, only JVM target platform is supported now") CompileService.CallResult.Error("Sorry, only JVM target platform is supported now")
else { else {
val disposable = Disposer.newDisposable() val disposable = Disposer.newDisposable()
val compilerMessagesStream = PrintStream(BufferedOutputStream(RemoteOutputStreamClient(compilerMessagesOutputStream, DummyProfiler()), REMOTE_STREAM_BUFFER_SIZE)) val compilerMessagesStream = PrintStream(
BufferedOutputStream(
RemoteOutputStreamClient(compilerMessagesOutputStream, DummyProfiler()),
REMOTE_STREAM_BUFFER_SIZE
)
)
val messageCollector = KeepFirstErrorMessageCollector(compilerMessagesStream) val messageCollector = KeepFirstErrorMessageCollector(compilerMessagesStream)
val repl = KotlinJvmReplService(disposable, port, templateClasspath, templateClassName, val repl = KotlinJvmReplService(
messageCollector, operationsTracer) disposable, port, templateClasspath, templateClassName,
messageCollector, operationsTracer
)
val sessionId = state.sessions.leaseSession(ClientOrSessionProxy(aliveFlagPath, repl, disposable)) val sessionId = state.sessions.leaseSession(ClientOrSessionProxy(aliveFlagPath, repl, disposable))
CompileService.CallResult.Good(sessionId) CompileService.CallResult.Good(sessionId)
@@ -587,42 +636,49 @@ class CompileServiceImpl(
override fun releaseReplSession(sessionId: Int): CompileService.CallResult<Nothing> = releaseCompileSession(sessionId) override fun releaseReplSession(sessionId: Int): CompileService.CallResult<Nothing> = releaseCompileSession(sessionId)
override fun remoteReplLineCheck(sessionId: Int, codeLine: ReplCodeLine): CompileService.CallResult<ReplCheckResult> = override fun remoteReplLineCheck(sessionId: Int, codeLine: ReplCodeLine): CompileService.CallResult<ReplCheckResult> =
ifAlive(minAliveness = Aliveness.Alive) { ifAlive(minAliveness = Aliveness.Alive) {
withValidRepl(sessionId) { withValidRepl(sessionId) {
CompileService.CallResult.Good(check(codeLine)) CompileService.CallResult.Good(check(codeLine))
}
} }
}
override fun remoteReplLineCompile(sessionId: Int, codeLine: ReplCodeLine, history: List<ReplCodeLine>?): CompileService.CallResult<ReplCompileResult> = override fun remoteReplLineCompile(
ifAlive(minAliveness = Aliveness.Alive) { sessionId: Int,
withValidRepl(sessionId) { codeLine: ReplCodeLine,
CompileService.CallResult.Good(compile(codeLine, history)) history: List<ReplCodeLine>?
} ): CompileService.CallResult<ReplCompileResult> =
ifAlive(minAliveness = Aliveness.Alive) {
withValidRepl(sessionId) {
CompileService.CallResult.Good(compile(codeLine, history))
} }
}
override fun remoteReplLineEval( override fun remoteReplLineEval(
sessionId: Int, sessionId: Int,
codeLine: ReplCodeLine, codeLine: ReplCodeLine,
history: List<ReplCodeLine>? history: List<ReplCodeLine>?
): CompileService.CallResult<ReplEvalResult> = ): CompileService.CallResult<ReplEvalResult> =
ifAlive(minAliveness = Aliveness.Alive) { ifAlive(minAliveness = Aliveness.Alive) {
CompileService.CallResult.Error("Eval on daemon is not supported") CompileService.CallResult.Error("Eval on daemon is not supported")
} }
override fun leaseReplSession(aliveFlagPath: String?, override fun leaseReplSession(
compilerArguments: Array<out String>, aliveFlagPath: String?,
compilationOptions: CompilationOptions, compilerArguments: Array<out String>,
servicesFacade: CompilerServicesFacadeBase, compilationOptions: CompilationOptions,
templateClasspath: List<File>, servicesFacade: CompilerServicesFacadeBase,
templateClassName: String templateClasspath: List<File>,
): CompileService.CallResult<Int> = ifAlive(minAliveness = Aliveness.Alive) { templateClassName: String
): CompileService.CallResult<Int> = ifAlive(minAliveness = Aliveness.Alive) {
if (compilationOptions.targetPlatform != CompileService.TargetPlatform.JVM) if (compilationOptions.targetPlatform != CompileService.TargetPlatform.JVM)
CompileService.CallResult.Error("Sorry, only JVM target platform is supported now") CompileService.CallResult.Error("Sorry, only JVM target platform is supported now")
else { else {
val disposable = Disposer.newDisposable() val disposable = Disposer.newDisposable()
val messageCollector = CompileServicesFacadeMessageCollector(servicesFacade, compilationOptions) val messageCollector = CompileServicesFacadeMessageCollector(servicesFacade, compilationOptions)
val repl = KotlinJvmReplService(disposable, port, templateClasspath, templateClassName, val repl = KotlinJvmReplService(
messageCollector, null) disposable, port, templateClasspath, templateClassName,
messageCollector, null
)
val sessionId = state.sessions.leaseSession(ClientOrSessionProxy(aliveFlagPath, repl, disposable)) val sessionId = state.sessions.leaseSession(ClientOrSessionProxy(aliveFlagPath, repl, disposable))
CompileService.CallResult.Good(sessionId) CompileService.CallResult.Good(sessionId)
@@ -630,29 +686,29 @@ class CompileServiceImpl(
} }
override fun replCreateState(sessionId: Int): CompileService.CallResult<ReplStateFacade> = override fun replCreateState(sessionId: Int): CompileService.CallResult<ReplStateFacade> =
ifAlive(minAliveness = Aliveness.Alive) { ifAlive(minAliveness = Aliveness.Alive) {
withValidRepl(sessionId) { withValidRepl(sessionId) {
CompileService.CallResult.Good(createRemoteState(port)) CompileService.CallResult.Good(createRemoteState(port))
}
} }
}
override fun replCheck(sessionId: Int, replStateId: Int, codeLine: ReplCodeLine): CompileService.CallResult<ReplCheckResult> = override fun replCheck(sessionId: Int, replStateId: Int, codeLine: ReplCodeLine): CompileService.CallResult<ReplCheckResult> =
ifAlive(minAliveness = Aliveness.Alive) { ifAlive(minAliveness = Aliveness.Alive) {
withValidRepl(sessionId) { withValidRepl(sessionId) {
withValidReplState(replStateId) { state -> withValidReplState(replStateId) { state ->
check(state, codeLine) check(state, codeLine)
}
} }
} }
}
override fun replCompile(sessionId: Int, replStateId: Int, codeLine: ReplCodeLine): CompileService.CallResult<ReplCompileResult> = override fun replCompile(sessionId: Int, replStateId: Int, codeLine: ReplCodeLine): CompileService.CallResult<ReplCompileResult> =
ifAlive(minAliveness = Aliveness.Alive) { ifAlive(minAliveness = Aliveness.Alive) {
withValidRepl(sessionId) { withValidRepl(sessionId) {
withValidReplState(replStateId) { state -> withValidReplState(replStateId) { state ->
compile(state, codeLine) compile(state, codeLine)
}
} }
} }
}
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// internal implementation stuff // internal implementation stuff
@@ -675,13 +731,17 @@ class CompileServiceImpl(
try { try {
// cleanup for the case of incorrect restart and many other situations // cleanup for the case of incorrect restart and many other situations
UnicastRemoteObject.unexportObject(this, false) UnicastRemoteObject.unexportObject(this, false)
} } catch (e: NoSuchObjectException) {
catch (e: NoSuchObjectException) {
// ignoring if object already exported // ignoring if object already exported
} }
val stub = UnicastRemoteObject.exportObject(this, port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory) as CompileService val stub = UnicastRemoteObject.exportObject(
registry.rebind (COMPILER_SERVICE_RMI_NAME, stub) this,
port,
LoopbackNetworkInterface.clientLoopbackSocketFactory,
LoopbackNetworkInterface.serverLoopbackSocketFactory
) as CompileService
registry.rebind(COMPILER_SERVICE_RMI_NAME, stub)
timer.schedule(10) { timer.schedule(10) {
exceptionLoggingTimerThread { initiateElections() } exceptionLoggingTimerThread { initiateElections() }
@@ -697,8 +757,7 @@ class CompileServiceImpl(
private inline fun exceptionLoggingTimerThread(body: () -> Unit) { private inline fun exceptionLoggingTimerThread(body: () -> Unit) {
try { try {
body() body()
} } catch (e: Throwable) {
catch (e: Throwable) {
System.err.println("Exception in timer thread: " + e.message) System.err.println("Exception in timer thread: " + e.message)
e.printStackTrace(System.err) e.printStackTrace(System.err)
log.log(Level.SEVERE, "Exception in timer thread", e) log.log(Level.SEVERE, "Exception in timer thread", e)
@@ -768,12 +827,22 @@ class CompileServiceImpl(
ifAliveUnit { ifAliveUnit {
log.info("initiate elections") log.info("initiate elections")
val aliveWithOpts = walkDaemons(File(daemonOptions.runFilesPathOrDefault), compilerId, runFile, filter = { _, p -> p != port }, report = { _, msg -> log.info(msg) }).toList() val aliveWithOpts = walkDaemons(
val comparator = compareByDescending<DaemonWithMetadata, DaemonJVMOptions>(DaemonJVMOptionsMemoryComparator(), { it.jvmOptions }) File(daemonOptions.runFilesPathOrDefault),
compilerId,
runFile,
filter = { _, p -> p != port },
report = { _, msg -> log.info(msg) }).toList()
val comparator =
compareByDescending<DaemonWithMetadata, DaemonJVMOptions>(DaemonJVMOptionsMemoryComparator(), { it.jvmOptions })
.thenBy(FileAgeComparator()) { it.runFile } .thenBy(FileAgeComparator()) { it.runFile }
aliveWithOpts.maxWith(comparator)?.let { bestDaemonWithMetadata -> aliveWithOpts.maxWith(comparator)?.let { bestDaemonWithMetadata ->
val fattestOpts = bestDaemonWithMetadata.jvmOptions val fattestOpts = bestDaemonWithMetadata.jvmOptions
if (fattestOpts memorywiseFitsInto daemonJVMOptions && FileAgeComparator().compare(bestDaemonWithMetadata.runFile, runFile) < 0 ) { if (fattestOpts memorywiseFitsInto daemonJVMOptions && FileAgeComparator().compare(
bestDaemonWithMetadata.runFile,
runFile
) < 0
) {
// all others are smaller that me, take overs' clients and shut them down // all others are smaller that me, take overs' clients and shut them down
log.info("$LOG_PREFIX_ASSUMING_OTHER_DAEMONS_HAVE lower prio, taking clients from them and schedule them to shutdown: my runfile: ${runFile.name} (${runFile.lastModified()}) vs best other runfile: ${bestDaemonWithMetadata.runFile.name} (${bestDaemonWithMetadata.runFile.lastModified()})") log.info("$LOG_PREFIX_ASSUMING_OTHER_DAEMONS_HAVE lower prio, taking clients from them and schedule them to shutdown: my runfile: ${runFile.name} (${runFile.lastModified()}) vs best other runfile: ${bestDaemonWithMetadata.runFile.name} (${bestDaemonWithMetadata.runFile.lastModified()})")
aliveWithOpts.forEach { (daemon, runFile, _) -> aliveWithOpts.forEach { (daemon, runFile, _) ->
@@ -782,8 +851,7 @@ class CompileServiceImpl(
it.get().forEach { clientAliveFile -> registerClient(clientAliveFile) } it.get().forEach { clientAliveFile -> registerClient(clientAliveFile) }
} }
daemon.scheduleShutdown(true) daemon.scheduleShutdown(true)
} } catch (e: Throwable) {
catch (e: Throwable) {
log.info("Cannot connect to a daemon, assuming dying ('${runFile.canonicalPath}'): ${e.message}") log.info("Cannot connect to a daemon, assuming dying ('${runFile.canonicalPath}'): ${e.message}")
} }
} }
@@ -803,15 +871,18 @@ class CompileServiceImpl(
// B performs election: (1) is false because neither A nor C does not fit into B, (2) is false because B does not fit into neither A nor C. // B performs election: (1) is false because neither A nor C does not fit into B, (2) is false because B does not fit into neither A nor C.
// C performs election: (1) is false because B is better than A and B does not fit into C, (2) is false C does not fit into neither A nor B. // C performs election: (1) is false because B is better than A and B does not fit into C, (2) is false C does not fit into neither A nor B.
// Result: all daemons are alive and well. // Result: all daemons are alive and well.
else if (daemonJVMOptions memorywiseFitsInto fattestOpts && FileAgeComparator().compare(bestDaemonWithMetadata.runFile, runFile) > 0) { else if (daemonJVMOptions memorywiseFitsInto fattestOpts && FileAgeComparator().compare(
bestDaemonWithMetadata.runFile,
runFile
) > 0
) {
// there is at least one bigger, handover my clients to it and shutdown // there is at least one bigger, handover my clients to it and shutdown
log.info("$LOG_PREFIX_ASSUMING_OTHER_DAEMONS_HAVE higher prio, handover clients to it and schedule shutdown: my runfile: ${runFile.name} (${runFile.lastModified()}) vs best other runfile: ${bestDaemonWithMetadata.runFile.name} (${bestDaemonWithMetadata.runFile.lastModified()})") log.info("$LOG_PREFIX_ASSUMING_OTHER_DAEMONS_HAVE higher prio, handover clients to it and schedule shutdown: my runfile: ${runFile.name} (${runFile.lastModified()}) vs best other runfile: ${bestDaemonWithMetadata.runFile.name} (${bestDaemonWithMetadata.runFile.lastModified()})")
getClients().takeIf { it.isGood }?.let { getClients().takeIf { it.isGood }?.let {
it.get().forEach { bestDaemonWithMetadata.daemon.registerClient(it) } it.get().forEach { bestDaemonWithMetadata.daemon.registerClient(it) }
} }
scheduleShutdown(true) scheduleShutdown(true)
} } else {
else {
// undecided, do nothing // undecided, do nothing
log.info("$LOG_PREFIX_ASSUMING_OTHER_DAEMONS_HAVE equal prio, continue: ${runFile.name} (${runFile.lastModified()}) vs best other runfile: ${bestDaemonWithMetadata.runFile.name} (${bestDaemonWithMetadata.runFile.lastModified()})") log.info("$LOG_PREFIX_ASSUMING_OTHER_DAEMONS_HAVE equal prio, continue: ${runFile.name} (${runFile.lastModified()}) vs best other runfile: ${bestDaemonWithMetadata.runFile.name} (${bestDaemonWithMetadata.runFile.lastModified()})")
// TODO: implement some behaviour here, e.g.: // TODO: implement some behaviour here, e.g.:
@@ -825,7 +896,7 @@ class CompileServiceImpl(
private fun shutdownNow() { private fun shutdownNow() {
log.info("Shutdown started") log.info("Shutdown started")
fun Long.mb() = this / (1024 * 1024) fun Long.mb() = this / (1024 * 1024)
with (Runtime.getRuntime()) { with(Runtime.getRuntime()) {
log.info("Memory stats: total: ${totalMemory().mb()}mb, free: ${freeMemory().mb()}mb, max: ${maxMemory().mb()}mb") log.info("Memory stats: total: ${totalMemory().mb()}mb, free: ${freeMemory().mb()}mb, max: ${maxMemory().mb()}mb")
} }
state.alive.set(Aliveness.Dying.ordinal) state.alive.set(Aliveness.Dying.ordinal)
@@ -846,14 +917,13 @@ class CompileServiceImpl(
state.delayedShutdownQueued.set(false) state.delayedShutdownQueued.set(false)
if (currentClientsCount == state.clientsCounter && if (currentClientsCount == state.clientsCounter &&
currentCompilationsCount == compilationsCounter.get() && currentCompilationsCount == compilationsCounter.get() &&
currentSessionId == state.sessions.lastSessionId) currentSessionId == state.sessions.lastSessionId
{ ) {
ifAliveExclusiveUnit(minAliveness = Aliveness.LastSession) { ifAliveExclusiveUnit(minAliveness = Aliveness.LastSession) {
log.fine("Execute delayed shutdown") log.fine("Execute delayed shutdown")
shutdownNow() shutdownNow()
} }
} } else {
else {
log.info("Cancel delayed shutdown due to a new activity") log.info("Cancel delayed shutdown due to a new activity")
} }
} }
@@ -864,7 +934,8 @@ class CompileServiceImpl(
fun shutdownIfIdle() = when { fun shutdownIfIdle() = when {
state.sessions.isEmpty() -> shutdownWithDelay() state.sessions.isEmpty() -> shutdownWithDelay()
else -> { else -> {
daemonOptions.autoshutdownIdleSeconds = TimeUnit.MILLISECONDS.toSeconds(daemonOptions.forceShutdownTimeoutMilliseconds).toInt() daemonOptions.autoshutdownIdleSeconds =
TimeUnit.MILLISECONDS.toSeconds(daemonOptions.forceShutdownTimeoutMilliseconds).toInt()
daemonOptions.autoshutdownUnusedSeconds = daemonOptions.autoshutdownIdleSeconds daemonOptions.autoshutdownUnusedSeconds = daemonOptions.autoshutdownIdleSeconds
log.info("Some sessions are active, waiting for them to finish") log.info("Some sessions are active, waiting for them to finish")
log.info("Unused/idle timeouts are set to ${daemonOptions.autoshutdownUnusedSeconds}/${daemonOptions.autoshutdownIdleSeconds}s") log.info("Unused/idle timeouts are set to ${daemonOptions.autoshutdownUnusedSeconds}/${daemonOptions.autoshutdownIdleSeconds}s")
@@ -879,8 +950,7 @@ class CompileServiceImpl(
if (!onAnotherThread) { if (!onAnotherThread) {
shutdownIfIdle() shutdownIfIdle()
} } else {
else {
timer.schedule(1) { timer.schedule(1) {
ifAliveExclusiveUnit(minAliveness = Aliveness.LastSession) { ifAliveExclusiveUnit(minAliveness = Aliveness.LastSession) {
shutdownIfIdle() shutdownIfIdle()
@@ -891,64 +961,79 @@ class CompileServiceImpl(
} }
// todo: remove after remoteIncrementalCompile is removed // todo: remove after remoteIncrementalCompile is removed
private fun doCompile(sessionId: Int, private fun doCompile(
args: Array<out String>, sessionId: Int,
compilerMessagesStreamProxy: RemoteOutputStream, args: Array<out String>,
serviceOutputStreamProxy: RemoteOutputStream, compilerMessagesStreamProxy: RemoteOutputStream,
operationsTracer: RemoteOperationsTracer?, serviceOutputStreamProxy: RemoteOutputStream,
body: (PrintStream, EventManager, Profiler) -> ExitCode): CompileService.CallResult<Int> = operationsTracer: RemoteOperationsTracer?,
ifAlive { body: (PrintStream, EventManager, Profiler) -> ExitCode
withValidClientOrSessionProxy(sessionId) { ): CompileService.CallResult<Int> =
operationsTracer?.before("compile") ifAlive {
val rpcProfiler = if (daemonOptions.reportPerf) WallAndThreadTotalProfiler() else DummyProfiler() withValidClientOrSessionProxy(sessionId) {
val eventManger = EventManagerImpl() operationsTracer?.before("compile")
val compilerMessagesStream = PrintStream(BufferedOutputStream(RemoteOutputStreamClient(compilerMessagesStreamProxy, rpcProfiler), REMOTE_STREAM_BUFFER_SIZE)) val rpcProfiler = if (daemonOptions.reportPerf) WallAndThreadTotalProfiler() else DummyProfiler()
val serviceOutputStream = PrintStream(BufferedOutputStream(RemoteOutputStreamClient(serviceOutputStreamProxy, rpcProfiler), REMOTE_STREAM_BUFFER_SIZE)) val eventManger = EventManagerImpl()
try { val compilerMessagesStream = PrintStream(
val compileServiceReporter = DaemonMessageReporterPrintStreamAdapter(serviceOutputStream) BufferedOutputStream(
if (args.none()) RemoteOutputStreamClient(compilerMessagesStreamProxy, rpcProfiler),
throw IllegalArgumentException("Error: empty arguments list.") REMOTE_STREAM_BUFFER_SIZE
log.info("Starting compilation with args: " + args.joinToString(" ")) )
val exitCode = checkedCompile(compileServiceReporter, rpcProfiler) { )
body(compilerMessagesStream, eventManger, rpcProfiler).code val serviceOutputStream = PrintStream(
} BufferedOutputStream(
CompileService.CallResult.Good(exitCode) RemoteOutputStreamClient(serviceOutputStreamProxy, rpcProfiler),
} REMOTE_STREAM_BUFFER_SIZE
finally { )
serviceOutputStream.flush() )
compilerMessagesStream.flush() try {
eventManger.fireCompilationFinished() val compileServiceReporter = DaemonMessageReporterPrintStreamAdapter(serviceOutputStream)
operationsTracer?.after("compile") if (args.none())
throw IllegalArgumentException("Error: empty arguments list.")
log.info("Starting compilation with args: " + args.joinToString(" "))
val exitCode = checkedCompile(compileServiceReporter, rpcProfiler) {
body(compilerMessagesStream, eventManger, rpcProfiler).code
} }
CompileService.CallResult.Good(exitCode)
} finally {
serviceOutputStream.flush()
compilerMessagesStream.flush()
eventManger.fireCompilationFinished()
operationsTracer?.after("compile")
} }
} }
}
private fun doCompile(sessionId: Int, private fun doCompile(
daemonMessageReporter: DaemonMessageReporter, sessionId: Int,
tracer: RemoteOperationsTracer?, daemonMessageReporter: DaemonMessageReporter,
body: (EventManager, Profiler) -> ExitCode): CompileService.CallResult<Int> = tracer: RemoteOperationsTracer?,
ifAlive { body: (EventManager, Profiler) -> ExitCode
withValidClientOrSessionProxy(sessionId) { ): CompileService.CallResult<Int> =
tracer?.before("compile") ifAlive {
val rpcProfiler = if (daemonOptions.reportPerf) WallAndThreadTotalProfiler() else DummyProfiler() withValidClientOrSessionProxy(sessionId) {
val eventManger = EventManagerImpl() tracer?.before("compile")
try { val rpcProfiler = if (daemonOptions.reportPerf) WallAndThreadTotalProfiler() else DummyProfiler()
val exitCode = checkedCompile(daemonMessageReporter, rpcProfiler) { val eventManger = EventManagerImpl()
body(eventManger, rpcProfiler).code try {
} val exitCode = checkedCompile(daemonMessageReporter, rpcProfiler) {
CompileService.CallResult.Good(exitCode) body(eventManger, rpcProfiler).code
}
finally {
eventManger.fireCompilationFinished()
tracer?.after("compile")
} }
CompileService.CallResult.Good(exitCode)
} finally {
eventManger.fireCompilationFinished()
tracer?.after("compile")
} }
} }
}
private fun createCompileServices(facade: CompilerCallbackServicesFacade, eventManager: EventManager, rpcProfiler: Profiler): Services { private fun createCompileServices(facade: CompilerCallbackServicesFacade, eventManager: EventManager, rpcProfiler: Profiler): Services {
val builder = Services.Builder() val builder = Services.Builder()
if (facade.hasIncrementalCaches()) { if (facade.hasIncrementalCaches()) {
builder.register(IncrementalCompilationComponents::class.java, RemoteIncrementalCompilationComponentsClient(facade, eventManager, rpcProfiler)) builder.register(
IncrementalCompilationComponents::class.java,
RemoteIncrementalCompilationComponentsClient(facade, eventManager, rpcProfiler)
)
} }
if (facade.hasLookupTracker()) { if (facade.hasLookupTracker()) {
builder.register(LookupTracker::class.java, RemoteLookupTrackerClient(facade, eventManager, rpcProfiler)) builder.register(LookupTracker::class.java, RemoteLookupTrackerClient(facade, eventManager, rpcProfiler))
@@ -970,7 +1055,7 @@ class CompileServiceImpl(
} }
private fun<R> checkedCompile(daemonMessageReporter: DaemonMessageReporter, rpcProfiler: Profiler, body: () -> R): R { private fun <R> checkedCompile(daemonMessageReporter: DaemonMessageReporter, rpcProfiler: Profiler, body: () -> R): R {
try { try {
val profiler = if (daemonOptions.reportPerf) WallAndThreadAndMemoryTotalProfiler(withGC = false) else DummyProfiler() val profiler = if (daemonOptions.reportPerf) WallAndThreadAndMemoryTotalProfiler(withGC = false) else DummyProfiler()
@@ -986,7 +1071,9 @@ class CompileServiceImpl(
val pc = profiler.getTotalCounters() val pc = profiler.getTotalCounters()
val rpc = rpcProfiler.getTotalCounters() val rpc = rpcProfiler.getTotalCounters()
"PERF: Compile on daemon: ${pc.time.ms()} ms; thread: user ${pc.threadUserTime.ms()} ms, sys ${(pc.threadTime - pc.threadUserTime).ms()} ms; rpc: ${rpc.count} calls, ${rpc.time.ms()} ms, thread ${rpc.threadTime.ms()} ms; memory: ${endMem.kb()} kb (${"%+d".format(pc.memory.kb())} kb)".let { "PERF: Compile on daemon: ${pc.time.ms()} ms; thread: user ${pc.threadUserTime.ms()} ms, sys ${(pc.threadTime - pc.threadUserTime).ms()} ms; rpc: ${rpc.count} calls, ${rpc.time.ms()} ms, thread ${rpc.threadTime.ms()} ms; memory: ${endMem.kb()} kb (${"%+d".format(
pc.memory.kb()
)} kb)".let {
daemonMessageReporter.report(ReportSeverity.INFO, it) daemonMessageReporter.report(ReportSeverity.INFO, it)
log.info(it) log.info(it)
} }
@@ -1009,7 +1096,8 @@ class CompileServiceImpl(
if (e.cause != null && e.cause != e) { if (e.cause != null && e.cause != e) {
"\nCaused by: ${e.cause}\n ${e.cause!!.stackTrace.joinToString("\n ")}" "\nCaused by: ${e.cause}\n ${e.cause!!.stackTrace.joinToString("\n ")}"
} else "" } else ""
}") }"
)
throw e throw e
} }
} }
@@ -1019,7 +1107,10 @@ class CompileServiceImpl(
(KotlinCoreEnvironment.applicationEnvironment?.jarFileSystem as? CoreJarFileSystem)?.clearHandlersCache() (KotlinCoreEnvironment.applicationEnvironment?.jarFileSystem as? CoreJarFileSystem)?.clearHandlersCache()
} }
private inline fun<R> ifAlive(minAliveness: Aliveness = Aliveness.LastSession, body: () -> CompileService.CallResult<R>): CompileService.CallResult<R> = rwlock.read { private inline fun <R> ifAlive(
minAliveness: Aliveness = Aliveness.LastSession,
body: () -> CompileService.CallResult<R>
): CompileService.CallResult<R> = rwlock.read {
ifAliveChecksImpl(minAliveness, body) ifAliveChecksImpl(minAliveness, body)
} }
@@ -1030,7 +1121,10 @@ class CompileServiceImpl(
} }
} }
private inline fun<R> ifAliveExclusive(minAliveness: Aliveness = Aliveness.LastSession, body: () -> CompileService.CallResult<R>): CompileService.CallResult<R> = rwlock.write { private inline fun <R> ifAliveExclusive(
minAliveness: Aliveness = Aliveness.LastSession,
body: () -> CompileService.CallResult<R>
): CompileService.CallResult<R> = rwlock.write {
ifAliveChecksImpl(minAliveness, body) ifAliveChecksImpl(minAliveness, body)
} }
@@ -1041,7 +1135,10 @@ class CompileServiceImpl(
} }
} }
private inline fun<R> ifAliveChecksImpl(minAliveness: Aliveness = Aliveness.LastSession, body: () -> CompileService.CallResult<R>): CompileService.CallResult<R> { private inline fun <R> ifAliveChecksImpl(
minAliveness: Aliveness = Aliveness.LastSession,
body: () -> CompileService.CallResult<R>
): CompileService.CallResult<R> {
val curState = state.alive.get() val curState = state.alive.get()
return when { return when {
curState < minAliveness.ordinal -> { curState < minAliveness.ordinal -> {
@@ -1051,8 +1148,7 @@ class CompileServiceImpl(
else -> { else -> {
try { try {
body() body()
} } catch (e: Throwable) {
catch (e: Throwable) {
log.log(Level.SEVERE, "Exception", e) log.log(Level.SEVERE, "Exception", e)
CompileService.CallResult.Error(e.message ?: "unknown") CompileService.CallResult.Error(e.message ?: "unknown")
} }
@@ -1060,31 +1156,34 @@ class CompileServiceImpl(
} }
} }
private inline fun<R> withValidClientOrSessionProxy(sessionId: Int, private inline fun <R> withValidClientOrSessionProxy(
body: (ClientOrSessionProxy<Any>?) -> CompileService.CallResult<R> sessionId: Int,
body: (ClientOrSessionProxy<Any>?) -> CompileService.CallResult<R>
): CompileService.CallResult<R> { ): CompileService.CallResult<R> {
val session: ClientOrSessionProxy<Any>? = val session: ClientOrSessionProxy<Any>? =
if (sessionId == CompileService.NO_SESSION) null if (sessionId == CompileService.NO_SESSION) null
else state.sessions[sessionId] ?: return CompileService.CallResult.Error("Unknown or invalid session $sessionId") else state.sessions[sessionId] ?: return CompileService.CallResult.Error("Unknown or invalid session $sessionId")
try { try {
compilationsCounter.incrementAndGet() compilationsCounter.incrementAndGet()
return body(session) return body(session)
} } finally {
finally {
_lastUsedSeconds = nowSeconds() _lastUsedSeconds = nowSeconds()
} }
} }
private inline fun<R> withValidRepl(sessionId: Int, body: KotlinJvmReplService.() -> R): CompileService.CallResult<R> = private inline fun <R> withValidRepl(sessionId: Int, body: KotlinJvmReplService.() -> R): CompileService.CallResult<R> =
withValidClientOrSessionProxy(sessionId) { session -> withValidClientOrSessionProxy(sessionId) { session ->
(session?.data as? KotlinJvmReplService?)?.let { (session?.data as? KotlinJvmReplService?)?.let {
CompileService.CallResult.Good(it.body()) CompileService.CallResult.Good(it.body())
} ?: CompileService.CallResult.Error("Not a REPL session $sessionId") } ?: CompileService.CallResult.Error("Not a REPL session $sessionId")
} }
@JvmName("withValidRepl1") @JvmName("withValidRepl1")
private inline fun<R> withValidRepl(sessionId: Int, body: KotlinJvmReplService.() -> CompileService.CallResult<R>): CompileService.CallResult<R> = private inline fun <R> withValidRepl(
withValidClientOrSessionProxy(sessionId) { session -> sessionId: Int,
(session?.data as? KotlinJvmReplService?)?.body() ?: CompileService.CallResult.Error("Not a REPL session $sessionId") body: KotlinJvmReplService.() -> CompileService.CallResult<R>
} ): CompileService.CallResult<R> =
withValidClientOrSessionProxy(sessionId) { session ->
(session?.data as? KotlinJvmReplService?)?.body() ?: CompileService.CallResult.Error("Not a REPL session $sessionId")
}
} }
@@ -28,6 +28,8 @@ import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.parsing.classesFqNames import org.jetbrains.kotlin.incremental.parsing.classesFqNames
import org.jetbrains.kotlin.incremental.storage.version.CacheVersionManager
import org.jetbrains.kotlin.incremental.storage.version.saveIfNeeded
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.progress.CompilationCanceledStatus import org.jetbrains.kotlin.progress.CompilationCanceledStatus
import java.io.File import java.io.File
@@ -37,11 +39,11 @@ abstract class IncrementalCompilerRunner<
Args : CommonCompilerArguments, Args : CommonCompilerArguments,
CacheManager : IncrementalCachesManager<*> CacheManager : IncrementalCachesManager<*>
>( >(
workingDir: File, workingDir: File,
cacheDirName: String, cacheDirName: String,
protected val cacheVersions: List<CacheVersion>, protected val cachesVersionManagers: List<CacheVersionManager>,
protected val reporter: ICReporter, protected val reporter: ICReporter,
private val buildHistoryFile: File, private val buildHistoryFile: File,
private val localStateDirs: Collection<File> = emptyList() private val localStateDirs: Collection<File> = emptyList()
) { ) {
@@ -268,7 +270,7 @@ abstract class IncrementalCompilerRunner<
processChangesAfterBuild(compilationMode, currentBuildInfo, dirtyData) processChangesAfterBuild(compilationMode, currentBuildInfo, dirtyData)
if (exitCode == ExitCode.OK) { if (exitCode == ExitCode.OK) {
cacheVersions.forEach { it.saveIfNeeded() } cachesVersionManagers.forEach { it.saveIfNeeded() }
} }
return exitCode return exitCode
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.js.* import org.jetbrains.kotlin.incremental.js.*
import org.jetbrains.kotlin.incremental.storage.version.CacheVersionManager
import org.jetbrains.kotlin.incremental.multiproject.EmptyModulesApiHistory import org.jetbrains.kotlin.incremental.multiproject.EmptyModulesApiHistory
import org.jetbrains.kotlin.incremental.multiproject.ModulesApiHistory import org.jetbrains.kotlin.incremental.multiproject.ModulesApiHistory
import java.io.File import java.io.File
@@ -38,7 +39,7 @@ fun makeJsIncrementally(
reporter: ICReporter = EmptyICReporter reporter: ICReporter = EmptyICReporter
) { ) {
val isIncremental = IncrementalCompilation.isEnabledForJs() val isIncremental = IncrementalCompilation.isEnabledForJs()
val versions = commonCacheVersions(cachesDir, isIncremental) + standaloneCacheVersion(cachesDir, isIncremental) val versions = commonCacheVersionsManagers(cachesDir, isIncremental) + standaloneCacheVersionManager(cachesDir, isIncremental)
val allKotlinFiles = sourceRoots.asSequence().flatMap { it.walk() } val allKotlinFiles = sourceRoots.asSequence().flatMap { it.walk() }
.filter { it.isFile && it.extension.equals("kt", ignoreCase = true) }.toList() .filter { it.isFile && it.extension.equals("kt", ignoreCase = true) }.toList()
val buildHistoryFile = File(cachesDir, "build-history.bin") val buildHistoryFile = File(cachesDir, "build-history.bin")
@@ -64,16 +65,16 @@ inline fun <R> withJsIC(fn: () -> R): R {
} }
class IncrementalJsCompilerRunner( class IncrementalJsCompilerRunner(
workingDir: File, workingDir: File,
cacheVersions: List<CacheVersion>, cachesVersionManagers: List<CacheVersionManager>,
reporter: ICReporter, reporter: ICReporter,
buildHistoryFile: File, buildHistoryFile: File,
private val modulesApiHistory: ModulesApiHistory private val modulesApiHistory: ModulesApiHistory
) : IncrementalCompilerRunner<K2JSCompilerArguments, IncrementalJsCachesManager>( ) : IncrementalCompilerRunner<K2JSCompilerArguments, IncrementalJsCachesManager>(
workingDir, workingDir,
"caches-js", "caches-js",
cacheVersions, cachesVersionManagers,
reporter, reporter,
buildHistoryFile = buildHistoryFile buildHistoryFile = buildHistoryFile
) { ) {
override fun isICEnabled(): Boolean = override fun isICEnabled(): Boolean =
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.multiproject.EmptyModulesApiHistory import org.jetbrains.kotlin.incremental.multiproject.EmptyModulesApiHistory
import org.jetbrains.kotlin.incremental.multiproject.ModulesApiHistory import org.jetbrains.kotlin.incremental.multiproject.ModulesApiHistory
import org.jetbrains.kotlin.incremental.storage.version.CacheVersionManager
import org.jetbrains.kotlin.incremental.util.Either import org.jetbrains.kotlin.incremental.util.Either
import org.jetbrains.kotlin.load.java.JavaClassesTracker import org.jetbrains.kotlin.load.java.JavaClassesTracker
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
@@ -57,7 +58,7 @@ fun makeIncrementally(
reporter: ICReporter = EmptyICReporter reporter: ICReporter = EmptyICReporter
) { ) {
val isIncremental = IncrementalCompilation.isEnabledForJvm() val isIncremental = IncrementalCompilation.isEnabledForJvm()
val versions = commonCacheVersions(cachesDir, isIncremental) + standaloneCacheVersion(cachesDir, isIncremental) val versions = commonCacheVersionsManagers(cachesDir, isIncremental) + standaloneCacheVersionManager(cachesDir, isIncremental)
val kotlinExtensions = listOf("kt", "kts") val kotlinExtensions = listOf("kt", "kts")
val allExtensions = kotlinExtensions + listOf("java") val allExtensions = kotlinExtensions + listOf("java")
@@ -99,20 +100,20 @@ inline fun <R> withIC(enabled: Boolean = true, fn: ()->R): R {
} }
class IncrementalJvmCompilerRunner( class IncrementalJvmCompilerRunner(
workingDir: File, workingDir: File,
private val javaSourceRoots: Set<JvmSourceRoot>, private val javaSourceRoots: Set<JvmSourceRoot>,
cacheVersions: List<CacheVersion>, cachesVersionManagers: List<CacheVersionManager>,
reporter: ICReporter, reporter: ICReporter,
private val usePreciseJavaTracking: Boolean, private val usePreciseJavaTracking: Boolean,
buildHistoryFile: File, buildHistoryFile: File,
localStateDirs: Collection<File>, localStateDirs: Collection<File>,
private val modulesApiHistory: ModulesApiHistory private val modulesApiHistory: ModulesApiHistory
) : IncrementalCompilerRunner<K2JVMCompilerArguments, IncrementalJvmCachesManager>( ) : IncrementalCompilerRunner<K2JVMCompilerArguments, IncrementalJvmCachesManager>(
workingDir, workingDir,
"caches-jvm", "caches-jvm",
cacheVersions, cachesVersionManagers,
reporter, reporter,
localStateDirs = localStateDirs, localStateDirs = localStateDirs,
buildHistoryFile = buildHistoryFile buildHistoryFile = buildHistoryFile
) { ) {
override fun isICEnabled(): Boolean = override fun isICEnabled(): Boolean =
@@ -16,21 +16,25 @@
package org.jetbrains.kotlin.incremental package org.jetbrains.kotlin.incremental
import org.jetbrains.kotlin.incremental.storage.version.CacheVersionManager
import org.jetbrains.kotlin.incremental.storage.version.localCacheVersionManager
import org.jetbrains.kotlin.incremental.storage.version.lookupsCacheVersionManager
import java.io.File import java.io.File
internal const val STANDALONE_CACHE_VERSION = 2 internal const val STANDALONE_CACHE_VERSION = 2
internal const val STANDALONE_VERSION_FILE_NAME = "standalone-ic-format-version.txt" internal const val STANDALONE_VERSION_FILE_NAME = "standalone-ic-format-version.txt"
fun standaloneCacheVersion(dataRoot: File, enabled: Boolean): CacheVersion = fun standaloneCacheVersionManager(dataRoot: File, enabled: Boolean): CacheVersionManager =
customCacheVersion(STANDALONE_CACHE_VERSION, STANDALONE_VERSION_FILE_NAME, dataRoot, enabled) customCacheVersionManager(STANDALONE_CACHE_VERSION, STANDALONE_VERSION_FILE_NAME, dataRoot, enabled)
fun customCacheVersion(version: Int, fileName: String, dataRoot: File, enabled: Boolean): CacheVersion = fun customCacheVersionManager(version: Int, fileName: String, dataRoot: File, enabled: Boolean): CacheVersionManager =
CacheVersion(ownVersion = version, CacheVersionManager(
versionFile = File(dataRoot, fileName), File(dataRoot, fileName),
whenVersionChanged = CacheVersion.Action.REBUILD_ALL_KOTLIN, if (enabled) version else null
whenTurnedOn = CacheVersion.Action.REBUILD_ALL_KOTLIN, )
whenTurnedOff = CacheVersion.Action.REBUILD_ALL_KOTLIN,
isEnabled = enabled)
fun commonCacheVersions(cachesDir: File, enabled: Boolean): List<CacheVersion> = fun commonCacheVersionsManagers(cachesDir: File, enabled: Boolean): List<CacheVersionManager> =
listOf(normalCacheVersion(cachesDir, enabled), dataContainerCacheVersion(cachesDir, enabled)) listOf(
localCacheVersionManager(cachesDir, enabled),
lookupsCacheVersionManager(cachesDir, enabled)
)
@@ -29,6 +29,10 @@ import com.intellij.util.xmlb.XmlSerializerUtil
) )
) )
class KotlinCompilerWorkspaceSettings : PersistentStateComponent<KotlinCompilerWorkspaceSettings> { class KotlinCompilerWorkspaceSettings : PersistentStateComponent<KotlinCompilerWorkspaceSettings> {
/**
* incrementalCompilationForJvmEnabled
* (name `preciseIncrementalEnabled` is kept for workspace file compatibility)
*/
var preciseIncrementalEnabled: Boolean = true var preciseIncrementalEnabled: Boolean = true
var incrementalCompilationForJsEnabled: Boolean = false var incrementalCompilationForJsEnabled: Boolean = false
var enableDaemon: Boolean = true var enableDaemon: Boolean = true
@@ -16,14 +16,13 @@
package org.jetbrains.kotlin.jps.build package org.jetbrains.kotlin.jps.build
import org.jetbrains.jps.incremental.ModuleBuildTarget
import org.jetbrains.kotlin.incremental.testingUtils.BuildLogFinder import org.jetbrains.kotlin.incremental.testingUtils.BuildLogFinder
import org.jetbrains.kotlin.jps.incremental.CacheVersionProvider import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
abstract class AbstractDataContainerVersionChangedTest : AbstractIncrementalCacheVersionChangedTest() { abstract class AbstractDataContainerVersionChangedTest : AbstractIncrementalCacheVersionChangedTest() {
override val buildLogFinder: BuildLogFinder override val buildLogFinder: BuildLogFinder
get() = BuildLogFinder(isDataContainerBuildLogEnabled = true) get() = BuildLogFinder(isDataContainerBuildLogEnabled = true)
override fun getVersions(cacheVersionProvider: CacheVersionProvider, targets: Iterable<ModuleBuildTarget>) = override fun getVersionManagersToTest(target: KotlinModuleBuildTarget<*>) =
listOf(cacheVersionProvider.dataContainerVersion()) listOf(kotlinCompileContext.lookupsCacheAttributesManager.versionManagerForTesting)
} }
@@ -16,15 +16,14 @@
package org.jetbrains.kotlin.jps.build package org.jetbrains.kotlin.jps.build
import org.jetbrains.jps.incremental.ModuleBuildTarget import org.jetbrains.kotlin.incremental.storage.version.CacheVersionManager
import org.jetbrains.kotlin.incremental.testingUtils.Modification import org.jetbrains.kotlin.incremental.testingUtils.Modification
import org.jetbrains.kotlin.incremental.testingUtils.ModifyContent import org.jetbrains.kotlin.incremental.testingUtils.ModifyContent
import org.jetbrains.kotlin.jps.incremental.CacheVersionProvider import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
abstract class AbstractIncrementalCacheVersionChangedTest : AbstractIncrementalJpsTest(allowNoFilesWithSuffixInTestData = true) { abstract class AbstractIncrementalCacheVersionChangedTest : AbstractIncrementalJpsTest(allowNoFilesWithSuffixInTestData = true) {
override fun performAdditionalModifications(modifications: List<Modification>) { override fun performAdditionalModifications(modifications: List<Modification>) {
val modifiedFiles = modifications.filterIsInstance<ModifyContent>().map { it.path } val modifiedFiles = modifications.filterIsInstance<ModifyContent>().map { it.path }
val paths = projectDescriptor.dataManager.dataPaths
val targets = projectDescriptor.allModuleTargets val targets = projectDescriptor.allModuleTargets
val hasKotlin = HasKotlinMarker(projectDescriptor.dataManager) val hasKotlin = HasKotlinMarker(projectDescriptor.dataManager)
@@ -33,13 +32,18 @@ abstract class AbstractIncrementalCacheVersionChangedTest : AbstractIncrementalJ
} }
if (modifiedFiles.none { it.endsWith("do-not-change-cache-versions") }) { if (modifiedFiles.none { it.endsWith("do-not-change-cache-versions") }) {
val cacheVersionProvider = CacheVersionProvider(paths, isIncrementalCompilationEnabled = true) val versions = targets.flatMap {
val versions = getVersions(cacheVersionProvider, targets) getVersionManagersToTest(kotlinCompileContext.targetsBinding[it]!!)
val versionFiles = versions.map { it.formatVersionFile }.filter { it.exists() } }
versionFiles.forEach { it.writeText("777") }
versions.forEach {
if (it.versionFileForTesting.exists()) {
it.versionFileForTesting.writeText("777")
}
}
} }
} }
protected open fun getVersions(cacheVersionProvider: CacheVersionProvider, targets: Iterable<ModuleBuildTarget>) = protected open fun getVersionManagersToTest(target: KotlinModuleBuildTarget<*>): List<CacheVersionManager> =
targets.map { cacheVersionProvider.normalVersion(it) } listOf(target.localCacheVersionManager)
} }
@@ -44,13 +44,15 @@ import org.jetbrains.jps.model.java.JpsJavaDependencyScope
import org.jetbrains.jps.model.java.JpsJavaExtensionService import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.util.JpsPathUtil import org.jetbrains.jps.util.JpsPathUtil
import org.jetbrains.kotlin.config.IncrementalCompilation import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.incremental.CacheVersion
import org.jetbrains.kotlin.incremental.LookupSymbol import org.jetbrains.kotlin.incremental.LookupSymbol
import org.jetbrains.kotlin.incremental.isJavaFile import org.jetbrains.kotlin.incremental.isJavaFile
import org.jetbrains.kotlin.incremental.storage.version.CacheAttributesDiff
import org.jetbrains.kotlin.incremental.storage.version.CacheVersionManager
import org.jetbrains.kotlin.incremental.testingUtils.* import org.jetbrains.kotlin.incremental.testingUtils.*
import org.jetbrains.kotlin.jps.incremental.CompositeLookupsCacheAttributesManager
import org.jetbrains.kotlin.jps.incremental.getKotlinCache import org.jetbrains.kotlin.jps.incremental.getKotlinCache
import org.jetbrains.kotlin.jps.incremental.withLookupStorage import org.jetbrains.kotlin.jps.incremental.withLookupStorage
import org.jetbrains.kotlin.jps.platforms.kotlinBuildTargets import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.utils.Printer import org.jetbrains.kotlin.utils.Printer
import org.jetbrains.kotlin.utils.keysToMap import org.jetbrains.kotlin.utils.keysToMap
@@ -82,6 +84,8 @@ abstract class AbstractIncrementalJpsTest(
protected var mapWorkingToOriginalFile: MutableMap<File, File> = hashMapOf() protected var mapWorkingToOriginalFile: MutableMap<File, File> = hashMapOf()
lateinit var kotlinCompileContext: KotlinCompileContext
protected open val buildLogFinder: BuildLogFinder protected open val buildLogFinder: BuildLogFinder
get() = BuildLogFinder() get() = BuildLogFinder()
@@ -145,19 +149,21 @@ abstract class AbstractIncrementalJpsTest(
projectDescriptor = createProjectDescriptor(BuildLoggingManager(logger)) projectDescriptor = createProjectDescriptor(BuildLoggingManager(logger))
val lookupTracker = TestLookupTracker() val lookupTracker = TestLookupTracker()
projectDescriptor.project.setTestingContext(TestingContext(lookupTracker, logger)) val testingContext = TestingContext(lookupTracker, logger)
projectDescriptor.project.setTestingContext(testingContext)
try { try {
val builder = IncProjectBuilder(projectDescriptor, BuilderRegistry.getInstance(), myBuildParams, CanceledStatus.NULL, mockConstantSearch, true) val builder = IncProjectBuilder(projectDescriptor, BuilderRegistry.getInstance(), myBuildParams, CanceledStatus.NULL, mockConstantSearch, true)
val buildResult = BuildResult() val buildResult = BuildResult()
builder.addMessageHandler(buildResult) builder.addMessageHandler(buildResult)
val finalScope = scope.build() val finalScope = scope.build()
builder.build(finalScope, false) builder.build(finalScope, false)
lookupTracker.lookups.mapTo(lookupsDuringTest) { LookupSymbol(it.name, it.scopeFqName) } // testingContext.kotlinCompileContext is initialized in KotlinBuilder.initializeKotlinContext
kotlinCompileContext = testingContext.kotlinCompileContext!!
// for getting kotlin platform only lookupTracker.lookups.mapTo(lookupsDuringTest) { LookupSymbol(it.name, it.scopeFqName) }
val dummyCompileContext = CompileContextImpl.createContextForTests(finalScope, projectDescriptor)
if (!buildResult.isSuccessful) { if (!buildResult.isSuccessful) {
val errorMessages = val errorMessages =
@@ -169,7 +175,7 @@ abstract class AbstractIncrementalJpsTest(
return MakeResult(logger.log + "$COMPILATION_FAILED\n" + errorMessages + "\n", true, null) return MakeResult(logger.log + "$COMPILATION_FAILED\n" + errorMessages + "\n", true, null)
} }
else { else {
return MakeResult(logger.log, false, createMappingsDump(projectDescriptor, dummyCompileContext)) return MakeResult(logger.log, false, createMappingsDump(projectDescriptor))
} }
} }
finally { finally {
@@ -287,20 +293,18 @@ abstract class AbstractIncrementalJpsTest(
} }
private fun createMappingsDump( private fun createMappingsDump(
project: ProjectDescriptor, project: ProjectDescriptor
dummyCompileContext: CompileContext ) = createKotlinIncrementalCacheDump(project) + "\n\n\n" +
) = createKotlinIncrementalCacheDump(project, dummyCompileContext) + "\n\n\n" +
createLookupCacheDump(project) + "\n\n\n" + createLookupCacheDump(project) + "\n\n\n" +
createCommonMappingsDump(project) + "\n\n\n" + createCommonMappingsDump(project) + "\n\n\n" +
createJavaMappingsDump(project) createJavaMappingsDump(project)
private fun createKotlinIncrementalCacheDump( private fun createKotlinIncrementalCacheDump(
project: ProjectDescriptor, project: ProjectDescriptor
dummyCompileContext: CompileContext
): String { ): String {
return buildString { return buildString {
for (target in project.allModuleTargets.sortedBy { it.presentableName }) { for (target in project.allModuleTargets.sortedBy { it.presentableName }) {
val kotlinCache = project.dataManager.getKotlinCache(dummyCompileContext.kotlinBuildTargets[target]) val kotlinCache = project.dataManager.getKotlinCache(kotlinCompileContext.targetsBinding[target])
if (kotlinCache != null) { if (kotlinCache != null) {
append("<target $target>\n") append("<target $target>\n")
append(kotlinCache.dump()) append(kotlinCache.dump())
@@ -443,14 +447,23 @@ abstract class AbstractIncrementalJpsTest(
override fun doGetProjectDir(): File? = workDir override fun doGetProjectDir(): File? = workDir
private class MyLogger(val rootPath: String) : ProjectBuilderLoggerBase(), BuildLogger { private class MyLogger(val rootPath: String) : ProjectBuilderLoggerBase(), TestingBuildLogger {
private val markedDirtyBeforeRound = ArrayList<File>() private val markedDirtyBeforeRound = ArrayList<File>()
private val markedDirtyAfterRound = ArrayList<File>() private val markedDirtyAfterRound = ArrayList<File>()
override fun actionsOnCacheVersionChanged(actions: List<CacheVersion.Action>) { override fun invalidOrUnusedCache(
if (actions.size > 1 && actions.any { it != CacheVersion.Action.DO_NOTHING }) { chunk: KotlinChunk?,
logLine("Actions after cache changed: $actions") target: KotlinModuleBuildTarget<*>?,
attributesDiff: CacheAttributesDiff<*>
) {
val cacheManager = attributesDiff.manager
val cacheTitle = when (cacheManager) {
is CacheVersionManager -> "Local cache for ${chunk ?: target}"
is CompositeLookupsCacheAttributesManager -> "Lookups cache"
else -> error("Unknown cache manager $cacheManager")
} }
logLine("$cacheTitle are $attributesDiff")
} }
override fun markedAsDirtyBeforeRound(files: Iterable<File>) { override fun markedAsDirtyBeforeRound(files: Iterable<File>) {
@@ -461,13 +474,15 @@ abstract class AbstractIncrementalJpsTest(
markedDirtyAfterRound.addAll(files) markedDirtyAfterRound.addAll(files)
} }
override fun buildStarted(context: CompileContext, chunk: ModuleChunk) { override fun chunkBuildStarted(context: CompileContext, chunk: ModuleChunk) {
logDirtyFiles(markedDirtyBeforeRound) // files can be marked as dirty during build start (KotlinCompileContext initialization)
if (!chunk.isDummy(context) && context.projectDescriptor.project.modules.size > 1) { if (!chunk.isDummy(context) && context.projectDescriptor.project.modules.size > 1) {
logLine("Building ${chunk.modules.sortedBy { it.name }.joinToString { it.name }}") logLine("Building ${chunk.modules.sortedBy { it.name }.joinToString { it.name }}")
} }
} }
override fun afterBuildStarted(context: CompileContext, chunk: ModuleChunk) { override fun afterChunkBuildStarted(context: CompileContext, chunk: ModuleChunk) {
logDirtyFiles(markedDirtyBeforeRound) logDirtyFiles(markedDirtyBeforeRound)
} }
@@ -44,13 +44,14 @@ import org.jetbrains.jps.model.java.JpsJavaDependencyScope
import org.jetbrains.jps.model.java.JpsJavaExtensionService import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.util.JpsPathUtil import org.jetbrains.jps.util.JpsPathUtil
import org.jetbrains.kotlin.config.IncrementalCompilation import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.incremental.CacheVersion
import org.jetbrains.kotlin.incremental.LookupSymbol import org.jetbrains.kotlin.incremental.LookupSymbol
import org.jetbrains.kotlin.incremental.isJavaFile import org.jetbrains.kotlin.incremental.storage.version.CacheAttributesDiff
import org.jetbrains.kotlin.incremental.storage.version.CacheVersionManager
import org.jetbrains.kotlin.incremental.testingUtils.* import org.jetbrains.kotlin.incremental.testingUtils.*
import org.jetbrains.kotlin.jps.incremental.CompositeLookupsCacheAttributesManager
import org.jetbrains.kotlin.jps.incremental.getKotlinCache import org.jetbrains.kotlin.jps.incremental.getKotlinCache
import org.jetbrains.kotlin.jps.incremental.withLookupStorage import org.jetbrains.kotlin.jps.incremental.withLookupStorage
import org.jetbrains.kotlin.jps.platforms.kotlinBuildTargets import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.utils.Printer import org.jetbrains.kotlin.utils.Printer
import org.jetbrains.kotlin.utils.keysToMap import org.jetbrains.kotlin.utils.keysToMap
@@ -82,6 +83,8 @@ abstract class AbstractIncrementalJpsTest(
protected var mapWorkingToOriginalFile: MutableMap<File, File> = hashMapOf() protected var mapWorkingToOriginalFile: MutableMap<File, File> = hashMapOf()
lateinit var kotlinCompileContext: KotlinCompileContext
protected open val buildLogFinder: BuildLogFinder protected open val buildLogFinder: BuildLogFinder
get() = BuildLogFinder() get() = BuildLogFinder()
@@ -144,19 +147,20 @@ abstract class AbstractIncrementalJpsTest(
projectDescriptor = createProjectDescriptor(BuildLoggingManager(logger)) projectDescriptor = createProjectDescriptor(BuildLoggingManager(logger))
val lookupTracker = TestLookupTracker() val lookupTracker = TestLookupTracker()
projectDescriptor.project.setTestingContext(TestingContext(lookupTracker, logger)) val testingContext = TestingContext(lookupTracker, logger)
projectDescriptor.project.setTestingContext(testingContext)
try { try {
val builder = IncProjectBuilder(projectDescriptor, BuilderRegistry.getInstance(), myBuildParams, CanceledStatus.NULL, mockConstantSearch, true) val builder = IncProjectBuilder(projectDescriptor, BuilderRegistry.getInstance(), myBuildParams, CanceledStatus.NULL, mockConstantSearch, true)
val buildResult = BuildResult() val buildResult = BuildResult()
builder.addMessageHandler(buildResult) builder.addMessageHandler(buildResult)
val finalScope = scope.build() val finalScope = scope.build()
builder.build(finalScope, false) builder.build(finalScope, false)
lookupTracker.lookups.mapTo(lookupsDuringTest) { LookupSymbol(it.name, it.scopeFqName) } // testingContext.kotlinCompileContext is initialized in KotlinBuilder.initializeKotlinContext
kotlinCompileContext = testingContext.kotlinCompileContext!!
// for getting kotlin platform only
val dummyCompileContext = CompileContextImpl.createContextForTests(finalScope, projectDescriptor)
if (!buildResult.isSuccessful) { if (!buildResult.isSuccessful) {
val errorMessages = val errorMessages =
@@ -168,7 +172,7 @@ abstract class AbstractIncrementalJpsTest(
return MakeResult(logger.log + "$COMPILATION_FAILED\n" + errorMessages + "\n", true, null) return MakeResult(logger.log + "$COMPILATION_FAILED\n" + errorMessages + "\n", true, null)
} }
else { else {
return MakeResult(logger.log, false, createMappingsDump(projectDescriptor, dummyCompileContext)) return MakeResult(logger.log, false, createMappingsDump(projectDescriptor))
} }
} }
finally { finally {
@@ -286,20 +290,18 @@ abstract class AbstractIncrementalJpsTest(
} }
private fun createMappingsDump( private fun createMappingsDump(
project: ProjectDescriptor, project: ProjectDescriptor
dummyCompileContext: CompileContext ) = createKotlinIncrementalCacheDump(project) + "\n\n\n" +
) = createKotlinIncrementalCacheDump(project, dummyCompileContext) + "\n\n\n" +
createLookupCacheDump(project) + "\n\n\n" + createLookupCacheDump(project) + "\n\n\n" +
createCommonMappingsDump(project) + "\n\n\n" + createCommonMappingsDump(project) + "\n\n\n" +
createJavaMappingsDump(project) createJavaMappingsDump(project)
private fun createKotlinIncrementalCacheDump( private fun createKotlinIncrementalCacheDump(
project: ProjectDescriptor, project: ProjectDescriptor
dummyCompileContext: CompileContext
): String { ): String {
return buildString { return buildString {
for (target in project.allModuleTargets.sortedBy { it.presentableName }) { for (target in project.allModuleTargets.sortedBy { it.presentableName }) {
val kotlinCache = project.dataManager.getKotlinCache(dummyCompileContext.kotlinBuildTargets[target]) val kotlinCache = project.dataManager.getKotlinCache(kotlinCompileContext.targetsBinding[target])
if (kotlinCache != null) { if (kotlinCache != null) {
append("<target $target>\n") append("<target $target>\n")
append(kotlinCache.dump()) append(kotlinCache.dump())
@@ -442,14 +444,23 @@ abstract class AbstractIncrementalJpsTest(
override fun doGetProjectDir(): File? = workDir override fun doGetProjectDir(): File? = workDir
private class MyLogger(val rootPath: String) : ProjectBuilderLoggerBase(), BuildLogger { private class MyLogger(val rootPath: String) : ProjectBuilderLoggerBase(), TestingBuildLogger {
private val markedDirtyBeforeRound = ArrayList<File>() private val markedDirtyBeforeRound = ArrayList<File>()
private val markedDirtyAfterRound = ArrayList<File>() private val markedDirtyAfterRound = ArrayList<File>()
override fun actionsOnCacheVersionChanged(actions: List<CacheVersion.Action>) { override fun invalidOrUnusedCache(
if (actions.size > 1 && actions.any { it != CacheVersion.Action.DO_NOTHING }) { chunk: KotlinChunk?,
logLine("Actions after cache changed: $actions") target: KotlinModuleBuildTarget<*>?,
attributesDiff: CacheAttributesDiff<*>
) {
val cacheManager = attributesDiff.manager
val cacheTitle = when (cacheManager) {
is CacheVersionManager -> "Local cache for ${chunk ?: target}"
is CompositeLookupsCacheAttributesManager -> "Lookups cache"
else -> error("Unknown cache manager $cacheManager")
} }
logLine("$cacheTitle are $attributesDiff")
} }
override fun markedAsDirtyBeforeRound(files: Iterable<File>) { override fun markedAsDirtyBeforeRound(files: Iterable<File>) {
@@ -460,13 +471,15 @@ abstract class AbstractIncrementalJpsTest(
markedDirtyAfterRound.addAll(files) markedDirtyAfterRound.addAll(files)
} }
override fun buildStarted(context: CompileContext, chunk: ModuleChunk) { override fun chunkBuildStarted(context: CompileContext, chunk: ModuleChunk) {
logDirtyFiles(markedDirtyBeforeRound) // files can be marked as dirty during build start (KotlinCompileContext initialization)
if (!chunk.isDummy(context) && context.projectDescriptor.project.modules.size > 1) { if (!chunk.isDummy(context) && context.projectDescriptor.project.modules.size > 1) {
logLine("Building ${chunk.modules.sortedBy { it.name }.joinToString { it.name }}") logLine("Building ${chunk.modules.sortedBy { it.name }.joinToString { it.name }}")
} }
} }
override fun afterBuildStarted(context: CompileContext, chunk: ModuleChunk) { override fun afterChunkBuildStarted(context: CompileContext, chunk: ModuleChunk) {
logDirtyFiles(markedDirtyBeforeRound) logDirtyFiles(markedDirtyBeforeRound)
} }
@@ -18,17 +18,14 @@ package org.jetbrains.kotlin.jps.build
import com.intellij.testFramework.UsefulTestCase import com.intellij.testFramework.UsefulTestCase
import org.jetbrains.jps.builders.BuildTarget import org.jetbrains.jps.builders.BuildTarget
import org.jetbrains.jps.builders.CompileScopeTestBuilder
import org.jetbrains.jps.builders.storage.BuildDataPaths import org.jetbrains.jps.builders.storage.BuildDataPaths
import org.jetbrains.jps.incremental.CompileContextImpl
import org.jetbrains.kotlin.config.IncrementalCompilation import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.incremental.KOTLIN_CACHE_DIRECTORY_NAME import org.jetbrains.kotlin.incremental.KOTLIN_CACHE_DIRECTORY_NAME
import org.jetbrains.kotlin.incremental.storage.BasicMapsOwner import org.jetbrains.kotlin.incremental.storage.BasicMapsOwner
import org.jetbrains.kotlin.incremental.testingUtils.Modification import org.jetbrains.kotlin.incremental.testingUtils.Modification
import org.jetbrains.kotlin.incremental.testingUtils.ModifyContent import org.jetbrains.kotlin.incremental.testingUtils.ModifyContent
import org.jetbrains.kotlin.jps.incremental.CacheVersionProvider
import org.jetbrains.kotlin.jps.incremental.KotlinDataContainerTarget import org.jetbrains.kotlin.jps.incremental.KotlinDataContainerTarget
import org.jetbrains.kotlin.jps.platforms.kotlinBuildTargets import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
import org.jetbrains.kotlin.utils.Printer import org.jetbrains.kotlin.utils.Printer
import java.io.File import java.io.File
@@ -85,44 +82,55 @@ abstract class AbstractIncrementalLazyCachesTest : AbstractIncrementalJpsTest()
private fun dumpKotlinCachesFileNames(): String { private fun dumpKotlinCachesFileNames(): String {
val sb = StringBuilder() val sb = StringBuilder()
val p = Printer(sb) val printer = Printer(sb)
val targets = projectDescriptor.allModuleTargets val chunks = kotlinCompileContext.targetsIndex.chunks
val dataManager = projectDescriptor.dataManager val dataManager = projectDescriptor.dataManager
val paths = dataManager.dataPaths val paths = dataManager.dataPaths
val versions = CacheVersionProvider(paths, isIncrementalCompilationEnabled = true)
dumpCachesForTarget(p, paths, KotlinDataContainerTarget, versions.dataContainerVersion().formatVersionFile) dumpCachesForTarget(
printer,
paths,
KotlinDataContainerTarget,
kotlinCompileContext.lookupsCacheAttributesManager.versionManagerForTesting.versionFileForTesting
)
// for getting kotlin platform only data class TargetInChunk(val chunk: KotlinChunk, val target: KotlinModuleBuildTarget<*>)
val dummyCompileContext = CompileContextImpl.createContextForTests(CompileScopeTestBuilder.make().build(), projectDescriptor)
for (target in targets.sortedBy { it.presentableName }) { val allTargets = chunks.flatMap { chunk ->
val kotlinModuleBuildTarget = dummyCompileContext.kotlinBuildTargets[target]!! chunk.targets.map { target ->
val metaBuildInfo = kotlinModuleBuildTarget.buildMetaInfoFile(target, dataManager) TargetInChunk(chunk, target)
dumpCachesForTarget(p, paths, target, }
versions.normalVersion(target).formatVersionFile, }.sortedBy { it.target.jpsModuleBuildTarget.presentableName }
metaBuildInfo,
subdirectory = KOTLIN_CACHE_DIRECTORY_NAME) allTargets.forEach { (chunk, target) ->
val metaBuildInfo = chunk.buildMetaInfoFile(target.jpsModuleBuildTarget)
dumpCachesForTarget(
printer, paths, target.jpsModuleBuildTarget,
target.localCacheVersionManager.versionFileForTesting,
metaBuildInfo,
subdirectory = KOTLIN_CACHE_DIRECTORY_NAME
)
} }
return sb.toString() return sb.toString()
} }
private fun dumpCachesForTarget( private fun dumpCachesForTarget(
p: Printer, p: Printer,
paths: BuildDataPaths, paths: BuildDataPaths,
target: BuildTarget<*>, target: BuildTarget<*>,
vararg cacheVersionsFiles: File, vararg cacheVersionsFiles: File,
subdirectory: String? = null subdirectory: String? = null
) { ) {
p.println(target) p.println(target)
p.pushIndent() p.pushIndent()
val dataRoot = paths.getTargetDataRoot(target).let { if (subdirectory != null) File(it, subdirectory) else it } val dataRoot = paths.getTargetDataRoot(target).let { if (subdirectory != null) File(it, subdirectory) else it }
cacheVersionsFiles cacheVersionsFiles
.filter(File::exists) .filter(File::exists)
.sortedBy { it.name } .sortedBy { it.name }
.forEach { p.println(it.name) } .forEach { p.println(it.name) }
kotlinCacheNames(dataRoot).sorted().forEach { p.println(it) } kotlinCacheNames(dataRoot).sorted().forEach { p.println(it) }
@@ -58,13 +58,13 @@ import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.JvmCodegenUtil import org.jetbrains.kotlin.codegen.JvmCodegenUtil
import org.jetbrains.kotlin.config.IncrementalCompilation import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.config.KotlinCompilerVersion.TEST_IS_PRE_RELEASE_SYSTEM_PROPERTY import org.jetbrains.kotlin.config.KotlinCompilerVersion.TEST_IS_PRE_RELEASE_SYSTEM_PROPERTY
import org.jetbrains.kotlin.incremental.CacheVersion
import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.storage.version.CacheAttributesDiff
import org.jetbrains.kotlin.incremental.withIC import org.jetbrains.kotlin.incremental.withIC
import org.jetbrains.kotlin.jps.build.KotlinJpsBuildTest.LibraryDependency.* import org.jetbrains.kotlin.jps.build.KotlinJpsBuildTest.LibraryDependency.*
import org.jetbrains.kotlin.jps.model.kotlinCommonCompilerArguments import org.jetbrains.kotlin.jps.model.kotlinCommonCompilerArguments
import org.jetbrains.kotlin.jps.model.kotlinCompilerArguments import org.jetbrains.kotlin.jps.model.kotlinCompilerArguments
import org.jetbrains.kotlin.jps.platforms.productionBuildTarget import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
@@ -970,6 +970,9 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() {
} }
} }
// c -> b -exported-> a
// c2 -> b2 ------------^
val a = addModuleWithSourceAndTestRoot("a") val a = addModuleWithSourceAndTestRoot("a")
val b = addModuleWithSourceAndTestRoot("b") val b = addModuleWithSourceAndTestRoot("b")
val c = addModuleWithSourceAndTestRoot("c") val c = addModuleWithSourceAndTestRoot("c")
@@ -983,15 +986,21 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() {
val actual = StringBuilder() val actual = StringBuilder()
buildCustom(CanceledStatus.NULL, TestProjectBuilderLogger(), BuildResult()) { buildCustom(CanceledStatus.NULL, TestProjectBuilderLogger(), BuildResult()) {
project.setTestingContext(TestingContext(LookupTracker.DO_NOTHING, object : BuildLogger { project.setTestingContext(TestingContext(LookupTracker.DO_NOTHING, object : TestingBuildLogger {
override fun buildStarted(context: CompileContext, chunk: ModuleChunk) { override fun chunkBuildStarted(context: CompileContext, chunk: ModuleChunk) {
actual.append("Targets dependent on ${chunk.targets.joinToString() }:\n") actual.append("Targets dependent on ${chunk.targets.joinToString()}:\n")
actual.append(getDependentTargets(chunk.targets, context).map { it.toString() }.sorted().joinToString("\n")) val dependentRecursively = mutableSetOf<KotlinChunk>()
context.kotlin.getChunk(chunk)!!.collectDependentChunksRecursivelyExportedOnly(dependentRecursively)
dependentRecursively.asSequence().map { it.targets.joinToString() }.sorted().joinTo(actual, "\n")
actual.append("\n---------\n") actual.append("\n---------\n")
} }
override fun afterBuildStarted(context: CompileContext, chunk: ModuleChunk) {} override fun afterChunkBuildStarted(context: CompileContext, chunk: ModuleChunk) {}
override fun actionsOnCacheVersionChanged(actions: List<CacheVersion.Action>) {} override fun invalidOrUnusedCache(
chunk: KotlinChunk?,
target: KotlinModuleBuildTarget<*>?,
attributesDiff: CacheAttributesDiff<*>
) {}
override fun buildFinished(exitCode: ModuleLevelBuilder.ExitCode) {} override fun buildFinished(exitCode: ModuleLevelBuilder.ExitCode) {}
override fun markedAsDirtyBeforeRound(files: Iterable<File>) {} override fun markedAsDirtyBeforeRound(files: Iterable<File>) {}
override fun markedAsDirtyAfterRound(files: Iterable<File>) {} override fun markedAsDirtyAfterRound(files: Iterable<File>) {}
@@ -0,0 +1,82 @@
/*
* 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.jps.incremental
import org.jetbrains.kotlin.incremental.storage.version.CacheStatus
import org.jetbrains.kotlin.incremental.storage.version.loadDiff
import org.junit.Test
import java.io.File
import kotlin.test.assertEquals
class CompositeLookupsCacheAttributesManagerTest {
val manager = CompositeLookupsCacheAttributesManager(File("not-used"), setOf())
@Test
fun testNothingToJava() {
assertEquals(
CacheStatus.INVALID,
manager.loadDiff(
actual = null,
expected = CompositeLookupsCacheAttributes(1, setOf("jvm"))
).status
)
}
@Test
fun testNothingToJavaAndJs() {
assertEquals(
CacheStatus.INVALID,
manager.loadDiff(
actual = null,
expected = CompositeLookupsCacheAttributes(1, setOf("jvm", "js"))
).status
)
}
@Test
fun testJsToJava() {
assertEquals(
CacheStatus.INVALID,
manager.loadDiff(
actual = CompositeLookupsCacheAttributes(1, setOf("jvm")),
expected = CompositeLookupsCacheAttributes(1, setOf("js"))
).status
)
}
@Test
fun testJsAndJavaToJava() {
assertEquals(
CacheStatus.VALID,
manager.loadDiff(
actual = CompositeLookupsCacheAttributes(1, setOf("jvm", "js")),
expected = CompositeLookupsCacheAttributes(1, setOf("jvm"))
).status
)
}
@Test
fun testJsAndJavaToJavaWithOtherVersion() {
assertEquals(
CacheStatus.INVALID,
manager.loadDiff(
actual = CompositeLookupsCacheAttributes(1, setOf("jvm", "js")),
expected = CompositeLookupsCacheAttributes(2, setOf("jvm"))
).status
)
}
@Test
fun testJavaToJsAndJava() {
assertEquals(
CacheStatus.INVALID,
manager.loadDiff(
actual = CompositeLookupsCacheAttributes(1, setOf("jvm")),
expected = CompositeLookupsCacheAttributes(1, setOf("jvm", "js"))
).status
)
}
}
@@ -89,7 +89,9 @@ class FSOperationsHelper(
} }
fun markInChunkOrDependents(files: Iterable<File>, excludeFiles: Set<File>) { fun markInChunkOrDependents(files: Iterable<File>, excludeFiles: Set<File>) {
markFilesImpl(files, currentRound = false) { it !in excludeFiles && it.exists() && moduleBasedFilter.accept(it) } markFilesImpl(files, currentRound = false) {
it !in excludeFiles && it.exists() && moduleBasedFilter.accept(it)
}
} }
private inline fun markFilesImpl( private inline fun markFilesImpl(
@@ -17,9 +17,7 @@
package org.jetbrains.kotlin.jps.build package org.jetbrains.kotlin.jps.build
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.jps.ModuleChunk import org.jetbrains.jps.ModuleChunk
import org.jetbrains.jps.builders.BuildTarget
import org.jetbrains.jps.builders.DirtyFilesHolder import org.jetbrains.jps.builders.DirtyFilesHolder
import org.jetbrains.jps.builders.FileProcessor import org.jetbrains.jps.builders.FileProcessor
import org.jetbrains.jps.builders.impl.DirtyFilesHolderBase import org.jetbrains.jps.builders.impl.DirtyFilesHolderBase
@@ -30,9 +28,6 @@ import org.jetbrains.jps.incremental.ModuleLevelBuilder.ExitCode.*
import org.jetbrains.jps.incremental.java.JavaBuilder import org.jetbrains.jps.incremental.java.JavaBuilder
import org.jetbrains.jps.incremental.storage.BuildDataManager import org.jetbrains.jps.incremental.storage.BuildDataManager
import org.jetbrains.jps.model.JpsProject import org.jetbrains.jps.model.JpsProject
import org.jetbrains.jps.model.java.JpsJavaClasspathKind
import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.model.module.JpsModule
import org.jetbrains.kotlin.build.GeneratedFile import org.jetbrains.kotlin.build.GeneratedFile
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
@@ -45,20 +40,20 @@ import org.jetbrains.kotlin.daemon.common.isDaemonEnabled
import org.jetbrains.kotlin.incremental.* import org.jetbrains.kotlin.incremental.*
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.jps.incremental.* import org.jetbrains.kotlin.jps.incremental.JpsIncrementalCache
import org.jetbrains.kotlin.jps.incremental.withLookupStorage
import org.jetbrains.kotlin.jps.model.kotlinKind import org.jetbrains.kotlin.jps.model.kotlinKind
import org.jetbrains.kotlin.jps.platforms.KotlinJvmModuleBuildTarget import org.jetbrains.kotlin.jps.targets.KotlinJvmModuleBuildTarget
import org.jetbrains.kotlin.jps.platforms.KotlinModuleBuildTarget import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
import org.jetbrains.kotlin.jps.platforms.kotlinBuildTargets
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.preloading.ClassCondition import org.jetbrains.kotlin.preloading.ClassCondition
import org.jetbrains.kotlin.utils.KotlinPaths import org.jetbrains.kotlin.utils.KotlinPaths
import org.jetbrains.kotlin.utils.KotlinPathsFromHomeDir import org.jetbrains.kotlin.utils.KotlinPathsFromHomeDir
import org.jetbrains.kotlin.utils.PathUtil import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.kotlin.utils.keysToMapExceptNulls
import java.io.File import java.io.File
import java.util.* import java.util.*
import kotlin.collections.HashSet import kotlin.collections.HashSet
import kotlin.system.measureTimeMillis
class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) { class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
companion object { companion object {
@@ -89,12 +84,16 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
override fun getCompilableFileExtensions() = arrayListOf("kt", "kts") override fun getCompilableFileExtensions() = arrayListOf("kt", "kts")
override fun buildStarted(context: CompileContext) { override fun buildStarted(context: CompileContext) {
logSettings(context)
}
private fun logSettings(context: CompileContext) {
LOG.debug("==========================================") LOG.debug("==========================================")
LOG.info("is Kotlin incremental compilation enabled for JVM: ${IncrementalCompilation.isEnabledForJvm()}") LOG.info("is Kotlin incremental compilation enabled for JVM: ${IncrementalCompilation.isEnabledForJvm()}")
LOG.info("is Kotlin incremental compilation enabled for JS: ${IncrementalCompilation.isEnabledForJs()}") LOG.info("is Kotlin incremental compilation enabled for JS: ${IncrementalCompilation.isEnabledForJs()}")
if (IncrementalCompilation.isEnabledForJs()) { if (IncrementalCompilation.isEnabledForJs()) {
val messageCollector = MessageCollectorAdapter(context, null) val messageCollector = MessageCollectorAdapter(context, null)
messageCollector.report(CompilerMessageSeverity.INFO, "Using experimental incremental compilation for Kotlin/JS") messageCollector.report(INFO, "Using experimental incremental compilation for Kotlin/JS")
} }
LOG.info("is Kotlin compiler daemon enabled: ${isDaemonEnabled()}") LOG.info("is Kotlin compiler daemon enabled: ${isDaemonEnabled()}")
@@ -105,8 +104,66 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
} }
} }
override fun buildFinished(context: CompileContext?) { /**
statisticsLogger.reportTotal() * Ensure Kotlin Context initialized.
* Kotlin Context should be initialized only when required (before first kotlin chunk build).
*/
private fun ensureKotlinContextInitialized(context: CompileContext): KotlinCompileContext {
val kotlinCompileContext = context.getUserData(kotlinCompileContextKey)
if (kotlinCompileContext != null) return kotlinCompileContext
// don't synchronize on context, since it is chunk local only
synchronized(kotlinCompileContextKey) {
val actualKotlinCompileContext = context.getUserData(kotlinCompileContextKey)
if (actualKotlinCompileContext != null) return actualKotlinCompileContext
try {
return initializeKotlinContext(context)
} catch (t: Throwable) {
jpsReportInternalBuilderError(context, Error("Cannot initialize Kotlin context: ${t.message}", t))
throw t
}
}
}
private fun initializeKotlinContext(context: CompileContext): KotlinCompileContext {
lateinit var kotlinContext: KotlinCompileContext
val time = measureTimeMillis {
kotlinContext = KotlinCompileContext(context)
context.putUserData(kotlinCompileContextKey, kotlinContext)
context.testingContext?.kotlinCompileContext = kotlinContext
if (kotlinContext.shouldCheckCacheVersions && kotlinContext.hasKotlin()) {
kotlinContext.checkCacheVersions()
}
kotlinContext.cleanupCaches()
}
LOG.info("Total Kotlin global compile context initialization time: $time ms")
return kotlinContext
}
override fun buildFinished(context: CompileContext) {
ensureKotlinContextDisposed(context)
}
private fun ensureKotlinContextDisposed(context: CompileContext) {
if (context.getUserData(kotlinCompileContextKey) != null) {
// don't synchronize on context, since it chunk local only
synchronized(kotlinCompileContextKey) {
val kotlinCompileContext = context.getUserData(kotlinCompileContextKey)
if (kotlinCompileContext != null) {
kotlinCompileContext.dispose()
context.putUserData(kotlinCompileContextKey, null)
statisticsLogger.reportTotal()
}
}
}
} }
override fun chunkBuildStarted(context: CompileContext, chunk: ModuleChunk) { override fun chunkBuildStarted(context: CompileContext, chunk: ModuleChunk) {
@@ -114,18 +171,40 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
if (chunk.isDummy(context)) return if (chunk.isDummy(context)) return
val kotlinContext = ensureKotlinContextInitialized(context)
val buildLogger = context.testingContext?.buildLogger val buildLogger = context.testingContext?.buildLogger
buildLogger?.buildStarted(context, chunk) buildLogger?.chunkBuildStarted(context, chunk)
if (JavaBuilderUtil.isForcedRecompilationAllJavaModules(context)) return if (JavaBuilderUtil.isForcedRecompilationAllJavaModules(context)) return
val targets = chunk.targets val targets = chunk.targets
val dataManager = context.projectDescriptor.dataManager if (targets.none { kotlinContext.hasKotlinMarker[it] == true }) return
val hasKotlin = HasKotlinMarker(dataManager)
if (targets.none { hasKotlin[it] == true }) return val kotlinChunk = kotlinContext.getChunk(chunk) ?: return
kotlinContext.checkChunkCacheVersion(kotlinChunk)
val roundDirtyFiles = KotlinDirtySourceFilesHolder( if (!kotlinContext.rebuildingAllKotlin) {
markAdditionalFilesForInitialRound(kotlinChunk, chunk, kotlinContext)
}
buildLogger?.afterChunkBuildStarted(context, chunk)
}
/**
* Invalidate usages of removed classes.
* See KT-13677 for more details.
*
* todo(1.2.80): move to KotlinChunk
* todo(1.2.80): got rid of jpsGlobalContext usages (replace with KotlinCompileContext)
*/
private fun markAdditionalFilesForInitialRound(
kotlinChunk: KotlinChunk,
chunk: ModuleChunk,
kotlinContext: KotlinCompileContext
) {
val context = kotlinContext.jpsContext
val dirtyFilesHolder = KotlinDirtySourceFilesHolder(
chunk, chunk,
context, context,
object : DirtyFilesHolderBase<JavaSourceRootDescriptor, ModuleBuildTarget>(context) { object : DirtyFilesHolderBase<JavaSourceRootDescriptor, ModuleBuildTarget>(context) {
@@ -134,50 +213,16 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
} }
} }
) )
val fsOperations = FSOperationsHelper(context, chunk, roundDirtyFiles, LOG) val fsOperations = FSOperationsHelper(context, chunk, dirtyFilesHolder, LOG)
val jpsRepresentativeTarget = chunk.representativeTarget() val representativeTarget = kotlinContext.targetsBinding[chunk.representativeTarget()] ?: return
val representativeTarget = context.kotlinBuildTargets[jpsRepresentativeTarget]
if (representativeTarget == null) {
LOG.warn("Unable to find Kotlin build target for JPS target ${jpsRepresentativeTarget.presentableName}")
}
if (System.getProperty(SKIP_CACHE_VERSION_CHECK_PROPERTY) == null && representativeTarget != null) {
val cacheVersionsProvider = CacheVersionProvider(dataManager.dataPaths, representativeTarget.isIncrementalCompilationEnabled)
val actions = checkCachesVersions(context, cacheVersionsProvider, chunk)
applyActionsOnCacheVersionChange(actions, cacheVersionsProvider, context, dataManager, targets, fsOperations)
if (CacheVersion.Action.REBUILD_ALL_KOTLIN in actions) {
return
}
}
// try to perform a lookup // dependent caches are not required, since we are not going to update caches
// request rebuild if storage is corrupted val incrementalCaches = kotlinChunk.loadCaches(loadDependent = false)
try {
dataManager.withLookupStorage {
it.get(LookupSymbol("<#NAME#>", "<#SCOPE#>"))
}
} catch (e: Exception) {
// todo: report to Intellij when IDEA-187115 is implemented
LOG.info(e)
markAllKotlinForRebuild(context, fsOperations, "Lookup storage is corrupted")
return
}
markAdditionalFilesForInitialRound(chunk, context, fsOperations, roundDirtyFiles)
buildLogger?.afterBuildStarted(context, chunk)
}
private fun markAdditionalFilesForInitialRound(
chunk: ModuleChunk,
context: CompileContext,
fsOperations: FSOperationsHelper,
dirtyFilesHolder: KotlinDirtySourceFilesHolder
) {
val representativeTarget = context.kotlinBuildTargets[chunk.representativeTarget()] ?: return
val incrementalCaches = getIncrementalCaches(chunk, context)
val messageCollector = MessageCollectorAdapter(context, representativeTarget) val messageCollector = MessageCollectorAdapter(context, representativeTarget)
val environment = createCompileEnvironment( val environment = createCompileEnvironment(
kotlinContext.jpsContext,
representativeTarget, representativeTarget,
incrementalCaches, incrementalCaches,
LookupTracker.DO_NOTHING, LookupTracker.DO_NOTHING,
@@ -187,10 +232,10 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
) ?: return ) ?: return
val removedClasses = HashSet<String>() val removedClasses = HashSet<String>()
for (target in chunk.targets) { for (target in kotlinChunk.targets) {
val cache = incrementalCaches[target] ?: continue val cache = incrementalCaches[target] ?: continue
val dirtyFiles = dirtyFilesHolder.getDirtyFiles(target) val dirtyFiles = dirtyFilesHolder.getDirtyFiles(target.jpsModuleBuildTarget)
val removedFiles = dirtyFilesHolder.getRemovedFiles(target) val removedFiles = dirtyFilesHolder.getRemovedFiles(target.jpsModuleBuildTarget)
val existingClasses = JpsKotlinCompilerRunner().classesFqNamesByFiles(environment, dirtyFiles) val existingClasses = JpsKotlinCompilerRunner().classesFqNamesByFiles(environment, dirtyFiles)
val previousClasses = cache.classesFqNamesBySources(dirtyFiles + removedFiles) val previousClasses = cache.classesFqNamesBySources(dirtyFiles + removedFiles)
@@ -209,23 +254,6 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
fsOperations.markFilesForCurrentRound(affectedByRemovedClasses) fsOperations.markFilesForCurrentRound(affectedByRemovedClasses)
} }
private fun checkCachesVersions(
context: CompileContext,
cacheVersionsProvider: CacheVersionProvider,
chunk: ModuleChunk
): Set<CacheVersion.Action> {
val targets = chunk.targets
val dataManager = context.projectDescriptor.dataManager
val allVersions = cacheVersionsProvider.allVersions(targets)
val actions = allVersions.map { it.checkVersion() }.toMutableSet()
val kotlinModuleBuilderTarget = context.kotlinBuildTargets[chunk.representativeTarget()]!!
kotlinModuleBuilderTarget.checkCachesVersions(chunk, dataManager, actions)
return actions
}
override fun chunkBuildFinished(context: CompileContext, chunk: ModuleChunk) { override fun chunkBuildFinished(context: CompileContext, chunk: ModuleChunk) {
super.chunkBuildFinished(context, chunk) super.chunkBuildFinished(context, chunk)
@@ -252,7 +280,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
if (chunk.modules.size > 1) { if (chunk.modules.size > 1) {
messageCollector.report( messageCollector.report(
CompilerMessageSeverity.ERROR, CompilerMessageSeverity.ERROR,
"Cyclically dependent modules is not supported in multiplatform projects" "Cyclically dependent modules are not supported in multiplatform projects"
) )
return ABORT return ABORT
} }
@@ -299,39 +327,47 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
return NOTHING_DONE return NOTHING_DONE
} }
val kotlinContext = context.kotlin
val projectDescriptor = context.projectDescriptor val projectDescriptor = context.projectDescriptor
val dataManager = projectDescriptor.dataManager val dataManager = projectDescriptor.dataManager
val targets = chunk.targets val targets = chunk.targets
val hasKotlin = HasKotlinMarker(dataManager)
val rebuildAfterCacheVersionChanged = RebuildAfterCacheVersionChangeMarker(dataManager)
val isChunkRebuilding = JavaBuilderUtil.isForcedRecompilationAllJavaModules(context) val isChunkRebuilding = JavaBuilderUtil.isForcedRecompilationAllJavaModules(context)
|| targets.any { rebuildAfterCacheVersionChanged[it] == true } || targets.any { kotlinContext.rebuildAfterCacheVersionChanged[it] == true }
if (kotlinDirtyFilesHolder.hasDirtyOrRemovedFiles) { if (!kotlinDirtyFilesHolder.hasDirtyOrRemovedFiles) {
if (!isChunkRebuilding && !representativeTarget.isIncrementalCompilationEnabled) {
targets.forEach { rebuildAfterCacheVersionChanged[it] = true }
return CHUNK_REBUILD_REQUIRED
}
} else {
if (isChunkRebuilding) { if (isChunkRebuilding) {
targets.forEach { hasKotlin[it] = false } targets.forEach {
kotlinContext.hasKotlinMarker[it] = false
}
} }
targets.forEach { rebuildAfterCacheVersionChanged.clean(it) } targets.forEach { kotlinContext.rebuildAfterCacheVersionChanged.clean(it) }
return NOTHING_DONE return NOTHING_DONE
} }
// Request CHUNK_REBUILD when IC is off and there are dirty Kotlin files
// Otherwise unexpected compile error might happen, when there are Groovy files,
// but they are not dirty, so Groovy builder does not generate source stubs,
// and Kotlin builder is filtering out output directory from classpath
// (because it may contain outdated Java classes).
if (!isChunkRebuilding && !representativeTarget.isIncrementalCompilationEnabled) {
targets.forEach { kotlinContext.rebuildAfterCacheVersionChanged[it] = true }
return CHUNK_REBUILD_REQUIRED
}
val targetsWithoutOutputDir = targets.filter { it.outputDir == null } val targetsWithoutOutputDir = targets.filter { it.outputDir == null }
if (targetsWithoutOutputDir.isNotEmpty()) { if (targetsWithoutOutputDir.isNotEmpty()) {
messageCollector.report(ERROR, "Output directory not specified for " + targetsWithoutOutputDir.joinToString()) messageCollector.report(ERROR, "Output directory not specified for " + targetsWithoutOutputDir.joinToString())
return ABORT return ABORT
} }
val kotlinChunk = chunk.toKotlinChunk(context)!!
val project = projectDescriptor.project val project = projectDescriptor.project
val lookupTracker = getLookupTracker(project, representativeTarget) val lookupTracker = getLookupTracker(project, representativeTarget)
val exceptActualTracer = ExpectActualTrackerImpl() val exceptActualTracer = ExpectActualTrackerImpl()
val incrementalCaches = getIncrementalCaches(chunk, context) val incrementalCaches = kotlinChunk.loadCaches()
val environment = createCompileEnvironment( val environment = createCompileEnvironment(
context,
representativeTarget, representativeTarget,
incrementalCaches, incrementalCaches,
lookupTracker, lookupTracker,
@@ -340,7 +376,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
messageCollector messageCollector
) ?: return ABORT ) ?: return ABORT
val commonArguments = representativeTarget.compilerArgumentsForChunk(chunk).apply { val commonArguments = kotlinChunk.compilerArguments.apply {
reportOutputFiles = true reportOutputFiles = true
version = true // Always report the version to help diagnosing user issues if they submit the compiler output version = true // Always report the version to help diagnosing user issues if they submit the compiler output
if (languageVersion == null) languageVersion = VersionView.RELEASED_VERSION.versionString if (languageVersion == null) languageVersion = VersionView.RELEASED_VERSION.versionString
@@ -352,8 +388,15 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
val start = System.nanoTime() val start = System.nanoTime()
val outputItemCollector = doCompileModuleChunk( val outputItemCollector = doCompileModuleChunk(
chunk, representativeTarget, commonArguments, context, kotlinDirtyFilesHolder, fsOperations, kotlinChunk,
environment, incrementalCaches chunk,
representativeTarget,
commonArguments,
context,
kotlinDirtyFilesHolder,
fsOperations,
environment,
incrementalCaches
) )
statisticsLogger.registerStatistic(chunk, System.nanoTime() - start) statisticsLogger.registerStatistic(chunk, System.nanoTime() - start)
@@ -373,22 +416,23 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
val generatedFiles = getGeneratedFiles(context, chunk, environment.outputItemsCollector) val generatedFiles = getGeneratedFiles(context, chunk, environment.outputItemsCollector)
registerOutputItems(outputConsumer, generatedFiles) registerOutputItems(outputConsumer, generatedFiles)
representativeTarget.saveVersions(context, chunk, commonArguments) kotlinChunk.saveVersions()
if (targets.any { hasKotlin[it] == null }) { if (targets.any { kotlinContext.hasKotlinMarker[it] == null }) {
fsOperations.markChunk(recursively = false, kotlinOnly = true, excludeFiles = kotlinDirtyFilesHolder.allDirtyFiles) fsOperations.markChunk(recursively = false, kotlinOnly = true, excludeFiles = kotlinDirtyFilesHolder.allDirtyFiles)
} }
for (target in targets) { for (target in targets) {
hasKotlin[target] = true kotlinContext.hasKotlinMarker[target] = true
rebuildAfterCacheVersionChanged.clean(target) kotlinContext.rebuildAfterCacheVersionChanged.clean(target)
} }
chunk.targets.forEach { kotlinChunk.targets.forEach {
context.kotlinBuildTargets[it]?.doAfterBuild() it.doAfterBuild()
} }
representativeTarget.updateChunkMappings( representativeTarget.updateChunkMappings(
context,
chunk, chunk,
kotlinDirtyFilesHolder, kotlinDirtyFilesHolder,
generatedFiles, generatedFiles,
@@ -407,8 +451,8 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
val changesCollector = ChangesCollector() val changesCollector = ChangesCollector()
for ((target, files) in generatedFiles) { for ((target, files) in generatedFiles) {
val kotlinModuleBuilderTarget = context.kotlinBuildTargets[target]!! val kotlinModuleBuilderTarget = kotlinContext.targetsBinding[target]!!
kotlinModuleBuilderTarget.updateCaches(incrementalCaches[target]!!, files, changesCollector, environment) kotlinModuleBuilderTarget.updateCaches(incrementalCaches[kotlinModuleBuilderTarget]!!, files, changesCollector, environment)
} }
updateLookupStorage(lookupTracker, dataManager, kotlinDirtyFilesHolder) updateLookupStorage(lookupTracker, dataManager, kotlinDirtyFilesHolder)
@@ -426,85 +470,10 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
return OK return OK
} }
private fun applyActionsOnCacheVersionChange( // todo(1.2.80): got rid of ModuleChunk (replace with KotlinChunk)
actions: Set<CacheVersion.Action>, // todo(1.2.80): introduce KotlinRoundCompileContext, move dirtyFilesHolder, fsOperations, environment to it
cacheVersionsProvider: CacheVersionProvider,
context: CompileContext,
dataManager: BuildDataManager,
targets: MutableSet<ModuleBuildTarget>,
fsOperations: FSOperationsHelper
) {
val hasKotlin = HasKotlinMarker(dataManager)
val sortedActions = actions.sorted()
context.testingContext?.buildLogger?.actionsOnCacheVersionChanged(sortedActions)
for (status in sortedActions) {
when (status) {
CacheVersion.Action.REBUILD_ALL_KOTLIN -> {
markAllKotlinForRebuild(context, fsOperations, "Kotlin global lookup map format changed")
return
}
CacheVersion.Action.REBUILD_CHUNK -> {
LOG.info("Clearing caches for " + targets.joinToString { it.presentableName })
val rebuildAfterCacheVersionChanged = RebuildAfterCacheVersionChangeMarker(dataManager)
val kotlinBuildTargets = context.kotlinBuildTargets
for (target in targets) {
dataManager.getKotlinCache(kotlinBuildTargets[target])?.clean()
hasKotlin.clean(target)
rebuildAfterCacheVersionChanged[target] = true
}
fsOperations.markChunk(recursively = false, kotlinOnly = true)
return
}
CacheVersion.Action.CLEAN_NORMAL_CACHES -> {
LOG.info("Clearing caches for all targets")
val kotlinBuildTargets = context.kotlinBuildTargets
for (target in context.allTargets()) {
dataManager.getKotlinCache(kotlinBuildTargets[target])?.clean()
}
}
CacheVersion.Action.CLEAN_DATA_CONTAINER -> {
LOG.info("Clearing lookup cache")
dataManager.cleanLookupStorage(LOG)
cacheVersionsProvider.dataContainerVersion().clean()
}
else -> {
assert(status == CacheVersion.Action.DO_NOTHING) { "Unknown version status $status" }
}
}
}
}
private fun CompileContext.allTargets() =
projectDescriptor.buildTargetIndex.allTargets.filterIsInstanceTo<ModuleBuildTarget, MutableSet<ModuleBuildTarget>>(HashSet())
private fun markAllKotlinForRebuild(context: CompileContext, fsOperations: FSOperationsHelper, reason: String) {
LOG.info("Rebuilding all Kotlin: $reason")
val project = context.projectDescriptor.project
val sourceRoots = project.modules.flatMap { it.sourceRoots }
val dataManager = context.projectDescriptor.dataManager
val rebuildAfterCacheVersionChanged = RebuildAfterCacheVersionChangeMarker(dataManager)
for (sourceRoot in sourceRoots) {
val ktFiles = sourceRoot.file.walk().filter { it.isKotlinSourceFile }
fsOperations.markFiles(ktFiles.toList())
}
val kotlinBuildTargets = context.kotlinBuildTargets
for (target in context.allTargets()) {
dataManager.getKotlinCache(kotlinBuildTargets[target])?.clean()
rebuildAfterCacheVersionChanged[target] = true
}
dataManager.cleanLookupStorage(LOG)
}
private fun doCompileModuleChunk( private fun doCompileModuleChunk(
kotlinChunk: KotlinChunk,
chunk: ModuleChunk, chunk: ModuleChunk,
representativeTarget: KotlinModuleBuildTarget<*>, representativeTarget: KotlinModuleBuildTarget<*>,
commonArguments: CommonCompilerArguments, commonArguments: CommonCompilerArguments,
@@ -512,9 +481,42 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
dirtyFilesHolder: KotlinDirtySourceFilesHolder, dirtyFilesHolder: KotlinDirtySourceFilesHolder,
fsOperations: FSOperationsHelper, fsOperations: FSOperationsHelper,
environment: JpsCompilerEnvironment, environment: JpsCompilerEnvironment,
incrementalCaches: Map<ModuleBuildTarget, JpsIncrementalCache> incrementalCaches: Map<KotlinModuleBuildTarget<*>, JpsIncrementalCache>
): OutputItemsCollector? { ): OutputItemsCollector? {
loadPlugins(representativeTarget, commonArguments, context)
kotlinChunk.targets.forEach {
it.nextRound(context)
}
if (representativeTarget.isIncrementalCompilationEnabled) {
for (target in kotlinChunk.targets) {
val cache = incrementalCaches[target]
val jpsTarget = target.jpsModuleBuildTarget
val targetDirtyFiles = dirtyFilesHolder.byTarget[jpsTarget]
if (cache != null && targetDirtyFiles != null) {
val complementaryFiles = cache.clearComplementaryFilesMapping(
targetDirtyFiles.dirty + targetDirtyFiles.removed
)
fsOperations.markFilesForCurrentRound(jpsTarget, complementaryFiles)
cache.markDirty(targetDirtyFiles.dirty + targetDirtyFiles.removed)
}
}
}
val isDoneSomething = representativeTarget.compileModuleChunk(chunk, commonArguments, dirtyFilesHolder, environment)
return if (isDoneSomething) environment.outputItemsCollector else null
}
private fun loadPlugins(
representativeTarget: KotlinModuleBuildTarget<*>,
commonArguments: CommonCompilerArguments,
context: CompileContext
) {
fun concatenate(strings: Array<String>?, cp: List<String>) = arrayOf(*strings.orEmpty(), *cp.toTypedArray()) fun concatenate(strings: Array<String>?, cp: List<String>) = arrayOf(*strings.orEmpty(), *cp.toTypedArray())
for (argumentProvider in ServiceLoader.load(KotlinJpsCompilerArgumentsProvider::class.java)) { for (argumentProvider in ServiceLoader.load(KotlinJpsCompilerArgumentsProvider::class.java)) {
@@ -532,29 +534,12 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
LOG.debug("Plugin loaded: ${argumentProvider::class.java.simpleName}") LOG.debug("Plugin loaded: ${argumentProvider::class.java.simpleName}")
} }
if (representativeTarget.isIncrementalCompilationEnabled) {
for (target in chunk.targets) {
val cache = incrementalCaches[target]
val targetDirtyFiles = dirtyFilesHolder.byTarget[target]
if (cache != null && targetDirtyFiles != null) {
val complementaryFiles = cache.clearComplementaryFilesMapping(targetDirtyFiles.dirty + targetDirtyFiles.removed)
fsOperations.markFilesForCurrentRound(target, complementaryFiles)
cache.markDirty(targetDirtyFiles.dirty + targetDirtyFiles.removed)
}
}
}
val isDoneSomething = representativeTarget.compileModuleChunk(chunk, commonArguments, dirtyFilesHolder, environment)
return if (isDoneSomething) environment.outputItemsCollector else null
} }
private fun createCompileEnvironment( private fun createCompileEnvironment(
context: CompileContext,
kotlinModuleBuilderTarget: KotlinModuleBuildTarget<*>, kotlinModuleBuilderTarget: KotlinModuleBuildTarget<*>,
incrementalCaches: Map<ModuleBuildTarget, JpsIncrementalCache>, incrementalCaches: Map<KotlinModuleBuildTarget<*>, JpsIncrementalCache>,
lookupTracker: LookupTracker, lookupTracker: LookupTracker,
exceptActualTracer: ExpectActualTracker, exceptActualTracer: ExpectActualTracker,
chunk: ModuleChunk, chunk: ModuleChunk,
@@ -580,7 +565,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
classesToLoadByParent, classesToLoadByParent,
messageCollector, messageCollector,
OutputItemsCollectorImpl(), OutputItemsCollectorImpl(),
ProgressReporterImpl(kotlinModuleBuilderTarget.context, chunk) ProgressReporterImpl(context, chunk)
) )
} }
@@ -622,11 +607,13 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
val representativeTarget = chunk.representativeTarget() val representativeTarget = chunk.representativeTarget()
fun SimpleOutputItem.target() = fun SimpleOutputItem.target() =
sourceFiles.firstOrNull()?.let { sourceToTarget[it] } ?: chunk.targets.singleOrNull { sourceFiles.firstOrNull()?.let { sourceToTarget[it] }
it.outputDir?.let { ?: chunk.targets.singleOrNull { target ->
outputFile.startsWith(it) target.outputDir?.let { outputDir ->
} ?: false outputFile.startsWith(outputDir)
} ?: representativeTarget } ?: false
}
?: representativeTarget
return outputItemCollector.outputs.groupBy(SimpleOutputItem::target, SimpleOutputItem::toGeneratedFile) return outputItemCollector.outputs.groupBy(SimpleOutputItem::target, SimpleOutputItem::toGeneratedFile)
} }
@@ -697,68 +684,4 @@ private fun getLookupTracker(project: JpsProject, representativeTarget: KotlinMo
if (representativeTarget.isIncrementalCompilationEnabled) return LookupTrackerImpl(testLookupTracker) if (representativeTarget.isIncrementalCompilationEnabled) return LookupTrackerImpl(testLookupTracker)
return testLookupTracker return testLookupTracker
} }
private fun getIncrementalCaches(
chunk: ModuleChunk,
context: CompileContext
): Map<ModuleBuildTarget, JpsIncrementalCache> {
val dataManager = context.projectDescriptor.dataManager
val kotlinBuildTargets = context.kotlinBuildTargets
val chunkCaches = chunk.targets.keysToMapExceptNulls {
dataManager.getKotlinCache(kotlinBuildTargets[it])
}
val dependentTargets = getDependentTargets(chunkCaches.keys, context)
val dependentCaches = dependentTargets.mapNotNull {
dataManager.getKotlinCache(kotlinBuildTargets[it])
}
for (chunkCache in chunkCaches.values) {
for (dependentCache in dependentCaches) {
chunkCache.addJpsDependentCache(dependentCache)
}
}
return chunkCaches
}
fun getDependentTargets(
compilingChunkTargets: Set<ModuleBuildTarget>,
context: CompileContext
): Set<ModuleBuildTarget> {
if (compilingChunkTargets.isEmpty()) return setOf()
val compilingChunkModules: Set<JpsModule> = compilingChunkTargets.mapTo(mutableSetOf()) { it.module }
val compilingChunkIsTests = compilingChunkTargets.any { it.isTests }
val classpathKind = JpsJavaClasspathKind.compile(compilingChunkIsTests)
fun dependsOnCompilingChunk(target: BuildTarget<*>): Boolean {
if (target !is ModuleBuildTarget || compilingChunkIsTests && !target.isTests) return false
val dependencies = getDependenciesRecursively(target.module, classpathKind)
return ContainerUtil.intersects(dependencies, compilingChunkModules)
}
val dependentTargets = HashSet<ModuleBuildTarget>()
val sortedChunks = context.projectDescriptor.buildTargetIndex.getSortedTargetChunks(context).iterator()
// skip chunks that are compiled before compilingChunk
while (sortedChunks.hasNext()) {
if (sortedChunks.next().targets == compilingChunkTargets) break
}
// process chunks that compiled after compilingChunk
for (followingChunk in sortedChunks) {
if (followingChunk.targets.none(::dependsOnCompilingChunk)) continue
dependentTargets.addAll(followingChunk.targets.filterIsInstance<ModuleBuildTarget>())
}
return dependentTargets
}
private fun getDependenciesRecursively(module: JpsModule, kind: JpsJavaClasspathKind): Set<JpsModule> =
JpsJavaExtensionService.dependencies(module).includedIn(kind).recursivelyExportedOnly().modules
@@ -0,0 +1,159 @@
/*
* 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.jps.build
import org.jetbrains.jps.incremental.ModuleBuildTarget
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.config.VersionView
import org.jetbrains.kotlin.incremental.storage.version.CacheStatus
import org.jetbrains.kotlin.jps.incremental.JpsIncrementalCache
import org.jetbrains.kotlin.jps.incremental.getKotlinCache
import org.jetbrains.kotlin.jps.model.kotlinCompilerArguments
import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
import org.jetbrains.kotlin.utils.keysToMapExceptNulls
import java.io.File
/**
* Chunk of cyclically dependent [KotlinModuleBuildTarget]s
*/
class KotlinChunk internal constructor(val context: KotlinCompileContext, val targets: List<KotlinModuleBuildTarget<*>>) {
val containsTests = targets.any { it.isTests }
lateinit var dependencies: List<KotlinModuleBuildTarget.Dependency>
// Should be initialized only in KotlinChunk.calculateChunkDependencies
internal set
lateinit var dependent: List<KotlinModuleBuildTarget.Dependency>
// Should be initialized only in KotlinChunk.calculateChunkDependencies
internal set
// used only during dependency calculation
internal var _dependent: MutableSet<KotlinModuleBuildTarget.Dependency>? = mutableSetOf()
val representativeTarget
get() = targets.first()
private val presentableModulesToCompilersList: String
get() = targets.joinToString { "${it.module.name} (${it.globalLookupCacheId})" }
init {
check(targets.all { it.javaClass == representativeTarget.javaClass }) {
"Cyclically dependent modules $presentableModulesToCompilersList should have same compiler."
}
}
val compilerArguments = representativeTarget.jpsModuleBuildTarget.module.kotlinCompilerArguments
val langVersion =
compilerArguments.languageVersion?.let { LanguageVersion.fromVersionString(it) } ?: VersionView.RELEASED_VERSION
val apiVersion =
compilerArguments.apiVersion?.let { ApiVersion.parse(it) } ?: ApiVersion.createByLanguageVersion(
langVersion
)
fun shouldRebuild(): Boolean {
val buildMetaInfo = representativeTarget.buildMetaInfoFactory.create(compilerArguments)
targets.forEach { target ->
if (target.isVersionChanged(this, buildMetaInfo)) {
KotlinBuilder.LOG.info("$target version changed, rebuilding $this")
return true
}
if (target.initialLocalCacheAttributesDiff.status == CacheStatus.INVALID) {
context.testingLogger?.invalidOrUnusedCache(this, null, target.initialLocalCacheAttributesDiff)
KotlinBuilder.LOG.info("$target cache is invalid ${target.initialLocalCacheAttributesDiff}, rebuilding $this")
return true
}
}
return false
}
fun buildMetaInfoFile(target: ModuleBuildTarget): File =
File(
context.dataPaths.getTargetDataRoot(target),
representativeTarget.buildMetaInfoFileName
)
fun saveVersions() {
context.ensureLookupsCacheAttributesSaved()
targets.forEach {
it.initialLocalCacheAttributesDiff.saveExpectedIfNeeded()
}
val serializedMetaInfo = representativeTarget.buildMetaInfoFactory.serializeToString(compilerArguments)
targets.forEach {
buildMetaInfoFile(it.jpsModuleBuildTarget).writeText(serializedMetaInfo)
}
}
fun collectDependentChunksRecursivelyExportedOnly(result: MutableSet<KotlinChunk> = mutableSetOf()) {
dependent.forEach {
if (result.add(it.src.chunk)) {
if (it.exported) {
it.src.chunk.collectDependentChunksRecursivelyExportedOnly(result)
}
}
}
}
fun loadCaches(loadDependent: Boolean = true): Map<KotlinModuleBuildTarget<*>, JpsIncrementalCache> {
val dataManager = context.dataManager
val cacheByChunkTarget = targets.keysToMapExceptNulls {
dataManager.getKotlinCache(it)
}
if (loadDependent) {
addDependentCaches(cacheByChunkTarget.values)
}
return cacheByChunkTarget
}
private fun addDependentCaches(targetsCaches: Collection<JpsIncrementalCache>) {
val dependentChunks = mutableSetOf<KotlinChunk>()
collectDependentChunksRecursivelyExportedOnly(dependentChunks)
val dataManager = context.dataManager
dependentChunks.forEach { decedentChunk ->
decedentChunk.targets.forEach {
val dependentCache = dataManager.getKotlinCache(it)
if (dependentCache != null) {
for (chunkCache in targetsCaches) {
chunkCache.addJpsDependentCache(dependentCache)
}
}
}
}
}
/**
* The same as [org.jetbrains.jps.ModuleChunk.getPresentableShortName]
*/
val presentableShortName: String
get() = buildString {
if (containsTests) append("tests of ")
append(targets.first().module.name)
if (targets.size > 1) {
val andXMore = "and ${targets.size - 1} more"
val other = ", " + targets.asSequence().drop(1).joinToString()
append(if (other.length < andXMore.length) other else andXMore)
}
}
override fun toString(): String {
return "KotlinChunk<${representativeTarget.javaClass.simpleName}>" +
"(${targets.joinToString { it.jpsModuleBuildTarget.presentableName }})"
}
}
@@ -0,0 +1,240 @@
/*
* 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.jps.build
import org.jetbrains.jps.ModuleChunk
import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.jps.incremental.FSOperations
import org.jetbrains.jps.incremental.GlobalContextKey
import org.jetbrains.jps.incremental.fs.CompilationRound
import org.jetbrains.kotlin.incremental.LookupSymbol
import org.jetbrains.kotlin.incremental.storage.version.CacheAttributesDiff
import org.jetbrains.kotlin.incremental.storage.version.CacheStatus
import org.jetbrains.kotlin.incremental.storage.version.loadDiff
import org.jetbrains.kotlin.jps.incremental.*
import org.jetbrains.kotlin.jps.targets.KotlinTargetsIndex
import org.jetbrains.kotlin.jps.targets.KotlinTargetsIndexBuilder
import java.io.File
import java.util.concurrent.atomic.AtomicBoolean
/**
* KotlinCompileContext is shared between all threads (i.e. it is [GlobalContextKey]).
*
* It is initialized lazily, and only before building of first chunk with kotlin code,
* and will be disposed on build finish.
*/
internal val CompileContext.kotlin: KotlinCompileContext
get() {
val userData = getUserData(kotlinCompileContextKey)
if (userData != null) return userData
// here is error (KotlinCompilation available only at build phase)
// let's also check for concurrent initialization
val errorMessage = "KotlinCompileContext available only at build phase " +
"(between first KotlinBuilder.chunkBuildStarted and KotlinBuilder.buildFinished)"
synchronized(kotlinCompileContextKey) {
val newUsedData = getUserData(kotlinCompileContextKey)
if (newUsedData != null) {
error("Concurrent CompileContext.kotlin getter call and KotlinCompileContext initialization detected: $errorMessage")
}
}
error(errorMessage)
}
internal val kotlinCompileContextKey = GlobalContextKey<KotlinCompileContext>("kotlin")
class KotlinCompileContext(val jpsContext: CompileContext) {
val dataManager = jpsContext.projectDescriptor.dataManager
val dataPaths = dataManager.dataPaths
val testingLogger: TestingBuildLogger?
get() = jpsContext.testingContext?.buildLogger
val targetsIndex: KotlinTargetsIndex = KotlinTargetsIndexBuilder(this).build()
val targetsBinding
get() = targetsIndex.byJpsTarget
val lookupsCacheAttributesManager: CompositeLookupsCacheAttributesManager = makeLookupsCacheAttributesManager()
val initialLookupsCacheStateDiff: CacheAttributesDiff<*> = loadLookupsCacheStateDiff()
val shouldCheckCacheVersions = System.getProperty(KotlinBuilder.SKIP_CACHE_VERSION_CHECK_PROPERTY) == null
val hasKotlinMarker = HasKotlinMarker(dataManager)
/**
* Flag to prevent rebuilding twice.
*
* TODO: looks like it is not required since cache version checking are refactored
*/
val rebuildAfterCacheVersionChanged = RebuildAfterCacheVersionChangeMarker(dataManager)
var rebuildingAllKotlin = false
private fun makeLookupsCacheAttributesManager(): CompositeLookupsCacheAttributesManager {
val expectedLookupsCacheComponents = mutableSetOf<String>()
targetsIndex.chunks.forEach { chunk ->
chunk.targets.forEach { target ->
if (target.isIncrementalCompilationEnabled) {
expectedLookupsCacheComponents.add(target.globalLookupCacheId)
}
}
}
val lookupsCacheRootPath = dataPaths.getTargetDataRoot(KotlinDataContainerTarget)
return CompositeLookupsCacheAttributesManager(lookupsCacheRootPath, expectedLookupsCacheComponents)
}
private fun loadLookupsCacheStateDiff(): CacheAttributesDiff<CompositeLookupsCacheAttributes> {
val diff = lookupsCacheAttributesManager.loadDiff()
if (diff.status == CacheStatus.VALID) {
// try to perform a lookup
// request rebuild if storage is corrupted
try {
dataManager.withLookupStorage {
it.get(LookupSymbol("<#NAME#>", "<#SCOPE#>"))
}
} catch (e: Exception) {
jpsReportInternalBuilderError(jpsContext, Error("Lookup storage is corrupted, probe failed: ${e.message}", e))
markAllKotlinForRebuild("Lookup storage is corrupted")
return diff.copy(actual = null)
}
}
return diff
}
fun hasKotlin() = targetsIndex.chunks.any { chunk ->
chunk.targets.any { target ->
hasKotlinMarker[target] == true
}
}
fun checkCacheVersions() {
when (initialLookupsCacheStateDiff.status) {
CacheStatus.INVALID -> {
// global cache needs to be rebuilt
testingLogger?.invalidOrUnusedCache(null, null, initialLookupsCacheStateDiff)
if (initialLookupsCacheStateDiff.actual != null) {
markAllKotlinForRebuild("Kotlin incremental cache settings or format was changed")
clearLookupCache()
} else {
markAllKotlinForRebuild("Kotlin incremental cache is missed or corrupted")
}
}
CacheStatus.VALID -> Unit
CacheStatus.SHOULD_BE_CLEARED -> {
jpsContext.testingContext?.buildLogger?.invalidOrUnusedCache(null, null, initialLookupsCacheStateDiff)
KotlinBuilder.LOG.info("Removing global cache as it is not required anymore: $initialLookupsCacheStateDiff")
clearAllCaches()
}
CacheStatus.CLEARED -> Unit
}
}
private val lookupAttributesSaved = AtomicBoolean(false)
/**
* Called on every successful compilation
*/
fun ensureLookupsCacheAttributesSaved() {
if (lookupAttributesSaved.compareAndSet(false, true)) {
initialLookupsCacheStateDiff.saveExpectedIfNeeded()
}
}
fun checkChunkCacheVersion(chunk: KotlinChunk) {
if (shouldCheckCacheVersions && !rebuildingAllKotlin) {
if (chunk.shouldRebuild()) markChunkForRebuildBeforeBuild(chunk)
}
}
private fun logMarkDirtyForTestingBeforeRound(file: File, shouldProcess: Boolean): Boolean {
if (shouldProcess) {
testingLogger?.markedAsDirtyBeforeRound(listOf(file))
}
return shouldProcess
}
private fun markAllKotlinForRebuild(reason: String) {
if (rebuildingAllKotlin) return
rebuildingAllKotlin = true
KotlinBuilder.LOG.info("Rebuilding all Kotlin: $reason")
val dataManager = jpsContext.projectDescriptor.dataManager
targetsIndex.chunks.forEach {
markChunkForRebuildBeforeBuild(it)
}
dataManager.cleanLookupStorage(KotlinBuilder.LOG)
}
private fun markChunkForRebuildBeforeBuild(chunk: KotlinChunk) {
chunk.targets.forEach {
FSOperations.markDirty(jpsContext, CompilationRound.NEXT, it.jpsModuleBuildTarget) { file ->
logMarkDirtyForTestingBeforeRound(file, file.isKotlinSourceFile)
}
dataManager.getKotlinCache(it)?.clean()
hasKotlinMarker.clean(it)
rebuildAfterCacheVersionChanged[it] = true
}
}
private fun clearAllCaches() {
clearLookupCache()
KotlinBuilder.LOG.info("Clearing caches for all targets")
targetsIndex.chunks.forEach { chunk ->
chunk.targets.forEach {
dataManager.getKotlinCache(it)?.clean()
}
}
}
private fun clearLookupCache() {
KotlinBuilder.LOG.info("Clearing lookup cache")
dataManager.cleanLookupStorage(KotlinBuilder.LOG)
initialLookupsCacheStateDiff.saveExpectedIfNeeded()
}
fun cleanupCaches() {
// todo: remove lookups for targets with disabled IC (or split global lookups cache into several caches for each compiler)
targetsIndex.chunks.forEach { chunk ->
chunk.targets.forEach { target ->
if (target.initialLocalCacheAttributesDiff.status == CacheStatus.SHOULD_BE_CLEARED) {
KotlinBuilder.LOG.info(
"$target caches is cleared as not required anymore: ${target.initialLocalCacheAttributesDiff}"
)
testingLogger?.invalidOrUnusedCache(null, target, target.initialLocalCacheAttributesDiff)
dataManager.getKotlinCache(target)?.clean()
}
}
}
}
fun dispose() {
}
fun getChunk(rawChunk: ModuleChunk): KotlinChunk? {
val rawRepresentativeTarget = rawChunk.representativeTarget()
if (rawRepresentativeTarget !in targetsBinding) return null
return targetsIndex.chunksByJpsRepresentativeTarget[rawRepresentativeTarget]
?: error("Kotlin binding for chunk $this is not loaded at build start")
}
}
@@ -48,7 +48,7 @@ class KotlinDirtySourceFilesHolder(
* and during KotlinDirtySourceFilesHolder initialization. * and during KotlinDirtySourceFilesHolder initialization.
*/ */
internal fun _markDirty(file: File) { internal fun _markDirty(file: File) {
_dirty.add(file) _dirty.add(file.canonicalFile)
} }
} }
@@ -55,17 +55,12 @@ class KotlinSourceRootProvider : AdditionalRootsProviderService<JavaSourceRootDe
// new multiplatform model support: // new multiplatform model support:
module.sourceSetModules.forEach { sourceSetModule -> module.sourceSetModules.forEach { sourceSetModule ->
addModuleSourceRoots(result, sourceSetModule, target, false) addModuleSourceRoots(result, sourceSetModule, target)
} }
// legacy multiplatform model support: // legacy multiplatform model support:
module.expectedByModules.forEach { commonModule -> module.expectedByModules.forEach { commonModule ->
// At the moment, incremental compilation is not supported by K2Metadata compiler. addModuleSourceRoots(result, commonModule, target)
// To avoid long running compilation of common modules, we do not run K2Metadata at all:
// instead all the common source roots are transitively added to the final platform modules.
val isRecursive = true
addModuleSourceRoots(result, commonModule, target, isRecursive)
} }
return result return result
@@ -74,8 +69,7 @@ class KotlinSourceRootProvider : AdditionalRootsProviderService<JavaSourceRootDe
private fun addModuleSourceRoots( private fun addModuleSourceRoots(
result: MutableList<JavaSourceRootDescriptor>, result: MutableList<JavaSourceRootDescriptor>,
module: JpsModule, module: JpsModule,
target: ModuleBuildTarget, target: ModuleBuildTarget
recursive: Boolean = false
) { ) {
for (commonSourceRoot in module.sourceRoots) { for (commonSourceRoot in module.sourceRoots) {
val isCommonTestsRootType = commonSourceRoot.rootType.isTestsRootType val isCommonTestsRootType = commonSourceRoot.rootType.isTestsRootType
@@ -94,21 +88,6 @@ class KotlinSourceRootProvider : AdditionalRootsProviderService<JavaSourceRootDe
) )
} }
} }
if (recursive) {
JpsJavaExtensionService.dependencies(module)
.also {
if (!target.isTests) it.productionOnly()
}
.processModules {
addModuleSourceRoots(
result,
it,
ModuleBuildTarget(it, target.targetType as JavaModuleBuildTargetType),
true
)
}
}
} }
} }
@@ -20,12 +20,16 @@ import org.jetbrains.jps.builders.storage.BuildDataPaths
import org.jetbrains.jps.incremental.ModuleBuildTarget import org.jetbrains.jps.incremental.ModuleBuildTarget
import org.jetbrains.jps.incremental.storage.BuildDataManager import org.jetbrains.jps.incremental.storage.BuildDataManager
import org.jetbrains.kotlin.incremental.KOTLIN_CACHE_DIRECTORY_NAME import org.jetbrains.kotlin.incremental.KOTLIN_CACHE_DIRECTORY_NAME
import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
import java.io.File import java.io.File
private val HAS_KOTLIN_MARKER_FILE_NAME = "has-kotlin-marker.txt" private val HAS_KOTLIN_MARKER_FILE_NAME = "has-kotlin-marker.txt"
private val REBUILD_AFTER_CACHE_VERSION_CHANGE_MARKER = "rebuild-after-cache-version-change-marker.txt" private val REBUILD_AFTER_CACHE_VERSION_CHANGE_MARKER = "rebuild-after-cache-version-change-marker.txt"
abstract class MarkerFile(private val fileName: String, private val paths: BuildDataPaths) { abstract class MarkerFile(private val fileName: String, private val paths: BuildDataPaths) {
operator fun get(target: KotlinModuleBuildTarget<*>): Boolean? =
get(target.jpsModuleBuildTarget)
operator fun get(target: ModuleBuildTarget): Boolean? { operator fun get(target: ModuleBuildTarget): Boolean? {
val file = target.markerFile val file = target.markerFile
@@ -34,6 +38,9 @@ abstract class MarkerFile(private val fileName: String, private val paths: Build
return file.readText().toBoolean() return file.readText().toBoolean()
} }
operator fun set(target: KotlinModuleBuildTarget<*>, value: Boolean) =
set(target.jpsModuleBuildTarget, value)
operator fun set(target: ModuleBuildTarget, value: Boolean) { operator fun set(target: ModuleBuildTarget, value: Boolean) {
val file = target.markerFile val file = target.markerFile
@@ -45,6 +52,9 @@ abstract class MarkerFile(private val fileName: String, private val paths: Build
file.writeText(value.toString()) file.writeText(value.toString())
} }
fun clean(target: KotlinModuleBuildTarget<*>) =
clean(target.jpsModuleBuildTarget)
fun clean(target: ModuleBuildTarget) { fun clean(target: ModuleBuildTarget) {
target.markerFile.delete() target.markerFile.delete()
} }
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.config.CompilerRunnerConstants import org.jetbrains.kotlin.config.CompilerRunnerConstants
import org.jetbrains.kotlin.jps.platforms.KotlinModuleBuildTarget import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
import java.io.File import java.io.File
class MessageCollectorAdapter( class MessageCollectorAdapter(
@@ -16,16 +16,19 @@
package org.jetbrains.kotlin.jps.build package org.jetbrains.kotlin.jps.build
import org.jetbrains.jps.ModuleChunk
import org.jetbrains.jps.incremental.CompileContext import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.jps.incremental.ModuleLevelBuilder import org.jetbrains.jps.incremental.ModuleLevelBuilder
import org.jetbrains.kotlin.incremental.CacheVersion import org.jetbrains.kotlin.incremental.storage.version.CacheAttributesDiff
import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
import java.io.File import java.io.File
interface BuildLogger { /**
fun actionsOnCacheVersionChanged(actions: List<CacheVersion.Action>) * Used for assertions in tests.
fun buildStarted(context: CompileContext, chunk: ModuleChunk) */
fun afterBuildStarted(context: CompileContext, chunk: ModuleChunk) interface TestingBuildLogger {
fun invalidOrUnusedCache(chunk: KotlinChunk?, target: KotlinModuleBuildTarget<*>?, attributesDiff: CacheAttributesDiff<*>)
fun chunkBuildStarted(context: CompileContext, chunk: org.jetbrains.jps.ModuleChunk)
fun afterChunkBuildStarted(context: CompileContext, chunk: org.jetbrains.jps.ModuleChunk)
fun buildFinished(exitCode: ModuleLevelBuilder.ExitCode) fun buildFinished(exitCode: ModuleLevelBuilder.ExitCode)
fun markedAsDirtyBeforeRound(files: Iterable<File>) fun markedAsDirtyBeforeRound(files: Iterable<File>)
fun markedAsDirtyAfterRound(files: Iterable<File>) fun markedAsDirtyAfterRound(files: Iterable<File>)
@@ -24,7 +24,7 @@ import org.jetbrains.jps.model.JpsSimpleElement
import org.jetbrains.jps.model.ex.JpsElementChildRoleBase import org.jetbrains.jps.model.ex.JpsElementChildRoleBase
import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.LookupTracker
private val TESTING_CONTEXT = JpsElementChildRoleBase.create<JpsSimpleElement<out TestingContext>>("Testing context") private val TESTING_CONTEXT = JpsElementChildRoleBase.create<JpsSimpleElement<out TestingContext>>("Testing kcontext")
@TestOnly @TestOnly
fun JpsProject.setTestingContext(context: TestingContext) { fun JpsProject.setTestingContext(context: TestingContext) {
@@ -40,5 +40,7 @@ val CompileContext.testingContext: TestingContext?
class TestingContext( class TestingContext(
val lookupTracker: LookupTracker, val lookupTracker: LookupTracker,
val buildLogger: BuildLogger val buildLogger: TestingBuildLogger
) ) {
var kotlinCompileContext: KotlinCompileContext? = null
}
@@ -0,0 +1,14 @@
/*
* 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.jps.build
import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.jps.incremental.messages.CompilerMessage
fun jpsReportInternalBuilderError(context: CompileContext, error: Throwable) {
val builderError = CompilerMessage.createInternalBuilderError("Kotlin", error)
context.processMessage(builderError)
}
@@ -0,0 +1,13 @@
/*
* 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.jps.build
import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.jps.incremental.messages.CompilerMessage
fun jpsReportInternalBuilderError(context: CompileContext, error: Throwable) {
KotlinBuilder.LOG.info(error)
}
@@ -0,0 +1,13 @@
/*
* 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.jps.build
import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.jps.incremental.messages.CompilerMessage
fun jpsReportInternalBuilderError(context: CompileContext, error: Throwable) {
KotlinBuilder.LOG.info(error)
}
@@ -0,0 +1,13 @@
/*
* 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.jps.build
import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.jps.incremental.messages.CompilerMessage
fun jpsReportInternalBuilderError(context: CompileContext, error: Throwable) {
KotlinBuilder.LOG.info(error)
}
@@ -0,0 +1,13 @@
/*
* 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.jps.build
import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.jps.incremental.messages.CompilerMessage
fun jpsReportInternalBuilderError(context: CompileContext, error: Throwable) {
KotlinBuilder.LOG.info(error)
}
@@ -0,0 +1,13 @@
/*
* 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.jps.build
import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.jps.incremental.messages.CompilerMessage
fun jpsReportInternalBuilderError(context: CompileContext, error: Throwable) {
KotlinBuilder.LOG.info(error)
}
@@ -17,9 +17,31 @@
package org.jetbrains.kotlin.jps.build package org.jetbrains.kotlin.jps.build
import org.jetbrains.jps.ModuleChunk import org.jetbrains.jps.ModuleChunk
import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType
import org.jetbrains.jps.incremental.CompileContext import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.jps.incremental.ModuleBuildTarget
import org.jetbrains.jps.model.module.JpsModule
fun ModuleChunk.isDummy(context: CompileContext): Boolean { fun ModuleChunk.isDummy(context: CompileContext): Boolean {
val targetIndex = context.projectDescriptor.buildTargetIndex val targetIndex = context.projectDescriptor.buildTargetIndex
return targets.all { targetIndex.isDummy(it) } return targets.all { targetIndex.isDummy(it) }
} }
@Deprecated("Use `kotlin.targetBinding` instead", ReplaceWith("kotlin.targetsBinding"))
val CompileContext.kotlinBuildTargets
get() = kotlin.targetsBinding
fun ModuleChunk.toKotlinChunk(context: CompileContext): KotlinChunk? =
context.kotlin.getChunk(this)
fun ModuleBuildTarget(module: JpsModule, isTests: Boolean) =
ModuleBuildTarget(
module,
if (isTests) JavaModuleBuildTargetType.TEST else JavaModuleBuildTargetType.PRODUCTION
)
val JpsModule.productionBuildTarget
get() = ModuleBuildTarget(this, false)
val JpsModule.testBuildTarget
get() = ModuleBuildTarget(this, true)
@@ -1,49 +0,0 @@
/*
* Copyright 2010-2015 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.jps.incremental
import org.jetbrains.jps.builders.BuildTarget
import org.jetbrains.jps.builders.storage.BuildDataPaths
import org.jetbrains.jps.incremental.ModuleBuildTarget
import org.jetbrains.kotlin.incremental.CacheVersion
import org.jetbrains.kotlin.incremental.dataContainerCacheVersion
import org.jetbrains.kotlin.incremental.normalCacheVersion
import java.io.File
class CacheVersionProvider(
private val paths: BuildDataPaths,
private val isIncrementalCompilationEnabled: Boolean
) {
private val BuildTarget<*>.dataRoot: File
get() = paths.getTargetDataRoot(this)
fun normalVersion(target: ModuleBuildTarget): CacheVersion = normalCacheVersion(target.dataRoot, isIncrementalCompilationEnabled)
fun dataContainerVersion(): CacheVersion = dataContainerCacheVersion(KotlinDataContainerTarget.dataRoot, isIncrementalCompilationEnabled)
fun allVersions(targets: Iterable<ModuleBuildTarget>): Iterable<CacheVersion> {
val versions = arrayListOf<CacheVersion>()
versions.add(dataContainerCacheVersion(KotlinDataContainerTarget.dataRoot, isIncrementalCompilationEnabled))
for (dataRoot in targets.map { it.dataRoot }) {
versions.add(normalCacheVersion(dataRoot, isIncrementalCompilationEnabled))
}
return versions
}
}
@@ -0,0 +1,78 @@
/*
* 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.jps.incremental
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.incremental.storage.version.CacheAttributesManager
import org.jetbrains.kotlin.incremental.storage.version.CacheVersion
import org.jetbrains.kotlin.incremental.storage.version.lookupsCacheVersionManager
import java.io.File
import java.io.IOException
/**
* Attributes manager for global lookups cache that may contain lookups for several compilers (jvm, js).
* Works by delegating to [lookupsCacheVersionManager] and managing additional file with list of executed compilers (cache components).
*
* TODO(1.2.80): got rid of shared lookup cache, replace with individual lookup cache for each compiler
*/
class CompositeLookupsCacheAttributesManager(
rootPath: File,
expectedComponents: Set<String>
) : CacheAttributesManager<CompositeLookupsCacheAttributes> {
private val versionManager = lookupsCacheVersionManager(
rootPath,
expectedComponents.isNotEmpty()
)
private val actualComponentsFile = File(rootPath, "components.txt")
override val expected: CompositeLookupsCacheAttributes? =
if (expectedComponents.isEmpty()) null
else CompositeLookupsCacheAttributes(versionManager.expected!!.version, expectedComponents)
override fun loadActual(): CompositeLookupsCacheAttributes? {
val version = versionManager.loadActual() ?: return null
if (!actualComponentsFile.exists()) return null
val components = try {
actualComponentsFile.readLines().toSet()
} catch (e: IOException) {
return null
}
return CompositeLookupsCacheAttributes(version.version, components)
}
override fun writeActualVersion(values: CompositeLookupsCacheAttributes?) {
if (values == null) {
versionManager.writeActualVersion(null)
actualComponentsFile.delete()
} else {
versionManager.writeActualVersion(CacheVersion(values.version))
actualComponentsFile.parentFile.mkdirs()
actualComponentsFile.writeText(values.components.joinToString("\n"))
}
}
override fun isCompatible(actual: CompositeLookupsCacheAttributes, expected: CompositeLookupsCacheAttributes): Boolean {
// cache can be reused when all required (expected) components are present
// (components that are not required anymore are not not interfere)
return actual.version == expected.version && actual.components.containsAll(expected.components)
}
@get:TestOnly
val versionManagerForTesting
get() = versionManager
}
data class CompositeLookupsCacheAttributes(
val version: Int,
val components: Set<String>
) {
override fun toString() = "($version, $components)"
}
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.incremental.IncrementalCacheCommon
import org.jetbrains.kotlin.incremental.IncrementalJsCache import org.jetbrains.kotlin.incremental.IncrementalJsCache
import org.jetbrains.kotlin.incremental.IncrementalJvmCache import org.jetbrains.kotlin.incremental.IncrementalJvmCache
import org.jetbrains.kotlin.jps.build.KotlinBuilder import org.jetbrains.kotlin.jps.build.KotlinBuilder
import org.jetbrains.kotlin.jps.platforms.KotlinModuleBuildTarget import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
import java.io.File import java.io.File
interface JpsIncrementalCache : IncrementalCacheCommon, StorageOwner { interface JpsIncrementalCache : IncrementalCacheCommon, StorageOwner {
@@ -1,99 +0,0 @@
/*
* 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.jps.platforms
import com.intellij.openapi.util.Key
import org.jetbrains.jps.builders.ModuleBasedBuildTargetType
import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType
import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.jps.incremental.ModuleBuildTarget
import org.jetbrains.jps.model.library.JpsOrderRootType
import org.jetbrains.jps.model.module.JpsModule
import org.jetbrains.jps.util.JpsPathUtil
import org.jetbrains.kotlin.jps.model.platform
import org.jetbrains.kotlin.platform.DefaultIdeTargetPlatformKindProvider
import org.jetbrains.kotlin.platform.IdePlatformKind
import org.jetbrains.kotlin.platform.impl.*
import org.jetbrains.kotlin.utils.LibraryUtils
import java.util.concurrent.ConcurrentHashMap
fun ModuleBuildTarget(module: JpsModule, isTests: Boolean) =
ModuleBuildTarget(module, if (isTests) JavaModuleBuildTargetType.TEST else JavaModuleBuildTargetType.PRODUCTION)
val JpsModule.productionBuildTarget
get() = ModuleBuildTarget(this, false)
private val kotlinBuildTargetsCompileContextKey = Key<KotlinBuildTargets>("kotlinBuildTargets")
val CompileContext.kotlinBuildTargets: KotlinBuildTargets
get() {
val value = getUserData(kotlinBuildTargetsCompileContextKey)
if (value != null) return value
synchronized(this) {
val actualValue = getUserData(kotlinBuildTargetsCompileContextKey)
if (actualValue != null) return actualValue
val newValue = KotlinBuildTargets(this)
putUserData(kotlinBuildTargetsCompileContextKey, newValue)
return newValue
}
}
class KotlinBuildTargets internal constructor(val compileContext: CompileContext) {
private val byJpsModuleBuildTarget = ConcurrentHashMap<ModuleBuildTarget, KotlinModuleBuildTarget<*>>()
private val isKotlinJsStdlibJar = ConcurrentHashMap<String, Boolean>()
@JvmName("getNullable")
operator fun get(target: ModuleBuildTarget?): KotlinModuleBuildTarget<*>? {
if (target == null) return null
return get(target)
}
operator fun get(target: ModuleBuildTarget): KotlinModuleBuildTarget<*>? {
if (target.targetType !is ModuleBasedBuildTargetType) return null
return byJpsModuleBuildTarget.computeIfAbsent(target) {
val platform = target.module.platform?.kind ?: detectTargetPlatform(target)
when {
platform.isCommon -> KotlinCommonModuleBuildTarget(compileContext, target)
platform.isJavaScript -> KotlinJsModuleBuildTarget(compileContext, target)
platform.isJvm -> KotlinJvmModuleBuildTarget(compileContext, target)
else -> error("Invalid platform $platform")
}
}
}
/**
* Compatibility for KT-14082
* todo: remove when all projects migrated to facets
*/
private fun detectTargetPlatform(target: ModuleBuildTarget): IdePlatformKind<*> {
if (hasJsStdLib(target)) {
return JsIdePlatformKind
}
return JvmIdePlatformKind
return DefaultIdeTargetPlatformKindProvider.defaultPlatform.kind
}
private fun hasJsStdLib(target: ModuleBuildTarget): Boolean {
KotlinJvmModuleBuildTarget(compileContext, target).allDependencies.libraries.forEach { library ->
for (root in library.getRoots(JpsOrderRootType.COMPILED)) {
val url = root.url
val isKotlinJsLib = isKotlinJsStdlibJar.computeIfAbsent(url) {
LibraryUtils.isKotlinJavascriptStdLibrary(JpsPathUtil.urlToFile(url))
}
if (isKotlinJsLib) return true
}
}
return false
}
}
@@ -3,11 +3,10 @@
* that can be found in the license/LICENSE.txt file. * that can be found in the license/LICENSE.txt file.
*/ */
package org.jetbrains.kotlin.jps.platforms package org.jetbrains.kotlin.jps.targets
import org.jetbrains.jps.ModuleChunk import org.jetbrains.jps.ModuleChunk
import org.jetbrains.jps.builders.storage.BuildDataPaths import org.jetbrains.jps.builders.storage.BuildDataPaths
import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.jps.incremental.ModuleBuildTarget import org.jetbrains.jps.incremental.ModuleBuildTarget
import org.jetbrains.jps.model.library.JpsOrderRootType import org.jetbrains.jps.model.library.JpsOrderRootType
import org.jetbrains.jps.model.module.JpsModule import org.jetbrains.jps.model.module.JpsModule
@@ -17,15 +16,16 @@ import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2MetadataCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2MetadataCompilerArguments
import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment
import org.jetbrains.kotlin.compilerRunner.JpsKotlinCompilerRunner import org.jetbrains.kotlin.compilerRunner.JpsKotlinCompilerRunner
import org.jetbrains.kotlin.config.IncrementalCompilation import org.jetbrains.kotlin.jps.build.KotlinCompileContext
import org.jetbrains.kotlin.jps.build.KotlinDirtySourceFilesHolder import org.jetbrains.kotlin.jps.build.KotlinDirtySourceFilesHolder
import org.jetbrains.kotlin.jps.build.ModuleBuildTarget
import org.jetbrains.kotlin.jps.model.k2MetadataCompilerArguments import org.jetbrains.kotlin.jps.model.k2MetadataCompilerArguments
import org.jetbrains.kotlin.jps.model.kotlinCompilerSettings import org.jetbrains.kotlin.jps.model.kotlinCompilerSettings
private const val COMMON_BUILD_META_INFO_FILE_NAME = "common-build-meta-info.txt" private const val COMMON_BUILD_META_INFO_FILE_NAME = "common-build-meta-info.txt"
class KotlinCommonModuleBuildTarget(context: CompileContext, jpsModuleBuildTarget: ModuleBuildTarget) : class KotlinCommonModuleBuildTarget(kotlinContext: KotlinCompileContext, jpsModuleBuildTarget: ModuleBuildTarget) :
KotlinModuleBuildTarget<CommonBuildMetaInfo>(context, jpsModuleBuildTarget) { KotlinModuleBuildTarget<CommonBuildMetaInfo>(kotlinContext, jpsModuleBuildTarget) {
override val isIncrementalCompilationEnabled: Boolean override val isIncrementalCompilationEnabled: Boolean
get() = false get() = false
@@ -36,6 +36,9 @@ class KotlinCommonModuleBuildTarget(context: CompileContext, jpsModuleBuildTarge
override val buildMetaInfoFileName override val buildMetaInfoFileName
get() = COMMON_BUILD_META_INFO_FILE_NAME get() = COMMON_BUILD_META_INFO_FILE_NAME
override val globalLookupCacheId: String
get() = "metadata-compiler"
override fun compileModuleChunk( override fun compileModuleChunk(
chunk: ModuleChunk, chunk: ModuleChunk,
commonArguments: CommonCompilerArguments, commonArguments: CommonCompilerArguments,
@@ -87,7 +90,7 @@ class KotlinCommonModuleBuildTarget(context: CompileContext, jpsModuleBuildTarge
result: MutableList<String>, result: MutableList<String>,
isTests: Boolean isTests: Boolean
) { ) {
val dependencyBuildTarget = context.kotlinBuildTargets[ModuleBuildTarget(module, isTests)] val dependencyBuildTarget = kotlinContext.targetsBinding[ModuleBuildTarget(module, isTests)]
if (dependencyBuildTarget != this@KotlinCommonModuleBuildTarget && if (dependencyBuildTarget != this@KotlinCommonModuleBuildTarget &&
dependencyBuildTarget is KotlinCommonModuleBuildTarget && dependencyBuildTarget is KotlinCommonModuleBuildTarget &&
@@ -3,11 +3,10 @@
* that can be found in the license/LICENSE.txt file. * that can be found in the license/LICENSE.txt file.
*/ */
package org.jetbrains.kotlin.jps.platforms package org.jetbrains.kotlin.jps.targets
import org.jetbrains.jps.ModuleChunk import org.jetbrains.jps.ModuleChunk
import org.jetbrains.jps.builders.storage.BuildDataPaths import org.jetbrains.jps.builders.storage.BuildDataPaths
import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.jps.incremental.ModuleBuildTarget import org.jetbrains.jps.incremental.ModuleBuildTarget
import org.jetbrains.jps.model.library.JpsOrderRootType import org.jetbrains.jps.model.library.JpsOrderRootType
import org.jetbrains.jps.model.module.JpsModule import org.jetbrains.jps.model.module.JpsModule
@@ -27,7 +26,9 @@ import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider
import org.jetbrains.kotlin.incremental.js.IncrementalDataProviderFromCache import org.jetbrains.kotlin.incremental.js.IncrementalDataProviderFromCache
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumerImpl import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumerImpl
import org.jetbrains.kotlin.jps.build.KotlinCompileContext
import org.jetbrains.kotlin.jps.build.KotlinDirtySourceFilesHolder import org.jetbrains.kotlin.jps.build.KotlinDirtySourceFilesHolder
import org.jetbrains.kotlin.jps.build.ModuleBuildTarget
import org.jetbrains.kotlin.jps.incremental.JpsIncrementalCache import org.jetbrains.kotlin.jps.incremental.JpsIncrementalCache
import org.jetbrains.kotlin.jps.incremental.JpsIncrementalJsCache import org.jetbrains.kotlin.jps.incremental.JpsIncrementalJsCache
import org.jetbrains.kotlin.jps.model.k2JsCompilerArguments import org.jetbrains.kotlin.jps.model.k2JsCompilerArguments
@@ -42,8 +43,10 @@ import java.net.URI
private const val JS_BUILD_META_INFO_FILE_NAME = "js-build-meta-info.txt" private const val JS_BUILD_META_INFO_FILE_NAME = "js-build-meta-info.txt"
class KotlinJsModuleBuildTarget(compileContext: CompileContext, jpsModuleBuildTarget: ModuleBuildTarget) : class KotlinJsModuleBuildTarget(kotlinContext: KotlinCompileContext, jpsModuleBuildTarget: ModuleBuildTarget) :
KotlinModuleBuildTarget<JsBuildMetaInfo>(compileContext, jpsModuleBuildTarget) { KotlinModuleBuildTarget<JsBuildMetaInfo>(kotlinContext, jpsModuleBuildTarget) {
override val globalLookupCacheId: String
get() = "js"
override val isIncrementalCompilationEnabled: Boolean override val isIncrementalCompilationEnabled: Boolean
get() = IncrementalCompilation.isEnabledForJs() get() = IncrementalCompilation.isEnabledForJs()
@@ -56,13 +59,13 @@ class KotlinJsModuleBuildTarget(compileContext: CompileContext, jpsModuleBuildTa
val isFirstBuild: Boolean val isFirstBuild: Boolean
get() { get() {
val targetDataRoot = context.projectDescriptor.dataManager.dataPaths.getTargetDataRoot(jpsModuleBuildTarget) val targetDataRoot = jpsGlobalContext.projectDescriptor.dataManager.dataPaths.getTargetDataRoot(jpsModuleBuildTarget)
return !IncrementalJsCache.hasHeaderFile(targetDataRoot) return !IncrementalJsCache.hasHeaderFile(targetDataRoot)
} }
override fun makeServices( override fun makeServices(
builder: Services.Builder, builder: Services.Builder,
incrementalCaches: Map<ModuleBuildTarget, JpsIncrementalCache>, incrementalCaches: Map<KotlinModuleBuildTarget<*>, JpsIncrementalCache>,
lookupTracker: LookupTracker, lookupTracker: LookupTracker,
exceptActualTracer: ExpectActualTracker exceptActualTracer: ExpectActualTracker
) { ) {
@@ -72,7 +75,7 @@ class KotlinJsModuleBuildTarget(compileContext: CompileContext, jpsModuleBuildTa
register(IncrementalResultsConsumer::class.java, IncrementalResultsConsumerImpl()) register(IncrementalResultsConsumer::class.java, IncrementalResultsConsumerImpl())
if (isIncrementalCompilationEnabled && !isFirstBuild) { if (isIncrementalCompilationEnabled && !isFirstBuild) {
val cache = incrementalCaches[jpsModuleBuildTarget] as IncrementalJsCache val cache = incrementalCaches[this@KotlinJsModuleBuildTarget] as IncrementalJsCache
register( register(
IncrementalDataProvider::class.java, IncrementalDataProvider::class.java,
@@ -188,7 +191,7 @@ class KotlinJsModuleBuildTarget(compileContext: CompileContext, jpsModuleBuildTa
result: MutableList<String>, result: MutableList<String>,
isTests: Boolean isTests: Boolean
) { ) {
val dependencyBuildTarget = context.kotlinBuildTargets[ModuleBuildTarget(module, isTests)] val dependencyBuildTarget = kotlinContext.targetsBinding[ModuleBuildTarget(module, isTests)]
if (dependencyBuildTarget != this@KotlinJsModuleBuildTarget && if (dependencyBuildTarget != this@KotlinJsModuleBuildTarget &&
dependencyBuildTarget is KotlinJsModuleBuildTarget && dependencyBuildTarget is KotlinJsModuleBuildTarget &&
@@ -3,7 +3,7 @@
* that can be found in the license/LICENSE.txt file. * that can be found in the license/LICENSE.txt file.
*/ */
package org.jetbrains.kotlin.jps.platforms package org.jetbrains.kotlin.jps.targets
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.incremental.*
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.jps.build.KotlinBuilder import org.jetbrains.kotlin.jps.build.KotlinBuilder
import org.jetbrains.kotlin.jps.build.KotlinCompileContext
import org.jetbrains.kotlin.jps.build.KotlinDirtySourceFilesHolder import org.jetbrains.kotlin.jps.build.KotlinDirtySourceFilesHolder
import org.jetbrains.kotlin.jps.incremental.JpsIncrementalCache import org.jetbrains.kotlin.jps.incremental.JpsIncrementalCache
import org.jetbrains.kotlin.jps.incremental.JpsIncrementalJvmCache import org.jetbrains.kotlin.jps.incremental.JpsIncrementalJvmCache
@@ -48,8 +49,8 @@ import java.io.IOException
private const val JVM_BUILD_META_INFO_FILE_NAME = "jvm-build-meta-info.txt" private const val JVM_BUILD_META_INFO_FILE_NAME = "jvm-build-meta-info.txt"
class KotlinJvmModuleBuildTarget(compileContext: CompileContext, jpsModuleBuildTarget: ModuleBuildTarget) : class KotlinJvmModuleBuildTarget(kotlinContext: KotlinCompileContext, jpsModuleBuildTarget: ModuleBuildTarget) :
KotlinModuleBuildTarget<JvmBuildMetaInfo>(compileContext, jpsModuleBuildTarget) { KotlinModuleBuildTarget<JvmBuildMetaInfo>(kotlinContext, jpsModuleBuildTarget) {
override val isIncrementalCompilationEnabled: Boolean override val isIncrementalCompilationEnabled: Boolean
get() = IncrementalCompilation.isEnabledForJvm() get() = IncrementalCompilation.isEnabledForJvm()
@@ -64,7 +65,7 @@ class KotlinJvmModuleBuildTarget(compileContext: CompileContext, jpsModuleBuildT
override fun makeServices( override fun makeServices(
builder: Services.Builder, builder: Services.Builder,
incrementalCaches: Map<ModuleBuildTarget, JpsIncrementalCache>, incrementalCaches: Map<KotlinModuleBuildTarget<*>, JpsIncrementalCache>,
lookupTracker: LookupTracker, lookupTracker: LookupTracker,
exceptActualTracer: ExpectActualTracker exceptActualTracer: ExpectActualTracker
) { ) {
@@ -74,7 +75,7 @@ class KotlinJvmModuleBuildTarget(compileContext: CompileContext, jpsModuleBuildT
register( register(
IncrementalCompilationComponents::class.java, IncrementalCompilationComponents::class.java,
IncrementalCompilationComponentsImpl( IncrementalCompilationComponentsImpl(
incrementalCaches.mapKeys { context.kotlinBuildTargets[it.key]!!.targetId } as Map<TargetId, IncrementalCache> incrementalCaches.mapKeys { it.key.targetId } as Map<TargetId, IncrementalCache>
) )
) )
} }
@@ -141,7 +142,7 @@ class KotlinJvmModuleBuildTarget(compileContext: CompileContext, jpsModuleBuildT
var hasDirtySources = false var hasDirtySources = false
val targets = chunk.targets.mapNotNull { this.context.kotlinBuildTargets[it] as? KotlinJvmModuleBuildTarget } val targets = chunk.targets.mapNotNull { kotlinContext.targetsBinding[it] as? KotlinJvmModuleBuildTarget }
val outputDirs = targets.map { it.outputDir }.toSet() val outputDirs = targets.map { it.outputDir }.toSet()
@@ -159,7 +160,7 @@ class KotlinJvmModuleBuildTarget(compileContext: CompileContext, jpsModuleBuildT
kotlinModuleId.name, kotlinModuleId.name,
outputDir.absolutePath, outputDir.absolutePath,
moduleSources, moduleSources,
target.findSourceRoots(context), target.findSourceRoots(dirtyFilesHolder.context),
target.findClassPathRoots(), target.findClassPathRoots(),
commonSources, commonSources,
target.findModularJdkRoot(), target.findModularJdkRoot(),
@@ -256,14 +257,18 @@ class KotlinJvmModuleBuildTarget(compileContext: CompileContext, jpsModuleBuildT
updateIncrementalCache(files, jpsIncrementalCache as IncrementalJvmCache, changesCollector, null) updateIncrementalCache(files, jpsIncrementalCache as IncrementalJvmCache, changesCollector, null)
} }
override val globalLookupCacheId: String
get() = "jvm"
override fun updateChunkMappings( override fun updateChunkMappings(
localContext: CompileContext,
chunk: ModuleChunk, chunk: ModuleChunk,
dirtyFilesHolder: KotlinDirtySourceFilesHolder, dirtyFilesHolder: KotlinDirtySourceFilesHolder,
outputItems: Map<ModuleBuildTarget, Iterable<GeneratedFile>>, outputItems: Map<ModuleBuildTarget, Iterable<GeneratedFile>>,
incrementalCaches: Map<ModuleBuildTarget, JpsIncrementalCache> incrementalCaches: Map<KotlinModuleBuildTarget<*>, JpsIncrementalCache>
) { ) {
val previousMappings = context.projectDescriptor.dataManager.mappings val previousMappings = localContext.projectDescriptor.dataManager.mappings
val callback = JavaBuilderUtil.getDependenciesRegistrar(context) val callback = JavaBuilderUtil.getDependenciesRegistrar(localContext)
val targetDirtyFiles: Map<ModuleBuildTarget, Set<File>> = chunk.targets.keysToMap { val targetDirtyFiles: Map<ModuleBuildTarget, Set<File>> = chunk.targets.keysToMap {
val files = HashSet<File>() val files = HashSet<File>()
@@ -273,7 +278,7 @@ class KotlinJvmModuleBuildTarget(compileContext: CompileContext, jpsModuleBuildT
} }
fun getOldSourceFiles(target: ModuleBuildTarget, generatedClass: GeneratedJvmClass): Set<File> { fun getOldSourceFiles(target: ModuleBuildTarget, generatedClass: GeneratedJvmClass): Set<File> {
val cache = incrementalCaches[target] ?: return emptySet() val cache = incrementalCaches[kotlinContext.targetsBinding[target]] ?: return emptySet()
cache as JpsIncrementalJvmCache cache as JpsIncrementalJvmCache
val className = generatedClass.outputClass.className val className = generatedClass.outputClass.className
@@ -301,7 +306,7 @@ class KotlinJvmModuleBuildTarget(compileContext: CompileContext, jpsModuleBuildT
} }
val allCompiled = dirtyFilesHolder.allDirtyFiles val allCompiled = dirtyFilesHolder.allDirtyFiles
JavaBuilderUtil.registerFilesToCompile(context, allCompiled) JavaBuilderUtil.registerFilesToCompile(localContext, allCompiled)
JavaBuilderUtil.registerSuccessfullyCompiled(context, allCompiled) JavaBuilderUtil.registerSuccessfullyCompiled(localContext, allCompiled)
} }
} }
@@ -3,14 +3,13 @@
* that can be found in the license/LICENSE.txt file. * that can be found in the license/LICENSE.txt file.
*/ */
package org.jetbrains.kotlin.jps.platforms package org.jetbrains.kotlin.jps.targets
import org.jetbrains.jps.ModuleChunk import org.jetbrains.jps.ModuleChunk
import org.jetbrains.jps.builders.storage.BuildDataPaths import org.jetbrains.jps.builders.storage.BuildDataPaths
import org.jetbrains.jps.incremental.CompileContext import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.jps.incremental.ModuleBuildTarget import org.jetbrains.jps.incremental.ModuleBuildTarget
import org.jetbrains.jps.incremental.ProjectBuildException import org.jetbrains.jps.incremental.ProjectBuildException
import org.jetbrains.jps.incremental.storage.BuildDataManager
import org.jetbrains.jps.model.java.JpsJavaClasspathKind import org.jetbrains.jps.model.java.JpsJavaClasspathKind
import org.jetbrains.jps.model.java.JpsJavaExtensionService import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.model.module.JpsModule import org.jetbrains.jps.model.module.JpsModule
@@ -21,20 +20,18 @@ import org.jetbrains.kotlin.build.GeneratedFile
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment
import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.incremental.CacheVersion import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.incremental.ChangesCollector import org.jetbrains.kotlin.incremental.ChangesCollector
import org.jetbrains.kotlin.incremental.ExpectActualTrackerImpl import org.jetbrains.kotlin.incremental.ExpectActualTrackerImpl
import org.jetbrains.kotlin.incremental.LookupTrackerImpl
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.jps.build.KotlinBuilder import org.jetbrains.kotlin.incremental.storage.version.CacheAttributesDiff
import org.jetbrains.kotlin.jps.build.KotlinIncludedModuleSourceRoot import org.jetbrains.kotlin.incremental.storage.version.loadDiff
import org.jetbrains.kotlin.jps.build.KotlinDirtySourceFilesHolder import org.jetbrains.kotlin.incremental.storage.version.localCacheVersionManager
import org.jetbrains.kotlin.jps.build.isKotlinSourceFile import org.jetbrains.kotlin.jps.build.*
import org.jetbrains.kotlin.jps.incremental.CacheVersionProvider
import org.jetbrains.kotlin.jps.incremental.JpsIncrementalCache import org.jetbrains.kotlin.jps.incremental.JpsIncrementalCache
import org.jetbrains.kotlin.jps.model.kotlinCompilerArguments
import org.jetbrains.kotlin.jps.model.productionOutputFilePath import org.jetbrains.kotlin.jps.model.productionOutputFilePath
import org.jetbrains.kotlin.jps.model.testOutputFilePath import org.jetbrains.kotlin.jps.model.testOutputFilePath
import org.jetbrains.kotlin.modules.TargetId import org.jetbrains.kotlin.modules.TargetId
@@ -46,12 +43,35 @@ import java.io.File
/** /**
* Properties and actions for Kotlin test / production module build target. * Properties and actions for Kotlin test / production module build target.
*/ */
abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo>( abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo> internal constructor(
val context: CompileContext, val kotlinContext: KotlinCompileContext,
val jpsModuleBuildTarget: ModuleBuildTarget val jpsModuleBuildTarget: ModuleBuildTarget
) { ) {
/**
* Note: beware of using this context for getting compilation round dependent data:
* for example groovy can provide temp source roots with stubs, and it will be visible
* only in round local compile context.
*
* TODO(1.2.80): got rid of jpsGlobalContext and replace it with kotlinContext
*/
val jpsGlobalContext: CompileContext
get() = kotlinContext.jpsContext
// Initialized in KotlinCompileContext.loadTargets
lateinit var chunk: KotlinChunk
abstract val globalLookupCacheId: String
abstract val isIncrementalCompilationEnabled: Boolean abstract val isIncrementalCompilationEnabled: Boolean
@Suppress("LeakingThis")
val localCacheVersionManager = localCacheVersionManager(
kotlinContext.dataPaths.getTargetDataRoot(jpsModuleBuildTarget),
isIncrementalCompilationEnabled
)
val initialLocalCacheAttributesDiff: CacheAttributesDiff<*> = localCacheVersionManager.loadDiff()
val module: JpsModule val module: JpsModule
get() = jpsModuleBuildTarget.module get() = jpsModuleBuildTarget.module
@@ -76,8 +96,8 @@ abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo>(
val explicitOutputPath = if (isTests) module.testOutputFilePath else module.productionOutputFilePath val explicitOutputPath = if (isTests) module.testOutputFilePath else module.productionOutputFilePath
val explicitOutputDir = explicitOutputPath?.let { File(it).absoluteFile.parentFile } val explicitOutputDir = explicitOutputPath?.let { File(it).absoluteFile.parentFile }
return@lazy explicitOutputDir return@lazy explicitOutputDir
?: jpsModuleBuildTarget.outputDir ?: jpsModuleBuildTarget.outputDir
?: throw ProjectBuildException("No output directory found for " + this) ?: throw ProjectBuildException("No output directory found for " + this)
} }
val friendBuildTargets: List<KotlinModuleBuildTarget<*>> val friendBuildTargets: List<KotlinModuleBuildTarget<*>>
@@ -85,8 +105,8 @@ abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo>(
val result = mutableListOf<KotlinModuleBuildTarget<*>>() val result = mutableListOf<KotlinModuleBuildTarget<*>>()
if (isTests) { if (isTests) {
result.addIfNotNull(context.kotlinBuildTargets[module.productionBuildTarget]) result.addIfNotNull(kotlinContext.targetsBinding[module.productionBuildTarget])
result.addIfNotNull(context.kotlinBuildTargets[relatedProductionModule?.productionBuildTarget]) result.addIfNotNull(kotlinContext.targetsBinding[relatedProductionModule?.productionBuildTarget])
} }
return result.filter { it.sources.isNotEmpty() } return result.filter { it.sources.isNotEmpty() }
@@ -100,26 +120,48 @@ abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo>(
private val relatedProductionModule: JpsModule? private val relatedProductionModule: JpsModule?
get() = JpsJavaExtensionService.getInstance().getTestModuleProperties(module)?.productionModule get() = JpsJavaExtensionService.getInstance().getTestModuleProperties(module)?.productionModule
data class Dependency(
val src: KotlinModuleBuildTarget<*>,
val target: KotlinModuleBuildTarget<*>,
val exported: Boolean
)
// TODO(1.2.80): try replace allDependencies with KotlinChunk.collectDependentChunksRecursivelyExportedOnly
@Deprecated("Consider using precalculated KotlinChunk.collectDependentChunksRecursivelyExportedOnly")
val allDependencies by lazy { val allDependencies by lazy {
JpsJavaExtensionService.dependencies(module).recursively().exportedOnly() JpsJavaExtensionService.dependencies(module).recursively().exportedOnly()
.includedIn(JpsJavaClasspathKind.compile(isTests)) .includedIn(JpsJavaClasspathKind.compile(isTests))
} }
val sources: Map<File, Source> by lazy { /**
mutableMapOf<File, Source>().also { result -> * All sources of this target (including non dirty).
collectSources(result) * Initialized lazily based on global context and will be updated on each round based on round local context.
*
* Update required since source roots can be changed, for example groovy can provide new temporary source roots with stubs.
* Lazy initialization is required for friend build targets, when friends are not compiled in this build run.
*/
val sources: Map<File, Source>
get() = _sources ?: synchronized(this) {
_sources ?: updateSourcesList(jpsGlobalContext)
} }
@Volatile
private var _sources: Map<File, Source>? = null
fun nextRound(localContext: CompileContext) {
updateSourcesList(localContext)
} }
private fun collectSources(receiver: MutableMap<File, Source>) { private fun updateSourcesList(localContext: CompileContext): Map<File, Source> {
val result = mutableMapOf<File, Source>()
val moduleExcludes = module.excludeRootsList.urls.mapTo(java.util.HashSet(), JpsPathUtil::urlToFile) val moduleExcludes = module.excludeRootsList.urls.mapTo(java.util.HashSet(), JpsPathUtil::urlToFile)
val compilerExcludes = JpsJavaExtensionService.getInstance() val compilerExcludes = JpsJavaExtensionService.getInstance()
.getOrCreateCompilerConfiguration(module.project) .getOrCreateCompilerConfiguration(module.project)
.compilerExcludes .compilerExcludes
val buildRootIndex = context.projectDescriptor.buildRootIndex val buildRootIndex = localContext.projectDescriptor.buildRootIndex
val roots = buildRootIndex.getTargetRoots(jpsModuleBuildTarget, context) val roots = buildRootIndex.getTargetRoots(jpsModuleBuildTarget, localContext)
roots.forEach { rootDescriptor -> roots.forEach { rootDescriptor ->
val isIncludedSourceRoot = rootDescriptor is KotlinIncludedModuleSourceRoot val isIncludedSourceRoot = rootDescriptor is KotlinIncludedModuleSourceRoot
@@ -127,11 +169,14 @@ abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo>(
.onEnter { file -> file !in moduleExcludes } .onEnter { file -> file !in moduleExcludes }
.forEach { file -> .forEach { file ->
if (!compilerExcludes.isExcluded(file) && file.isFile && file.isKotlinSourceFile) { if (!compilerExcludes.isExcluded(file) && file.isFile && file.isKotlinSourceFile) {
receiver[file] = Source(file, isIncludedSourceRoot) result[file] = Source(file, isIncludedSourceRoot)
} }
} }
} }
this._sources = result
return result
} }
/** /**
@@ -179,9 +224,6 @@ abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo>(
return false return false
} }
fun compilerArgumentsForChunk(chunk: ModuleChunk): CommonCompilerArguments =
chunk.representativeTarget().module.kotlinCompilerArguments
open fun doAfterBuild() { open fun doAfterBuild() {
} }
@@ -193,10 +235,11 @@ abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo>(
* Called for `ModuleChunk.representativeTarget` * Called for `ModuleChunk.representativeTarget`
*/ */
open fun updateChunkMappings( open fun updateChunkMappings(
localContext: CompileContext,
chunk: ModuleChunk, chunk: ModuleChunk,
dirtyFilesHolder: KotlinDirtySourceFilesHolder, dirtyFilesHolder: KotlinDirtySourceFilesHolder,
outputItems: Map<ModuleBuildTarget, Iterable<GeneratedFile>>, outputItems: Map<ModuleBuildTarget, Iterable<GeneratedFile>>,
incrementalCaches: Map<ModuleBuildTarget, JpsIncrementalCache> incrementalCaches: Map<KotlinModuleBuildTarget<*>, JpsIncrementalCache>
) { ) {
// by default do nothing // by default do nothing
} }
@@ -213,7 +256,7 @@ abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo>(
open fun makeServices( open fun makeServices(
builder: Services.Builder, builder: Services.Builder,
incrementalCaches: Map<ModuleBuildTarget, JpsIncrementalCache>, incrementalCaches: Map<KotlinModuleBuildTarget<*>, JpsIncrementalCache>,
lookupTracker: LookupTracker, lookupTracker: LookupTracker,
exceptActualTracer: ExpectActualTracker exceptActualTracer: ExpectActualTracker
) { ) {
@@ -222,7 +265,7 @@ abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo>(
register(ExpectActualTracker::class.java, exceptActualTracer) register(ExpectActualTracker::class.java, exceptActualTracer)
register(CompilationCanceledStatus::class.java, object : CompilationCanceledStatus { register(CompilationCanceledStatus::class.java, object : CompilationCanceledStatus {
override fun checkCanceled() { override fun checkCanceled() {
if (context.cancelStatus.isCanceled) throw CompilationCanceledException() if (jpsGlobalContext.cancelStatus.isCanceled) throw CompilationCanceledException()
} }
}) })
} }
@@ -261,7 +304,7 @@ abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo>(
val hasRemovedSources = dirtyFilesHolder.getRemovedFiles(target.jpsModuleBuildTarget).isNotEmpty() val hasRemovedSources = dirtyFilesHolder.getRemovedFiles(target.jpsModuleBuildTarget).isNotEmpty()
val hasDirtyOrRemovedSources = moduleSources.isNotEmpty() || hasRemovedSources val hasDirtyOrRemovedSources = moduleSources.isNotEmpty() || hasRemovedSources
if (hasDirtyOrRemovedSources) { if (hasDirtyOrRemovedSources) {
val logger = context.loggingManager.projectBuilderLogger val logger = jpsGlobalContext.loggingManager.projectBuilderLogger
if (logger.isEnabled) { if (logger.isEnabled) {
logger.logCompiledFiles(moduleSources, KotlinBuilder.KOTLIN_BUILDER_NAME, "Compiling files:") logger.logCompiledFiles(moduleSources, KotlinBuilder.KOTLIN_BUILDER_NAME, "Compiling files:")
} }
@@ -274,66 +317,48 @@ abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo>(
abstract val buildMetaInfoFileName: String abstract val buildMetaInfoFileName: String
fun buildMetaInfoFile(target: ModuleBuildTarget, dataManager: BuildDataManager): File = fun isVersionChanged(chunk: KotlinChunk, buildMetaInfo: BuildMetaInfo): Boolean {
File(dataManager.dataPaths.getTargetDataRoot(target), buildMetaInfoFileName) val file = chunk.buildMetaInfoFile(jpsModuleBuildTarget)
if (!file.exists()) return false
fun saveVersions(context: CompileContext, chunk: ModuleChunk, commonArguments: CommonCompilerArguments) { val prevBuildMetaInfo =
val dataManager = context.projectDescriptor.dataManager try {
val targets = chunk.targets buildMetaInfoFactory.deserializeFromString(file.readText()) ?: return false
val cacheVersionsProvider = CacheVersionProvider(dataManager.dataPaths, isIncrementalCompilationEnabled) } catch (e: Exception) {
cacheVersionsProvider.allVersions(targets).forEach { it.saveIfNeeded() } KotlinBuilder.LOG.error("Could not deserialize build meta info", e)
return false
val buildMetaInfo = buildMetaInfoFactory.create(commonArguments)
val serializedMetaInfo = buildMetaInfoFactory.serializeToString(buildMetaInfo)
for (target in chunk.targets) {
buildMetaInfoFile(target, dataManager).writeText(serializedMetaInfo)
}
}
fun checkCachesVersions(chunk: ModuleChunk, dataManager: BuildDataManager, actions: MutableSet<CacheVersion.Action>) {
val args = compilerArgumentsForChunk(chunk)
val currentBuildMetaInfo = buildMetaInfoFactory.create(args)
for (target in chunk.targets) {
val file = buildMetaInfoFile(target, dataManager)
if (!file.exists()) continue
val lastBuildMetaInfo =
try {
buildMetaInfoFactory.deserializeFromString(file.readText()) ?: continue
} catch (e: Exception) {
KotlinBuilder.LOG.error("Could not deserialize build meta info", e)
continue
}
val lastBuildLangVersion = LanguageVersion.fromVersionString(lastBuildMetaInfo.languageVersionString)
val lastBuildApiVersion = ApiVersion.parse(lastBuildMetaInfo.apiVersionString)
val currentLangVersion =
args.languageVersion?.let { LanguageVersion.fromVersionString(it) } ?: VersionView.RELEASED_VERSION
val currentApiVersion =
args.apiVersion?.let { ApiVersion.parse(it) } ?: ApiVersion.createByLanguageVersion(currentLangVersion)
val reasonToRebuild = when {
currentLangVersion != lastBuildLangVersion -> {
"Language version was changed ($lastBuildLangVersion -> $currentLangVersion)"
}
currentApiVersion != lastBuildApiVersion -> {
"Api version was changed ($lastBuildApiVersion -> $currentApiVersion)"
}
lastBuildLangVersion != LanguageVersion.KOTLIN_1_0 && lastBuildMetaInfo.isEAP && !currentBuildMetaInfo.isEAP -> {
// If EAP->Non-EAP build with IC, then rebuild all kotlin
"Last build was compiled with EAP-plugin"
}
else -> null
} }
if (reasonToRebuild != null) { val prevLangVersion = LanguageVersion.fromVersionString(prevBuildMetaInfo.languageVersionString)
KotlinBuilder.LOG.info("$reasonToRebuild. Performing non-incremental rebuild (kotlin only)") val prevApiVersion = ApiVersion.parse(prevBuildMetaInfo.apiVersionString)
actions.add(CacheVersion.Action.REBUILD_ALL_KOTLIN)
val reasonToRebuild = when {
chunk.langVersion != prevLangVersion -> "Language version was changed ($prevLangVersion -> ${chunk.langVersion})"
chunk.apiVersion != prevApiVersion -> "Api version was changed ($prevApiVersion -> ${chunk.apiVersion})"
prevLangVersion != LanguageVersion.KOTLIN_1_0 && prevBuildMetaInfo.isEAP && !buildMetaInfo.isEAP -> {
// If EAP->Non-EAP build with IC, then rebuild all kotlin
"Last build was compiled with EAP-plugin"
} }
else -> null
} }
if (reasonToRebuild != null) {
KotlinBuilder.LOG.info("$reasonToRebuild. Performing non-incremental rebuild (kotlin only)")
return true
}
return false
} }
}
private fun checkRepresentativeTarget(chunk: KotlinChunk) {
check(chunk.representativeTarget == this)
}
private fun checkRepresentativeTarget(chunk: ModuleChunk) {
check(chunk.representativeTarget() == jpsModuleBuildTarget)
}
private fun checkRepresentativeTarget(chunk: List<KotlinModuleBuildTarget<*>>) {
check(chunk.first() == this)
}
}
@@ -0,0 +1,178 @@
/*
* 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.jps.targets
import org.jetbrains.jps.incremental.ModuleBuildTarget
import org.jetbrains.jps.model.java.JpsJavaClasspathKind
import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.model.library.JpsOrderRootType
import org.jetbrains.jps.util.JpsPathUtil
import org.jetbrains.kotlin.jps.build.KotlinBuilder
import org.jetbrains.kotlin.jps.build.KotlinChunk
import org.jetbrains.kotlin.jps.build.KotlinCompileContext
import org.jetbrains.kotlin.jps.build.ModuleBuildTarget
import org.jetbrains.kotlin.jps.model.platform
import org.jetbrains.kotlin.platform.DefaultIdeTargetPlatformKindProvider
import org.jetbrains.kotlin.platform.IdePlatformKind
import org.jetbrains.kotlin.platform.impl.*
import org.jetbrains.kotlin.utils.LibraryUtils
import kotlin.system.measureTimeMillis
class KotlinTargetsIndex(
val byJpsTarget: Map<ModuleBuildTarget, KotlinModuleBuildTarget<*>>,
val chunks: List<KotlinChunk>,
val chunksByJpsRepresentativeTarget: Map<ModuleBuildTarget, KotlinChunk>
)
internal class KotlinTargetsIndexBuilder internal constructor(
private val uninitializedContext: KotlinCompileContext
) {
private val byJpsModuleBuildTarget = mutableMapOf<ModuleBuildTarget, KotlinModuleBuildTarget<*>>()
private val isKotlinJsStdlibJar = mutableMapOf<String, Boolean>()
private val chunks = mutableListOf<KotlinChunk>()
fun build(): KotlinTargetsIndex {
val time = measureTimeMillis {
val jpsContext = uninitializedContext.jpsContext
// visit all kotlin build targets
jpsContext.projectDescriptor.buildTargetIndex.getSortedTargetChunks(jpsContext).forEach { chunk ->
val moduleBuildTargets = chunk.targets.mapNotNull {
if (it is ModuleBuildTarget) ensureLoaded(it)!!
else null
}
if (moduleBuildTargets.isNotEmpty()) {
val kotlinChunk = KotlinChunk(uninitializedContext, moduleBuildTargets)
moduleBuildTargets.forEach {
it.chunk = kotlinChunk
}
chunks.add(kotlinChunk)
}
}
calculateChunkDependencies()
}
KotlinBuilder.LOG.info("KotlinTargetsIndex created in $time ms")
return KotlinTargetsIndex(
byJpsModuleBuildTarget,
chunks,
chunks.associateBy { it.representativeTarget.jpsModuleBuildTarget }
)
}
private fun calculateChunkDependencies() {
chunks.forEach { chunk ->
val dependencies = mutableSetOf<KotlinModuleBuildTarget.Dependency>()
chunk.targets.forEach {
dependencies.addAll(calculateTargetDependencies(it))
}
chunk.dependencies = dependencies.toList()
chunk.dependencies.forEach { dependency ->
dependency.target.chunk._dependent!!.add(dependency)
}
}
chunks.forEach {
it.dependent = it._dependent!!.toList()
it._dependent = null
}
}
private fun calculateTargetDependencies(srcTarget: KotlinModuleBuildTarget<*>): List<KotlinModuleBuildTarget.Dependency> {
val dependencies = mutableListOf<KotlinModuleBuildTarget.Dependency>()
val classpathKind = JpsJavaClasspathKind.compile(srcTarget.isTests)
// TODO(1.2.80): Ask for JPS API
// Unfortunately JPS has no API for accessing "exported" flag while enumerating module dependencies,
// but has API for getting all and exported only dependent modules.
// So, lets first get set of all dependent targets, then remove exported only.
val dependentTargets = mutableSetOf<KotlinModuleBuildTarget<*>>()
JpsJavaExtensionService.dependencies(srcTarget.module)
.includedIn(classpathKind)
.processModules { destModule ->
val destKotlinTarget = byJpsModuleBuildTarget[ModuleBuildTarget(destModule, srcTarget.isTests)]
if (destKotlinTarget != null) {
dependentTargets.add(destKotlinTarget)
}
}
JpsJavaExtensionService.dependencies(srcTarget.module)
.includedIn(classpathKind)
.exportedOnly()
.processModules { module ->
val destKotlinTarget = byJpsModuleBuildTarget[ModuleBuildTarget(module, srcTarget.isTests)]
if (destKotlinTarget != null) {
dependentTargets.remove(destKotlinTarget)
dependencies.add(KotlinModuleBuildTarget.Dependency(srcTarget, destKotlinTarget, true))
}
}
dependentTargets.forEach { destTarget ->
dependencies.add(KotlinModuleBuildTarget.Dependency(srcTarget, destTarget, false))
}
if (srcTarget.isTests) {
val srcProductionTarget = byJpsModuleBuildTarget[ModuleBuildTarget(srcTarget.module, false)]
if (srcProductionTarget != null) {
dependencies.add(KotlinModuleBuildTarget.Dependency(srcTarget, srcProductionTarget, true))
}
}
return dependencies
}
private fun ensureLoaded(target: ModuleBuildTarget): KotlinModuleBuildTarget<*>? {
return byJpsModuleBuildTarget.computeIfAbsent(target) {
val platform = target.module.platform?.kind ?: detectTargetPlatform(target)
when {
platform.isCommon -> KotlinCommonModuleBuildTarget(uninitializedContext, target)
platform.isJavaScript -> KotlinJsModuleBuildTarget(uninitializedContext, target)
platform.isJvm -> KotlinJvmModuleBuildTarget(uninitializedContext, target)
else -> error("Unsupported platform $platform")
}
}
}
/**
* Compatibility for KT-14082
* todo: remove when all projects migrated to facets
*/
private fun detectTargetPlatform(target: ModuleBuildTarget): IdePlatformKind<*> {
if (hasJsStdLib(target)) return JsIdePlatformKind
return DefaultIdeTargetPlatformKindProvider.defaultPlatform.kind
}
private fun hasJsStdLib(target: ModuleBuildTarget): Boolean {
JpsJavaExtensionService.dependencies(target.module)
.recursively()
.exportedOnly()
.includedIn(JpsJavaClasspathKind.compile(target.isTests))
.libraries
.forEach { library ->
for (root in library.getRoots(JpsOrderRootType.COMPILED)) {
val url = root.url
val isKotlinJsLib = isKotlinJsStdlibJar.computeIfAbsent(url) {
LibraryUtils.isKotlinJavascriptStdLibrary(JpsPathUtil.urlToFile(url))
}
if (isKotlinJsLib) return true
}
}
return false
}
}
@@ -1,7 +1,9 @@
================ Step #1 ================= ================ Step #1 =================
Building module1 Building module1
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module1' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module1/src/A.kt
Cleaning output files: Cleaning output files:
out/production/module1/META-INF/module1.kotlin_module out/production/module1/META-INF/module1.kotlin_module
out/production/module1/a/A.class out/production/module1/a/A.class
@@ -12,7 +14,9 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module2 Building module2
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module2' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module2/src/B.kt
Cleaning output files: Cleaning output files:
out/production/module2/META-INF/module2.kotlin_module out/production/module2/META-INF/module2.kotlin_module
out/production/module2/b/B.class out/production/module2/b/B.class
@@ -23,7 +27,9 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module3 Building module3
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module3' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module3/src/C.kt
Cleaning output files: Cleaning output files:
out/production/module3/META-INF/module3.kotlin_module out/production/module3/META-INF/module3.kotlin_module
out/production/module3/c/C.class out/production/module3/c/C.class
@@ -34,7 +40,9 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module4 Building module4
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module4' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module4/src/D.kt
Cleaning output files: Cleaning output files:
out/production/module4/D/D.class out/production/module4/D/D.class
out/production/module4/META-INF/module4.kotlin_module out/production/module4/META-INF/module4.kotlin_module
@@ -43,4 +51,4 @@ Compiling files:
module4/src/D.kt module4/src/D.kt
End of files End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
@@ -1,7 +1,12 @@
================ Step #1 ================= ================ Step #1 =================
Lookups cache are INVALID: actual=(777, [jvm]) -> expected=(3011001, [jvm])
Marked as dirty by Kotlin:
module1/src/A.kt
module2/src/B.kt
module3/src/C.kt
module4/src/D.kt
Building module1 Building module1
Actions after cache changed: [REBUILD_ALL_KOTLIN, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module1/META-INF/module1.kotlin_module out/production/module1/META-INF/module1.kotlin_module
out/production/module1/a/A.class out/production/module1/a/A.class
@@ -9,15 +14,9 @@ End of files
Compiling files: Compiling files:
module1/src/A.kt module1/src/A.kt
End of files End of files
Marked as dirty by Kotlin:
module1/src/A.kt
module2/src/B.kt
module3/src/C.kt
module4/src/D.kt
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module2 Building module2
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module2/META-INF/module2.kotlin_module out/production/module2/META-INF/module2.kotlin_module
out/production/module2/b/B.class out/production/module2/b/B.class
@@ -28,7 +27,6 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module3 Building module3
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module3/META-INF/module3.kotlin_module out/production/module3/META-INF/module3.kotlin_module
out/production/module3/c/C.class out/production/module3/c/C.class
@@ -39,7 +37,6 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module4 Building module4
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module4/D/D.class out/production/module4/D/D.class
out/production/module4/META-INF/module4.kotlin_module out/production/module4/META-INF/module4.kotlin_module
@@ -48,4 +45,4 @@ Compiling files:
module4/src/D.kt module4/src/D.kt
End of files End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
@@ -1,7 +1,9 @@
================ Step #1 ================= ================ Step #1 =================
Building module1 Building module1
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module1' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module1/src/a.kt
Cleaning output files: Cleaning output files:
out/production/module1/META-INF/module1.kotlin_module out/production/module1/META-INF/module1.kotlin_module
out/production/module1/module1/A.class out/production/module1/module1/A.class
@@ -13,7 +15,9 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module2 Building module2
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module2' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module2/src/b.kt
Cleaning output files: Cleaning output files:
out/production/module2/META-INF/module2.kotlin_module out/production/module2/META-INF/module2.kotlin_module
out/production/module2/module2/BKt.class out/production/module2/module2/BKt.class
@@ -24,7 +28,9 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module3 Building module3
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module3' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module3/src/c.kt
Cleaning output files: Cleaning output files:
out/production/module3/META-INF/module3.kotlin_module out/production/module3/META-INF/module3.kotlin_module
out/production/module3/module3/CKt.class out/production/module3/module3/CKt.class
@@ -35,7 +41,9 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module4 Building module4
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module4' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module4/src/d.kt
Cleaning output files: Cleaning output files:
out/production/module4/META-INF/module4.kotlin_module out/production/module4/META-INF/module4.kotlin_module
out/production/module4/module4/D.class out/production/module4/module4/D.class
@@ -47,4 +55,4 @@ Exit code: OK
------------------------------------------ ------------------------------------------
Building module5 Building module5
Exit code: NOTHING_DONE Exit code: NOTHING_DONE
------------------------------------------ ------------------------------------------
@@ -1,7 +1,12 @@
================ Step #1 ================= ================ Step #1 =================
Lookups cache are INVALID: actual=(777, [jvm]) -> expected=(3011001, [jvm])
Marked as dirty by Kotlin:
module1/src/a.kt
module2/src/b.kt
module3/src/c.kt
module4/src/d.kt
Building module1 Building module1
Actions after cache changed: [REBUILD_ALL_KOTLIN, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module1/META-INF/module1.kotlin_module out/production/module1/META-INF/module1.kotlin_module
out/production/module1/module1/A.class out/production/module1/module1/A.class
@@ -10,15 +15,9 @@ End of files
Compiling files: Compiling files:
module1/src/a.kt module1/src/a.kt
End of files End of files
Marked as dirty by Kotlin:
module1/src/a.kt
module2/src/b.kt
module3/src/c.kt
module4/src/d.kt
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module2 Building module2
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module2/META-INF/module2.kotlin_module out/production/module2/META-INF/module2.kotlin_module
out/production/module2/module2/BKt.class out/production/module2/module2/BKt.class
@@ -29,7 +28,6 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module3 Building module3
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module3/META-INF/module3.kotlin_module out/production/module3/META-INF/module3.kotlin_module
out/production/module3/module3/CKt.class out/production/module3/module3/CKt.class
@@ -40,7 +38,6 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module4 Building module4
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module4/META-INF/module4.kotlin_module out/production/module4/META-INF/module4.kotlin_module
out/production/module4/module4/D.class out/production/module4/module4/D.class
@@ -52,4 +49,4 @@ Exit code: OK
------------------------------------------ ------------------------------------------
Building module5 Building module5
Exit code: NOTHING_DONE Exit code: NOTHING_DONE
------------------------------------------ ------------------------------------------
@@ -1,7 +1,9 @@
================ Step #1 ================= ================ Step #1 =================
Building module1 Building module1
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module1' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module1/src/a.kt
Cleaning output files: Cleaning output files:
out/production/module1/META-INF/module1.kotlin_module out/production/module1/META-INF/module1.kotlin_module
out/production/module1/module1/A.class out/production/module1/module1/A.class
@@ -13,7 +15,9 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module2 Building module2
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module2' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module2/src/b.kt
Cleaning output files: Cleaning output files:
out/production/module2/META-INF/module2.kotlin_module out/production/module2/META-INF/module2.kotlin_module
out/production/module2/module2/BKt.class out/production/module2/module2/BKt.class
@@ -22,4 +26,4 @@ Compiling files:
module2/src/b.kt module2/src/b.kt
End of files End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
@@ -1,7 +1,10 @@
================ Step #1 ================= ================ Step #1 =================
Lookups cache are INVALID: actual=(777, [jvm]) -> expected=(3011001, [jvm])
Marked as dirty by Kotlin:
module1/src/a.kt
module2/src/b.kt
Building module1 Building module1
Actions after cache changed: [REBUILD_ALL_KOTLIN, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module1/META-INF/module1.kotlin_module out/production/module1/META-INF/module1.kotlin_module
out/production/module1/module1/A.class out/production/module1/module1/A.class
@@ -10,13 +13,9 @@ End of files
Compiling files: Compiling files:
module1/src/a.kt module1/src/a.kt
End of files End of files
Marked as dirty by Kotlin:
module1/src/a.kt
module2/src/b.kt
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module2 Building module2
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module2/META-INF/module2.kotlin_module out/production/module2/META-INF/module2.kotlin_module
out/production/module2/module2/BKt.class out/production/module2/module2/BKt.class
@@ -25,4 +24,4 @@ Compiling files:
module2/src/b.kt module2/src/b.kt
End of files End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
@@ -1,7 +1,9 @@
================ Step #1 ================= ================ Step #1 =================
Building module1 Building module1
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module1' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module1/src/A.kt
Cleaning output files: Cleaning output files:
out/production/module1/META-INF/module1.kotlin_module out/production/module1/META-INF/module1.kotlin_module
out/production/module1/a/AKt.class out/production/module1/a/AKt.class
@@ -12,7 +14,9 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module2 Building module2
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module2' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module2/src/B.kt
Cleaning output files: Cleaning output files:
out/production/module2/META-INF/module2.kotlin_module out/production/module2/META-INF/module2.kotlin_module
out/production/module2/b/B.class out/production/module2/b/B.class
@@ -21,4 +25,4 @@ Compiling files:
module2/src/B.kt module2/src/B.kt
End of files End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
@@ -1,7 +1,10 @@
================ Step #1 ================= ================ Step #1 =================
Lookups cache are INVALID: actual=(777, [jvm]) -> expected=(3011001, [jvm])
Marked as dirty by Kotlin:
module1/src/A.kt
module2/src/B.kt
Building module1 Building module1
Actions after cache changed: [REBUILD_ALL_KOTLIN, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module1/META-INF/module1.kotlin_module out/production/module1/META-INF/module1.kotlin_module
out/production/module1/a/AKt.class out/production/module1/a/AKt.class
@@ -9,13 +12,9 @@ End of files
Compiling files: Compiling files:
module1/src/A.kt module1/src/A.kt
End of files End of files
Marked as dirty by Kotlin:
module1/src/A.kt
module2/src/B.kt
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module2 Building module2
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module2/META-INF/module2.kotlin_module out/production/module2/META-INF/module2.kotlin_module
out/production/module2/b/B.class out/production/module2/b/B.class
@@ -24,4 +23,4 @@ Compiling files:
module2/src/B.kt module2/src/B.kt
End of files End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
@@ -1,7 +1,9 @@
================ Step #1 ================= ================ Step #1 =================
Building module1 Building module1
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module1' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module1/src/A.kt
Cleaning output files: Cleaning output files:
out/production/module1/META-INF/module1.kotlin_module out/production/module1/META-INF/module1.kotlin_module
out/production/module1/a/A.class out/production/module1/a/A.class
@@ -13,7 +15,9 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module2 Building module2
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module2' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module2/src/B.kt
Cleaning output files: Cleaning output files:
out/production/module2/META-INF/module2.kotlin_module out/production/module2/META-INF/module2.kotlin_module
out/production/module2/b/B.class out/production/module2/b/B.class
@@ -22,4 +26,4 @@ Compiling files:
module2/src/B.kt module2/src/B.kt
End of files End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
@@ -1,7 +1,10 @@
================ Step #1 ================= ================ Step #1 =================
Lookups cache are INVALID: actual=(777, [jvm]) -> expected=(3011001, [jvm])
Marked as dirty by Kotlin:
module1/src/A.kt
module2/src/B.kt
Building module1 Building module1
Actions after cache changed: [REBUILD_ALL_KOTLIN, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module1/META-INF/module1.kotlin_module out/production/module1/META-INF/module1.kotlin_module
out/production/module1/a/A.class out/production/module1/a/A.class
@@ -10,13 +13,9 @@ End of files
Compiling files: Compiling files:
module1/src/A.kt module1/src/A.kt
End of files End of files
Marked as dirty by Kotlin:
module1/src/A.kt
module2/src/B.kt
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module2 Building module2
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module2/META-INF/module2.kotlin_module out/production/module2/META-INF/module2.kotlin_module
out/production/module2/b/B.class out/production/module2/b/B.class
@@ -25,4 +24,4 @@ Compiling files:
module2/src/B.kt module2/src/B.kt
End of files End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
@@ -1,6 +1,10 @@
================ Step #1 ================= ================ Step #1 =================
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
src/a.kt
src/b.kt
src/other.kt
Cleaning output files: Cleaning output files:
out/production/module/META-INF/module.kotlin_module out/production/module/META-INF/module.kotlin_module
out/production/module/other/OtherKt.class out/production/module/other/OtherKt.class
@@ -1,6 +1,10 @@
================ Step #1 ================= ================ Step #1 =================
Actions after cache changed: [REBUILD_ALL_KOTLIN, DO_NOTHING] Lookups cache are INVALID: actual=(777, [jvm]) -> expected=(3011001, [jvm])
Marked as dirty by Kotlin:
src/a.kt
src/b.kt
src/other.kt
Cleaning output files: Cleaning output files:
out/production/module/META-INF/module.kotlin_module out/production/module/META-INF/module.kotlin_module
out/production/module/other/OtherKt.class out/production/module/other/OtherKt.class
@@ -12,9 +16,5 @@ Compiling files:
src/b.kt src/b.kt
src/other.kt src/other.kt
End of files End of files
Marked as dirty by Kotlin:
src/a.kt
src/b.kt
src/other.kt
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
@@ -1,6 +1,10 @@
================ Step #1 ================= ================ Step #1 =================
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
src/a.kt
src/b.kt
src/other.kt
Cleaning output files: Cleaning output files:
out/production/module/A.class out/production/module/A.class
out/production/module/META-INF/module.kotlin_module out/production/module/META-INF/module.kotlin_module
@@ -17,4 +21,4 @@ Exit code: OK
------------------------------------------ ------------------------------------------
Compiling files: Compiling files:
src/A.java src/A.java
End of files End of files
@@ -1,6 +1,10 @@
================ Step #1 ================= ================ Step #1 =================
Actions after cache changed: [REBUILD_ALL_KOTLIN, DO_NOTHING] Lookups cache are INVALID: actual=(777, [jvm]) -> expected=(3011001, [jvm])
Marked as dirty by Kotlin:
src/a.kt
src/b.kt
src/other.kt
Cleaning output files: Cleaning output files:
out/production/module/A.class out/production/module/A.class
out/production/module/META-INF/module.kotlin_module out/production/module/META-INF/module.kotlin_module
@@ -13,12 +17,8 @@ Compiling files:
src/b.kt src/b.kt
src/other.kt src/other.kt
End of files End of files
Marked as dirty by Kotlin:
src/a.kt
src/b.kt
src/other.kt
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Compiling files: Compiling files:
src/A.java src/A.java
End of files End of files
@@ -1,6 +1,9 @@
================ Step #1 ================= ================ Step #1 =================
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
src/a.kt
src/b.kt
Cleaning output files: Cleaning output files:
out/production/module/META-INF/module.kotlin_module out/production/module/META-INF/module.kotlin_module
out/production/module/test/AKt.class out/production/module/test/AKt.class
@@ -11,4 +14,4 @@ Compiling files:
src/b.kt src/b.kt
End of files End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
@@ -1,6 +1,9 @@
================ Step #1 ================= ================ Step #1 =================
Actions after cache changed: [REBUILD_ALL_KOTLIN, DO_NOTHING] Lookups cache are INVALID: actual=(777, [jvm]) -> expected=(3011001, [jvm])
Marked as dirty by Kotlin:
src/a.kt
src/b.kt
Cleaning output files: Cleaning output files:
out/production/module/META-INF/module.kotlin_module out/production/module/META-INF/module.kotlin_module
out/production/module/test/AKt.class out/production/module/test/AKt.class
@@ -10,8 +13,5 @@ Compiling files:
src/a.kt src/a.kt
src/b.kt src/b.kt
End of files End of files
Marked as dirty by Kotlin:
src/a.kt
src/b.kt
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
@@ -1,7 +1,10 @@
================ Step #1 ================= ================ Step #1 =================
Building module1 Building module1
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module1' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module1/src/a.kt
module1/src/f.kt
Cleaning output files: Cleaning output files:
out/production/module1/META-INF/module1.kotlin_module out/production/module1/META-INF/module1.kotlin_module
out/production/module1/module1/A.class out/production/module1/module1/A.class
@@ -27,7 +30,9 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module2 Building module2
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module2' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module2/src/b.kt
Cleaning output files: Cleaning output files:
out/production/module2/META-INF/module2.kotlin_module out/production/module2/META-INF/module2.kotlin_module
out/production/module2/module2/BKt.class out/production/module2/module2/BKt.class
@@ -38,7 +43,9 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module3 Building module3
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module3' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module3/src/c.kt
Cleaning output files: Cleaning output files:
out/production/module3/META-INF/module3.kotlin_module out/production/module3/META-INF/module3.kotlin_module
out/production/module3/module3/CKt.class out/production/module3/module3/CKt.class
@@ -49,7 +56,9 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module4 Building module4
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING] Local cache for KotlinChunk<KotlinJvmModuleBuildTarget>(Module 'module4' production) are INVALID: actual=CacheVersion(version=777) -> expected=CacheVersion(version=9011001)
Marked as dirty by Kotlin:
module4/src/d.kt
Cleaning output files: Cleaning output files:
out/production/module4/META-INF/module4.kotlin_module out/production/module4/META-INF/module4.kotlin_module
out/production/module4/module4/D.class out/production/module4/module4/D.class
@@ -61,4 +70,4 @@ Exit code: OK
------------------------------------------ ------------------------------------------
Building module5 Building module5
Exit code: NOTHING_DONE Exit code: NOTHING_DONE
------------------------------------------ ------------------------------------------
@@ -1,7 +1,13 @@
================ Step #1 ================= ================ Step #1 =================
Lookups cache are INVALID: actual=(777, [jvm]) -> expected=(3011001, [jvm])
Marked as dirty by Kotlin:
module1/src/a.kt
module1/src/f.kt
module2/src/b.kt
module3/src/c.kt
module4/src/d.kt
Building module1 Building module1
Actions after cache changed: [REBUILD_ALL_KOTLIN, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module1/META-INF/module1.kotlin_module out/production/module1/META-INF/module1.kotlin_module
out/production/module1/module1/A.class out/production/module1/module1/A.class
@@ -12,12 +18,6 @@ Compiling files:
module1/src/a.kt module1/src/a.kt
module1/src/f.kt module1/src/f.kt
End of files End of files
Marked as dirty by Kotlin:
module1/src/a.kt
module1/src/f.kt
module2/src/b.kt
module3/src/c.kt
module4/src/d.kt
Exit code: ABORT Exit code: ABORT
------------------------------------------ ------------------------------------------
COMPILATION FAILED COMPILATION FAILED
@@ -26,21 +26,13 @@ Name expected
================ Step #2 ================= ================ Step #2 =================
Building module1 Building module1
Actions after cache changed: [REBUILD_ALL_KOTLIN, REBUILD_CHUNK]
Compiling files: Compiling files:
module1/src/a.kt module1/src/a.kt
module1/src/f.kt module1/src/f.kt
End of files End of files
Marked as dirty by Kotlin:
module1/src/a.kt
module1/src/f.kt
module2/src/b.kt
module3/src/c.kt
module4/src/d.kt
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module2 Building module2
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module2/META-INF/module2.kotlin_module out/production/module2/META-INF/module2.kotlin_module
out/production/module2/module2/BKt.class out/production/module2/module2/BKt.class
@@ -51,7 +43,6 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module3 Building module3
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module3/META-INF/module3.kotlin_module out/production/module3/META-INF/module3.kotlin_module
out/production/module3/module3/CKt.class out/production/module3/module3/CKt.class
@@ -62,7 +53,6 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module4 Building module4
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module4/META-INF/module4.kotlin_module out/production/module4/META-INF/module4.kotlin_module
out/production/module4/module4/D.class out/production/module4/module4/D.class
@@ -74,4 +64,4 @@ Exit code: OK
------------------------------------------ ------------------------------------------
Building module5 Building module5
Exit code: NOTHING_DONE Exit code: NOTHING_DONE
------------------------------------------ ------------------------------------------
@@ -1,7 +1,11 @@
================ Step #1 ================= ================ Step #1 =================
Lookups cache are SHOULD_BE_CLEARED: actual=(3011001, [jvm]) -> expected=null
Local cache for Module 'module1' production are SHOULD_BE_CLEARED: actual=CacheVersion(version=9011001) -> expected=null
Local cache for Module 'module2' production are SHOULD_BE_CLEARED: actual=CacheVersion(version=9011001) -> expected=null
Local cache for Module 'module3' production are SHOULD_BE_CLEARED: actual=CacheVersion(version=9011001) -> expected=null
Local cache for Module 'module4' production are SHOULD_BE_CLEARED: actual=CacheVersion(version=9011001) -> expected=null
Building module1 Building module1
Actions after cache changed: [CLEAN_NORMAL_CACHES, CLEAN_DATA_CONTAINER]
Exit code: NOTHING_DONE Exit code: NOTHING_DONE
------------------------------------------ ------------------------------------------
Building module2 Building module2
@@ -1,7 +1,11 @@
================ Step #1 ================= ================ Step #1 =================
Lookups cache are SHOULD_BE_CLEARED: actual=(3011001, [jvm]) -> expected=null
Local cache for Module 'module1' production are SHOULD_BE_CLEARED: actual=CacheVersion(version=9011001) -> expected=null
Local cache for Module 'module2' production are SHOULD_BE_CLEARED: actual=CacheVersion(version=9011001) -> expected=null
Local cache for Module 'module3' production are SHOULD_BE_CLEARED: actual=CacheVersion(version=9011001) -> expected=null
Local cache for Module 'module4' production are SHOULD_BE_CLEARED: actual=CacheVersion(version=9011001) -> expected=null
Building module1 Building module1
Actions after cache changed: [CLEAN_NORMAL_CACHES, CLEAN_DATA_CONTAINER]
Exit code: NOTHING_DONE Exit code: NOTHING_DONE
------------------------------------------ ------------------------------------------
Building module2 Building module2
@@ -31,15 +35,7 @@ Exit code: NOTHING_DONE
================ Step #2 ================= ================ Step #2 =================
Building module1 Lookups cache are INVALID: actual=null -> expected=(3011001, [jvm])
Actions after cache changed: [REBUILD_ALL_KOTLIN, REBUILD_CHUNK]
Cleaning output files:
out/production/module1/META-INF/module1.kotlin_module
out/production/module1/foo/Z.class
End of files
Compiling files:
module1/src/z.kt
End of files
Marked as dirty by Kotlin: Marked as dirty by Kotlin:
module1/src/z.kt module1/src/z.kt
module2/src/a.kt module2/src/a.kt
@@ -47,10 +43,17 @@ Marked as dirty by Kotlin:
module2/src/c.kt module2/src/c.kt
module3/src/d.kt module3/src/d.kt
module4/src/e.kt module4/src/e.kt
Building module1
Cleaning output files:
out/production/module1/META-INF/module1.kotlin_module
out/production/module1/foo/Z.class
End of files
Compiling files:
module1/src/z.kt
End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module2 Building module2
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module2/META-INF/module2.kotlin_module out/production/module2/META-INF/module2.kotlin_module
out/production/module2/foo/AKt.class out/production/module2/foo/AKt.class
@@ -65,7 +68,6 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module3 Building module3
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module3/META-INF/module3.kotlin_module out/production/module3/META-INF/module3.kotlin_module
out/production/module3/foo/D.class out/production/module3/foo/D.class
@@ -76,7 +78,6 @@ End of files
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module4 Building module4
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module4/META-INF/module4.kotlin_module out/production/module4/META-INF/module4.kotlin_module
out/production/module4/foo/E.class out/production/module4/foo/E.class
@@ -1,7 +1,10 @@
================ Step #1 ================= ================ Step #1 =================
Lookups cache are SHOULD_BE_CLEARED: actual=(3011001, [jvm]) -> expected=null
Local cache for Module 'module1' production are SHOULD_BE_CLEARED: actual=CacheVersion(version=9011001) -> expected=null
Local cache for Module 'module2' production are SHOULD_BE_CLEARED: actual=CacheVersion(version=9011001) -> expected=null
Local cache for Module 'module3' production are SHOULD_BE_CLEARED: actual=CacheVersion(version=9011001) -> expected=null
Building module1 Building module1
Actions after cache changed: [CLEAN_NORMAL_CACHES, CLEAN_DATA_CONTAINER]
Exit code: NOTHING_DONE Exit code: NOTHING_DONE
------------------------------------------ ------------------------------------------
Building module2 Building module2
@@ -13,8 +16,12 @@ Exit code: NOTHING_DONE
================ Step #2 ================= ================ Step #2 =================
Lookups cache are INVALID: actual=null -> expected=(3011001, [jvm])
Marked as dirty by Kotlin:
module1/src/a.kt
module2/src/c.kt
module3/src/d.kt
Building module1 Building module1
Actions after cache changed: [REBUILD_ALL_KOTLIN, REBUILD_CHUNK]
Cleaning output files: Cleaning output files:
out/production/module1/AKt.class out/production/module1/AKt.class
out/production/module1/META-INF/module1.kotlin_module out/production/module1/META-INF/module1.kotlin_module
@@ -22,14 +29,9 @@ End of files
Compiling files: Compiling files:
module1/src/a.kt module1/src/a.kt
End of files End of files
Marked as dirty by Kotlin:
module1/src/a.kt
module2/src/c.kt
module3/src/d.kt
Exit code: OK Exit code: OK
------------------------------------------ ------------------------------------------
Building module2 Building module2
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module2/B.class out/production/module2/B.class
out/production/module2/CKt.class out/production/module2/CKt.class
@@ -44,7 +46,6 @@ Compiling files:
module2/src/B.java module2/src/B.java
End of files End of files
Building module3 Building module3
Actions after cache changed: [REBUILD_CHUNK, DO_NOTHING]
Cleaning output files: Cleaning output files:
out/production/module3/DKt.class out/production/module3/DKt.class
out/production/module3/META-INF/module3.kotlin_module out/production/module3/META-INF/module3.kotlin_module
@@ -7,4 +7,4 @@ Exit code: NOTHING_DONE
------------------------------------------ ------------------------------------------
Compiling files: Compiling files:
src/A.java src/A.java
End of files End of files
@@ -5,15 +5,15 @@
package org.jetbrains.kotlin.gradle.incremental package org.jetbrains.kotlin.gradle.incremental
import org.jetbrains.kotlin.incremental.CacheVersion import org.jetbrains.kotlin.incremental.customCacheVersionManager
import org.jetbrains.kotlin.incremental.customCacheVersion import org.jetbrains.kotlin.incremental.storage.version.CacheVersionManager
import java.io.File import java.io.File
internal const val GRADLE_CACHE_VERSION = 4 internal const val GRADLE_CACHE_VERSION = 4
internal const val GRADLE_CACHE_VERSION_FILE_NAME = "gradle-format-version.txt" internal const val GRADLE_CACHE_VERSION_FILE_NAME = "gradle-format-version.txt"
internal fun gradleCacheVersion(dataRoot: File, enabled: Boolean): CacheVersion = internal fun gradleCacheVersionManager(dataRoot: File, enabled: Boolean): CacheVersionManager =
customCacheVersion( customCacheVersionManager(
GRADLE_CACHE_VERSION, GRADLE_CACHE_VERSION,
GRADLE_CACHE_VERSION_FILE_NAME, GRADLE_CACHE_VERSION_FILE_NAME,
dataRoot, dataRoot,