KT-34862 use abi snapshot instead of build history files
Use jar snapshot instead build history file to avoid using time stamps and support remote gradle cache
This commit is contained in:
@@ -17,6 +17,7 @@ enum class BuildAttributeKind : Serializable {
|
|||||||
|
|
||||||
enum class BuildAttribute(val kind: BuildAttributeKind) : Serializable {
|
enum class BuildAttribute(val kind: BuildAttributeKind) : Serializable {
|
||||||
NO_BUILD_HISTORY(BuildAttributeKind.REBUILD_REASON),
|
NO_BUILD_HISTORY(BuildAttributeKind.REBUILD_REASON),
|
||||||
|
NO_ABI_SNAPSHOT(BuildAttributeKind.REBUILD_REASON),
|
||||||
CACHE_CORRUPTION(BuildAttributeKind.REBUILD_REASON),
|
CACHE_CORRUPTION(BuildAttributeKind.REBUILD_REASON),
|
||||||
UNKNOWN_CHANGES_IN_GRADLE_INPUTS(BuildAttributeKind.REBUILD_REASON),
|
UNKNOWN_CHANGES_IN_GRADLE_INPUTS(BuildAttributeKind.REBUILD_REASON),
|
||||||
JAVA_CHANGE_UNTRACKED_FILE_IS_REMOVED(BuildAttributeKind.REBUILD_REASON),
|
JAVA_CHANGE_UNTRACKED_FILE_IS_REMOVED(BuildAttributeKind.REBUILD_REASON),
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import java.io.Serializable
|
|||||||
|
|
||||||
@Suppress("Reformat")
|
@Suppress("Reformat")
|
||||||
enum class BuildTime(val parent: BuildTime? = null) : Serializable {
|
enum class BuildTime(val parent: BuildTime? = null) : Serializable {
|
||||||
|
GRADLE_TASK_ACTION,
|
||||||
GRADLE_TASK,
|
GRADLE_TASK,
|
||||||
CLEAR_OUTPUT(GRADLE_TASK),
|
CLEAR_OUTPUT(GRADLE_TASK),
|
||||||
BACKUP_OUTPUT(GRADLE_TASK),
|
BACKUP_OUTPUT(GRADLE_TASK),
|
||||||
@@ -20,6 +21,10 @@ enum class BuildTime(val parent: BuildTime? = null) : Serializable {
|
|||||||
NON_INCREMENTAL_COMPILATION_OUT_OF_PROCESS(RUN_COMPILER),
|
NON_INCREMENTAL_COMPILATION_OUT_OF_PROCESS(RUN_COMPILER),
|
||||||
NON_INCREMENTAL_COMPILATION_DAEMON(RUN_COMPILER),
|
NON_INCREMENTAL_COMPILATION_DAEMON(RUN_COMPILER),
|
||||||
INCREMENTAL_COMPILATION(RUN_COMPILER),
|
INCREMENTAL_COMPILATION(RUN_COMPILER),
|
||||||
|
STORE_BUILD_INFO(INCREMENTAL_COMPILATION),
|
||||||
|
JAR_SNAPSHOT(INCREMENTAL_COMPILATION),
|
||||||
|
SET_UP_ABI_SNAPSHOTS(JAR_SNAPSHOT),
|
||||||
|
IC_ANALYZE_JAR_FILES(JAR_SNAPSHOT),
|
||||||
IC_CALCULATE_INITIAL_DIRTY_SET(INCREMENTAL_COMPILATION),
|
IC_CALCULATE_INITIAL_DIRTY_SET(INCREMENTAL_COMPILATION),
|
||||||
IC_ANALYZE_CHANGES_IN_DEPENDENCIES(IC_CALCULATE_INITIAL_DIRTY_SET),
|
IC_ANALYZE_CHANGES_IN_DEPENDENCIES(IC_CALCULATE_INITIAL_DIRTY_SET),
|
||||||
IC_FIND_HISTORY_FILES(IC_ANALYZE_CHANGES_IN_DEPENDENCIES),
|
IC_FIND_HISTORY_FILES(IC_ANALYZE_CHANGES_IN_DEPENDENCIES),
|
||||||
|
|||||||
@@ -30,6 +30,28 @@ class ChangesCollector {
|
|||||||
private val changedMembers = hashMapOf<FqName, MutableSet<String>>()
|
private val changedMembers = hashMapOf<FqName, MutableSet<String>>()
|
||||||
private val areSubclassesAffected = hashMapOf<FqName, Boolean>()
|
private val areSubclassesAffected = hashMapOf<FqName, Boolean>()
|
||||||
|
|
||||||
|
//TODO for test only: ProtoData or ProtoBuf
|
||||||
|
private val storage = hashMapOf<FqName, ProtoData>()
|
||||||
|
private val removed = ArrayList<FqName>()
|
||||||
|
|
||||||
|
//TODO change to immutable map
|
||||||
|
fun protoDataChanges(): Map<FqName, ProtoData> = storage
|
||||||
|
fun protoDataRemoved(): List<FqName> = removed
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun <T> T.getNonPrivateNames(nameResolver: NameResolver, vararg members: T.() -> List<MessageLite>) =
|
||||||
|
members.flatMap { this.it().filterNot { it.isPrivate }.names(nameResolver) }.toSet()
|
||||||
|
|
||||||
|
fun ClassProtoData.getNonPrivateMemberNames(): Set<String> {
|
||||||
|
return proto.getNonPrivateNames(
|
||||||
|
nameResolver,
|
||||||
|
ProtoBuf.Class::getConstructorList,
|
||||||
|
ProtoBuf.Class::getFunctionList,
|
||||||
|
ProtoBuf.Class::getPropertyList
|
||||||
|
) + proto.enumEntryList.map { nameResolver.getString(it.name) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun changes(): List<ChangeInfo> {
|
fun changes(): List<ChangeInfo> {
|
||||||
val changes = arrayListOf<ChangeInfo>()
|
val changes = arrayListOf<ChangeInfo>()
|
||||||
|
|
||||||
@@ -57,7 +79,7 @@ class ChangesCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun <T, R> MutableMap<T, MutableSet<R>>.getSet(key: T) =
|
private fun <T, R> MutableMap<T, MutableSet<R>>.getSet(key: T) =
|
||||||
getOrPut(key) { HashSet() }
|
getOrPut(key) { HashSet() }
|
||||||
|
|
||||||
private fun collectChangedMember(scope: FqName, name: String) {
|
private fun collectChangedMember(scope: FqName, name: String) {
|
||||||
changedMembers.getSet(scope).add(name)
|
changedMembers.getSet(scope).add(name)
|
||||||
@@ -79,11 +101,35 @@ class ChangesCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun collectProtoChanges(oldData: ProtoData?, newData: ProtoData?, collectAllMembersForNewClass: Boolean = false) {
|
fun collectProtoChanges(oldData: ProtoData?, newData: ProtoData?, collectAllMembersForNewClass: Boolean = false, packageProtoKey: String? = null) {
|
||||||
if (oldData == null && newData == null) {
|
if (oldData == null && newData == null) {
|
||||||
throw IllegalStateException("Old and new value are null")
|
throw IllegalStateException("Old and new value are null")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newData != null) {
|
||||||
|
when (newData) {
|
||||||
|
is ClassProtoData -> {
|
||||||
|
val fqName = newData.nameResolver.getClassId(newData.proto.fqName).asSingleFqName()
|
||||||
|
storage[fqName] = newData
|
||||||
|
}
|
||||||
|
is PackagePartProtoData -> {
|
||||||
|
//TODO fqName is not unique. It's package and can be present in both java and kotlin
|
||||||
|
val fqName = newData.packageFqName
|
||||||
|
storage[packageProtoKey?.let { FqName(it) } ?: fqName] = newData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
when (oldData) {
|
||||||
|
is ClassProtoData -> {
|
||||||
|
removed.add(oldData.nameResolver.getClassId(oldData.proto.fqName).asSingleFqName())
|
||||||
|
}
|
||||||
|
is PackagePartProtoData -> {
|
||||||
|
//TODO fqName is not unique. It's package and can be present in both java and kotlin
|
||||||
|
removed.add(packageProtoKey?.let { FqName(it) } ?: oldData.packageFqName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (oldData == null) {
|
if (oldData == null) {
|
||||||
newData!!.collectAll(isRemoved = false, isAdded = true, collectAllMembersForNewClass = collectAllMembersForNewClass)
|
newData!!.collectAll(isRemoved = false, isAdded = true, collectAllMembersForNewClass = collectAllMembersForNewClass)
|
||||||
return
|
return
|
||||||
@@ -125,8 +171,8 @@ class ChangesCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> T.getNonPrivateNames(nameResolver: NameResolver, vararg members: T.() -> List<MessageLite>): Set<String> =
|
fun <T> T.getNonPrivateNames(nameResolver: NameResolver, vararg members: T.() -> List<MessageLite>) =
|
||||||
members.flatMap { this.it().filterNot { it.isPrivate }.names(nameResolver) }.toSet()
|
members.flatMap { this.it().filterNot { it.isPrivate }.names(nameResolver) }.toSet()
|
||||||
|
|
||||||
//TODO remember all sealed parent classes
|
//TODO remember all sealed parent classes
|
||||||
private fun ProtoData.collectAll(isRemoved: Boolean, isAdded: Boolean, collectAllMembersForNewClass: Boolean = false) =
|
private fun ProtoData.collectAll(isRemoved: Boolean, isAdded: Boolean, collectAllMembersForNewClass: Boolean = false) =
|
||||||
@@ -137,16 +183,15 @@ class ChangesCollector {
|
|||||||
|
|
||||||
private fun PackagePartProtoData.collectAllFromPackage(isRemoved: Boolean) {
|
private fun PackagePartProtoData.collectAllFromPackage(isRemoved: Boolean) {
|
||||||
val memberNames =
|
val memberNames =
|
||||||
proto.getNonPrivateNames(
|
proto.getNonPrivateNames(
|
||||||
nameResolver,
|
nameResolver,
|
||||||
ProtoBuf.Package::getFunctionList,
|
ProtoBuf.Package::getFunctionList,
|
||||||
ProtoBuf.Package::getPropertyList
|
ProtoBuf.Package::getPropertyList
|
||||||
)
|
)
|
||||||
|
|
||||||
if (isRemoved) {
|
if (isRemoved) {
|
||||||
collectRemovedMembers(packageFqName, memberNames)
|
collectRemovedMembers(packageFqName, memberNames)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
collectChangedMembers(packageFqName, memberNames)
|
collectChangedMembers(packageFqName, memberNames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,8 +206,7 @@ class ChangesCollector {
|
|||||||
val collectMember = if (isRemoved) this@ChangesCollector::collectRemovedMember else this@ChangesCollector::collectChangedMember
|
val collectMember = if (isRemoved) this@ChangesCollector::collectRemovedMember else this@ChangesCollector::collectChangedMember
|
||||||
collectMember(classFqName.parent(), classFqName.shortName().asString())
|
collectMember(classFqName.parent(), classFqName.shortName().asString())
|
||||||
memberNames.forEach { collectMember(classFqName, it) }
|
memberNames.forEach { collectMember(classFqName, it) }
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (!isRemoved && collectAllMembersForNewClass) {
|
if (!isRemoved && collectAllMembersForNewClass) {
|
||||||
val memberNames = getNonPrivateMemberNames()
|
val memberNames = getNonPrivateMemberNames()
|
||||||
memberNames.forEach { this@ChangesCollector.collectChangedMember(classFqName, it) }
|
memberNames.forEach { this@ChangesCollector.collectChangedMember(classFqName, it) }
|
||||||
@@ -189,15 +233,6 @@ class ChangesCollector {
|
|||||||
addChangedParents(fqName, changedParentsFqNames)
|
addChangedParents(fqName, changedParentsFqNames)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ClassProtoData.getNonPrivateMemberNames(): Set<String> {
|
|
||||||
return proto.getNonPrivateNames(
|
|
||||||
nameResolver,
|
|
||||||
ProtoBuf.Class::getConstructorList,
|
|
||||||
ProtoBuf.Class::getFunctionList,
|
|
||||||
ProtoBuf.Class::getPropertyList
|
|
||||||
) + proto.enumEntryList.map { nameResolver.getString(it.name) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun collectMemberIfValueWasChanged(scope: FqName, name: String, oldValue: Any?, newValue: Any?) {
|
fun collectMemberIfValueWasChanged(scope: FqName, name: String, oldValue: Any?, newValue: Any?) {
|
||||||
if (oldValue == null && newValue == null) {
|
if (oldValue == null && newValue == null) {
|
||||||
throw IllegalStateException("Old and new value are null for $scope#$name")
|
throw IllegalStateException("Old and new value are null for $scope#$name")
|
||||||
@@ -205,8 +240,7 @@ class ChangesCollector {
|
|||||||
|
|
||||||
if (oldValue != null && newValue == null) {
|
if (oldValue != null && newValue == null) {
|
||||||
collectRemovedMember(scope, name)
|
collectRemovedMember(scope, name)
|
||||||
}
|
} else if (oldValue != newValue) {
|
||||||
else if (oldValue != newValue) {
|
|
||||||
collectChangedMember(scope, name)
|
collectChangedMember(scope, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ open class IncrementalJvmCache(
|
|||||||
sourceToClassesMap.add(source, jvmClassName)
|
sourceToClassesMap.add(source, jvmClassName)
|
||||||
val (proto, nameResolver) = serializedJavaClass.toProtoData()
|
val (proto, nameResolver) = serializedJavaClass.toProtoData()
|
||||||
addToClassStorage(proto, nameResolver, source)
|
addToClassStorage(proto, nameResolver, source)
|
||||||
|
// collector.addJavaProto(ClassProtoData(proto, nameResolver))
|
||||||
dirtyOutputClassesMap.notDirty(jvmClassName)
|
dirtyOutputClassesMap.notDirty(jvmClassName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,7 +308,7 @@ open class IncrementalJvmCache(
|
|||||||
storage[key] = newData
|
storage[key] = newData
|
||||||
|
|
||||||
val packageFqName = kotlinClass.className.packageFqName
|
val packageFqName = kotlinClass.className.packageFqName
|
||||||
changesCollector.collectProtoChanges(oldData?.toProtoData(packageFqName), newData.toProtoData(packageFqName))
|
changesCollector.collectProtoChanges(oldData?.toProtoData(packageFqName), newData.toProtoData(packageFqName), packageProtoKey = key)
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun contains(className: JvmClassName): Boolean =
|
operator fun contains(className: JvmClassName): Boolean =
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ data class IncrementalModuleEntry(
|
|||||||
private val projectPath: String,
|
private val projectPath: String,
|
||||||
val name: String,
|
val name: String,
|
||||||
val buildDir: File,
|
val buildDir: File,
|
||||||
val buildHistoryFile: File
|
val buildHistoryFile: File,
|
||||||
|
val abiSnapshot: File
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
companion object {
|
companion object {
|
||||||
private const val serialVersionUID = 0L
|
private const val serialVersionUID = 0L
|
||||||
@@ -26,7 +27,9 @@ class IncrementalModuleInfo(
|
|||||||
val nameToModules: Map<String, Set<IncrementalModuleEntry>>,
|
val nameToModules: Map<String, Set<IncrementalModuleEntry>>,
|
||||||
val jarToClassListFile: Map<File, File>,
|
val jarToClassListFile: Map<File, File>,
|
||||||
// only for js and mpp
|
// only for js and mpp
|
||||||
val jarToModule: Map<File, IncrementalModuleEntry>
|
val jarToModule: Map<File, IncrementalModuleEntry>,
|
||||||
|
//for JVM only
|
||||||
|
val jarToAbiSnapshot: Map<File, File>
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
companion object {
|
companion object {
|
||||||
private const val serialVersionUID = 1L
|
private const val serialVersionUID = 1L
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ open class LookupStorage(
|
|||||||
private val countersFile = "counters".storageFile
|
private val countersFile = "counters".storageFile
|
||||||
private val idToFile = registerMap(IdToFileMap("id-to-file".storageFile, pathConverter))
|
private val idToFile = registerMap(IdToFileMap("id-to-file".storageFile, pathConverter))
|
||||||
private val fileToId = registerMap(FileToIdMap("file-to-id".storageFile, pathConverter))
|
private val fileToId = registerMap(FileToIdMap("file-to-id".storageFile, pathConverter))
|
||||||
private val lookupMap = registerMap(LookupMap("lookups".storageFile))
|
val lookupMap = registerMap(LookupMap("lookups".storageFile))
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
private var size: Int = 0
|
private var size: Int = 0
|
||||||
|
|||||||
@@ -255,22 +255,23 @@ fun withSubtypes(
|
|||||||
typeFqName: FqName,
|
typeFqName: FqName,
|
||||||
caches: Iterable<IncrementalCacheCommon>
|
caches: Iterable<IncrementalCacheCommon>
|
||||||
): Set<FqName> {
|
): Set<FqName> {
|
||||||
val types = LinkedHashSet(listOf(typeFqName))
|
val typesToProccess = LinkedHashSet(listOf(typeFqName))
|
||||||
val subtypes = hashSetOf<FqName>()
|
val proccessedTypes = hashSetOf<FqName>()
|
||||||
|
|
||||||
while (types.isNotEmpty()) {
|
|
||||||
val iterator = types.iterator()
|
while (typesToProccess.isNotEmpty()) {
|
||||||
|
val iterator = typesToProccess.iterator()
|
||||||
val unprocessedType = iterator.next()
|
val unprocessedType = iterator.next()
|
||||||
iterator.remove()
|
iterator.remove()
|
||||||
|
|
||||||
caches.asSequence()
|
caches.asSequence()
|
||||||
.flatMap { it.getSubtypesOf(unprocessedType) }
|
.flatMap { it.getSubtypesOf(unprocessedType) }
|
||||||
.filter { it !in subtypes }
|
.filter { it !in proccessedTypes }
|
||||||
.forEach { types.add(it) }
|
.forEach { typesToProccess.add(it) }
|
||||||
|
|
||||||
subtypes.add(unprocessedType)
|
proccessedTypes.add(unprocessedType)
|
||||||
}
|
}
|
||||||
|
|
||||||
return subtypes
|
return proccessedTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ package org.jetbrains.kotlin.incremental.storage
|
|||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
internal class LookupMap(storage: File) : BasicMap<LookupSymbolKey, Collection<Int>>(storage, LookupSymbolKeyDescriptor, IntCollectionExternalizer) {
|
class LookupMap(storage: File) : BasicMap<LookupSymbolKey, Collection<Int>>(storage, LookupSymbolKeyDescriptor, IntCollectionExternalizer) {
|
||||||
override fun dumpKey(key: LookupSymbolKey): String = key.toString()
|
override fun dumpKey(key: LookupSymbolKey): String = key.toString()
|
||||||
|
|
||||||
override fun dumpValue(value: Collection<Int>): String = value.toString()
|
override fun dumpValue(value: Collection<Int>): String = value.toString()
|
||||||
|
|||||||
@@ -21,23 +21,49 @@ import com.intellij.util.io.DataExternalizer
|
|||||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
import com.intellij.util.io.EnumeratorStringDescriptor
|
||||||
import com.intellij.util.io.IOUtil
|
import com.intellij.util.io.IOUtil
|
||||||
import com.intellij.util.io.KeyDescriptor
|
import com.intellij.util.io.KeyDescriptor
|
||||||
|
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
|
||||||
|
import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
||||||
import java.io.DataInput
|
import java.io.DataInput
|
||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
import java.io.DataOutput
|
import java.io.DataOutput
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage versioning:
|
||||||
|
* 0 - only name and value hashes are saved
|
||||||
|
* 1 - name and scope are saved
|
||||||
|
*/
|
||||||
object LookupSymbolKeyDescriptor : KeyDescriptor<LookupSymbolKey> {
|
object LookupSymbolKeyDescriptor : KeyDescriptor<LookupSymbolKey> {
|
||||||
override fun read(input: DataInput): LookupSymbolKey {
|
override fun read(input: DataInput): LookupSymbolKey {
|
||||||
val first = input.readInt()
|
val version = input.readByte()
|
||||||
val second = input.readInt()
|
return when (version.toInt()) {
|
||||||
|
0 -> {
|
||||||
return LookupSymbolKey(first, second)
|
val name = input.readUTF()
|
||||||
|
val scope = input.readUTF()
|
||||||
|
LookupSymbolKey(name.hashCode(), scope.hashCode(), name, scope)
|
||||||
|
}
|
||||||
|
1 -> {
|
||||||
|
val first = input.readInt()
|
||||||
|
val second = input.readInt()
|
||||||
|
LookupSymbolKey(first, second, "", "")
|
||||||
|
}
|
||||||
|
else -> throw RuntimeException("Unknown version of LookupSymbolKeyDescriptor=${version}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val storeFullFqName = CompilerSystemProperties.COMPILE_INCREMENTAL_WITH_CLASSPATH_SHAPSHOTS.value.toBooleanLenient() ?: false
|
||||||
|
|
||||||
override fun save(output: DataOutput, value: LookupSymbolKey) {
|
override fun save(output: DataOutput, value: LookupSymbolKey) {
|
||||||
output.writeInt(value.nameHash)
|
if (storeFullFqName) {
|
||||||
output.writeInt(value.scopeHash)
|
output.writeByte(0)
|
||||||
|
output.writeUTF(value.name)
|
||||||
|
output.writeUTF(value.scope)
|
||||||
|
} else {
|
||||||
|
output.writeByte(1)
|
||||||
|
output.writeInt(value.nameHash)
|
||||||
|
output.writeInt(value.scopeHash)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getHashCode(value: LookupSymbolKey): Int = value.hashCode()
|
override fun getHashCode(value: LookupSymbolKey): Int = value.hashCode()
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.incremental.storage
|
package org.jetbrains.kotlin.incremental.storage
|
||||||
|
|
||||||
data class LookupSymbolKey(val nameHash: Int, val scopeHash: Int) : Comparable<LookupSymbolKey> {
|
data class LookupSymbolKey(val nameHash: Int, val scopeHash: Int, val name:String, val scope:String) : Comparable<LookupSymbolKey> {
|
||||||
constructor(name: String, scope: String) : this(name.hashCode(), scope.hashCode())
|
constructor(name: String, scope: String) : this(name.hashCode(), scope.hashCode(), name, scope)
|
||||||
|
|
||||||
override fun compareTo(other: LookupSymbolKey): Int {
|
override fun compareTo(other: LookupSymbolKey): Int {
|
||||||
val nameCmp = nameHash.compareTo(other.nameHash)
|
val nameCmp = nameHash.compareTo(other.nameHash)
|
||||||
@@ -26,6 +26,26 @@ data class LookupSymbolKey(val nameHash: Int, val scopeHash: Int) : Comparable<L
|
|||||||
|
|
||||||
return scopeHash.compareTo(other.scopeHash)
|
return scopeHash.compareTo(other.scopeHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = nameHash
|
||||||
|
result = 31 * result + scopeHash
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as LookupSymbolKey
|
||||||
|
|
||||||
|
if (nameHash != other.nameHash) return false
|
||||||
|
if (scopeHash != other.scopeHash) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ProtoMapValue(val isPackageFacade: Boolean, val bytes: ByteArray, val strings: Array<String>)
|
data class ProtoMapValue(val isPackageFacade: Boolean, val bytes: ByteArray, val strings: Array<String>)
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ enum class CompilerSystemProperties(val property: String, val alwaysDirectAccess
|
|||||||
DAEMON_RMI_SOCKET_CONNECT_INTERVAL_PROPERTY("kotlin.daemon.socket.connect.interval"),
|
DAEMON_RMI_SOCKET_CONNECT_INTERVAL_PROPERTY("kotlin.daemon.socket.connect.interval"),
|
||||||
KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY("kotlin.environment.keepalive"),
|
KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY("kotlin.environment.keepalive"),
|
||||||
COMPILE_DAEMON_CUSTOM_RUN_FILES_PATH_FOR_TESTS("kotlin.daemon.custom.run.files.path.for.tests"),
|
COMPILE_DAEMON_CUSTOM_RUN_FILES_PATH_FOR_TESTS("kotlin.daemon.custom.run.files.path.for.tests"),
|
||||||
|
COMPILE_INCREMENTAL_WITH_CLASSPATH_SHAPSHOTS("kotlin.incremental.classpath.snapshot.enabled"),
|
||||||
KOTLIN_COLORS_ENABLED_PROPERTY("kotlin.colors.enabled"),
|
KOTLIN_COLORS_ENABLED_PROPERTY("kotlin.colors.enabled"),
|
||||||
|
|
||||||
KOTLIN_STAT_ENABLED_PROPERTY("kotlin.plugin.stat.enabled"),
|
KOTLIN_STAT_ENABLED_PROPERTY("kotlin.plugin.stat.enabled"),
|
||||||
@@ -48,7 +49,7 @@ enum class CompilerSystemProperties(val property: String, val alwaysDirectAccess
|
|||||||
USER_HOME("user.home", alwaysDirectAccess = true),
|
USER_HOME("user.home", alwaysDirectAccess = true),
|
||||||
JAVA_VERSION("java.specification.version", alwaysDirectAccess = true),
|
JAVA_VERSION("java.specification.version", alwaysDirectAccess = true),
|
||||||
JAVA_HOME("java.home", alwaysDirectAccess = true),
|
JAVA_HOME("java.home", alwaysDirectAccess = true),
|
||||||
JAVA_CLASS_PATH("java.class.path", alwaysDirectAccess = true),
|
JAVA_CLASS_PATH("java.class.path", alwaysDirectAccess = true)
|
||||||
;
|
;
|
||||||
|
|
||||||
private fun <T> getProperFunction(custom: T?, default: T): T {
|
private fun <T> getProperFunction(custom: T?, default: T): T {
|
||||||
|
|||||||
@@ -312,6 +312,8 @@ fun configureDaemonJVMOptions(opts: DaemonJVMOptions,
|
|||||||
if (inheritAdditionalProperties) {
|
if (inheritAdditionalProperties) {
|
||||||
CompilerSystemProperties.COMPILE_DAEMON_LOG_PATH_PROPERTY.value?.let { opts.jvmParams.add("D${CompilerSystemProperties.COMPILE_DAEMON_LOG_PATH_PROPERTY.property}=\"$it\"") }
|
CompilerSystemProperties.COMPILE_DAEMON_LOG_PATH_PROPERTY.value?.let { opts.jvmParams.add("D${CompilerSystemProperties.COMPILE_DAEMON_LOG_PATH_PROPERTY.property}=\"$it\"") }
|
||||||
CompilerSystemProperties.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY.value?.let { opts.jvmParams.add("D${CompilerSystemProperties.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY.property}") }
|
CompilerSystemProperties.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY.value?.let { opts.jvmParams.add("D${CompilerSystemProperties.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY.property}") }
|
||||||
|
//Temporary solution to test abi snapshot
|
||||||
|
CompilerSystemProperties.COMPILE_INCREMENTAL_WITH_CLASSPATH_SHAPSHOTS.value?.let { opts.jvmParams.add("D${CompilerSystemProperties.COMPILE_INCREMENTAL_WITH_CLASSPATH_SHAPSHOTS.property}") }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.jvmParams.none { it.matches(jvmAssertArgsRegex) }) {
|
if (opts.jvmParams.none { it.matches(jvmAssertArgsRegex) }) {
|
||||||
|
|||||||
+165
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.build.report.BuildReporter
|
||||||
|
import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl
|
||||||
|
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmNameResolver
|
||||||
|
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
|
||||||
|
import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.protobuf.MessageLite
|
||||||
|
import java.io.*
|
||||||
|
|
||||||
|
interface AbiSnapshot {
|
||||||
|
val protos: MutableMap<FqName, ProtoData>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AbiSnapshotImpl(override val protos: MutableMap<FqName, ProtoData>) : AbiSnapshot {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun ObjectInputStream.readStringArray(): Array<String> {
|
||||||
|
val size = readInt()
|
||||||
|
val stringArray = arrayOfNulls<String>(size)
|
||||||
|
repeat(size) {
|
||||||
|
stringArray[it] = readUTF()
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringArray.requireNoNulls()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun ObjectInputStream.readAbiSnapshot(): AbiSnapshotImpl {
|
||||||
|
// Format:
|
||||||
|
// numRecords: Int
|
||||||
|
// record {
|
||||||
|
// fqName
|
||||||
|
// isClassProtoData
|
||||||
|
// *for packageClassData - packageFqName
|
||||||
|
// protodata via JvmProtoBufUtil
|
||||||
|
// string array with size
|
||||||
|
// }
|
||||||
|
val size = readInt()
|
||||||
|
val mutableMap = hashMapOf<FqName, ProtoData>()
|
||||||
|
repeat(size) {
|
||||||
|
val fqNameString = readUTF()
|
||||||
|
val isClassProtoData = readBoolean()
|
||||||
|
if (isClassProtoData) {
|
||||||
|
val fqName = FqName(fqNameString)
|
||||||
|
val bytes = readStringArray()
|
||||||
|
val strings = readStringArray()
|
||||||
|
val (nameResolver, classProto) = JvmProtoBufUtil.readClassDataFrom(bytes, strings)
|
||||||
|
mutableMap[fqName] = ClassProtoData(classProto, nameResolver)
|
||||||
|
} else {
|
||||||
|
val fqName = FqName(fqNameString)
|
||||||
|
val packageFqName = FqName(readUTF())
|
||||||
|
val bytes = readStringArray()
|
||||||
|
val strings = readStringArray()
|
||||||
|
val (nameResolver, proto) = JvmProtoBufUtil.readPackageDataFrom(bytes, strings)
|
||||||
|
mutableMap[fqName] = PackagePartProtoData(proto, nameResolver, packageFqName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AbiSnapshotImpl(mutableMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ObjectOutputStream.writeStringArray(stringArray: Array<String>) {
|
||||||
|
writeInt(stringArray.size)
|
||||||
|
stringArray.forEach { writeUTF(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ObjectOutputStream.writeAbiSnapshot(abiSnapshot: AbiSnapshot) {
|
||||||
|
//TODO(valtman) temp solution while packageProto is not fully support
|
||||||
|
writeInt(abiSnapshot.protos.size)
|
||||||
|
for (entry in abiSnapshot.protos) {
|
||||||
|
writeUTF(entry.key.asString())
|
||||||
|
val protoData = entry.value
|
||||||
|
when (protoData) {
|
||||||
|
is ClassProtoData -> {
|
||||||
|
writeBoolean(true) //TODO(valtman) until PackageProto doesn't work
|
||||||
|
val nameResolver = protoData.nameResolver
|
||||||
|
when (nameResolver) {
|
||||||
|
is NameResolverImpl -> {
|
||||||
|
writeMessageWithNameResolverImpl(protoData.proto, nameResolver)
|
||||||
|
}
|
||||||
|
is JvmNameResolver -> {
|
||||||
|
writeMessageWithJvmNameResolver(protoData.proto, nameResolver)
|
||||||
|
}
|
||||||
|
else -> throw IllegalStateException("Can't store name resolver for class proto: ${nameResolver.javaClass}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PackagePartProtoData -> {
|
||||||
|
writeBoolean(false)
|
||||||
|
writeUTF(protoData.packageFqName.asString())
|
||||||
|
|
||||||
|
val nameResolver = protoData.nameResolver
|
||||||
|
when (nameResolver) {
|
||||||
|
is JvmNameResolver -> {
|
||||||
|
writeMessageWithJvmNameResolver(protoData.proto, nameResolver)
|
||||||
|
}
|
||||||
|
is NameResolverImpl -> {
|
||||||
|
writeMessageWithNameResolverImpl(protoData.proto, nameResolver)
|
||||||
|
}
|
||||||
|
else -> throw IllegalStateException("Can't store name resolver for package proto: ${nameResolver.javaClass}")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ObjectOutputStream.writeMessageWithNameResolverImpl(
|
||||||
|
message: MessageLite,
|
||||||
|
nameResolver: NameResolverImpl
|
||||||
|
) {
|
||||||
|
val stringTable = JvmStringTable()
|
||||||
|
repeat(nameResolver.strings.getStringCount()) {
|
||||||
|
stringTable.getStringIndex(nameResolver.getString(it))
|
||||||
|
}
|
||||||
|
repeat(nameResolver.qualifiedNames.qualifiedNameCount) {
|
||||||
|
stringTable.getQualifiedClassNameIndex(
|
||||||
|
nameResolver.getQualifiedClassName(it),
|
||||||
|
nameResolver.isLocalClassName(it)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val writeData = JvmProtoBufUtil.writeData(message, stringTable)
|
||||||
|
writeStringArray(writeData)
|
||||||
|
val size = nameResolver.strings.getStringCount()
|
||||||
|
writeInt(size)
|
||||||
|
repeat(size) {
|
||||||
|
val string = nameResolver.getString(it)
|
||||||
|
writeUTF(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ObjectOutputStream.writeMessageWithJvmNameResolver(
|
||||||
|
message: MessageLite,
|
||||||
|
nameResolver: JvmNameResolver
|
||||||
|
) {
|
||||||
|
val writeData = JvmProtoBufUtil.writeData(message, JvmStringTable(nameResolver))
|
||||||
|
writeStringArray(writeData)
|
||||||
|
writeStringArray(nameResolver.strings)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun write(buildInfo: AbiSnapshot, file: File) {
|
||||||
|
ObjectOutputStream(FileOutputStream(file)).use {
|
||||||
|
it.writeAbiSnapshot(buildInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun read(file: File, reporter: BuildReporter): AbiSnapshot? {
|
||||||
|
if (!file.exists()) {
|
||||||
|
reporter.report { "jar snapshot $file is found for jar" }
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectInputStream(FileInputStream(file)).use {
|
||||||
|
return it.readAbiSnapshot()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.incremental.ChangesCollector.Companion.getNonPrivateMemberNames
|
||||||
|
import org.jetbrains.kotlin.metadata.ProtoBuf.Visibility.PRIVATE
|
||||||
|
import org.jetbrains.kotlin.metadata.deserialization.Flags
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.resolve.sam.SAM_LOOKUP_NAME
|
||||||
|
|
||||||
|
//TODO(valtman) Should be in gradle daemon.
|
||||||
|
class AbiSnapshotDiffService() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
//Store list of changed lookups
|
||||||
|
private val diffCache: MutableMap<Pair<AbiSnapshot, AbiSnapshot>, DirtyData> = mutableMapOf()
|
||||||
|
|
||||||
|
//TODO(valtman) move out from Kotlin daemon
|
||||||
|
fun compareJarsInternal(
|
||||||
|
oldSnapshot: AbiSnapshot, newSnapshot: AbiSnapshot,
|
||||||
|
caches: IncrementalCacheCommon
|
||||||
|
) = diffCache.computeIfAbsent(Pair(oldSnapshot, newSnapshot)) { (snapshot, actual) -> doCompute(snapshot, actual, caches, emptyList()) }
|
||||||
|
|
||||||
|
fun inScope(fqName: FqName, scopes: Collection<String>) = scopes.any { scope -> fqName.toString().startsWith(scope) }
|
||||||
|
|
||||||
|
fun doCompute(snapshot: AbiSnapshot, actual: AbiSnapshot, caches: IncrementalCacheCommon, scopes: Collection<String>): DirtyData {
|
||||||
|
|
||||||
|
val dirtyFqNames = mutableListOf<FqName>()
|
||||||
|
val dirtyLookupSymbols = mutableListOf<LookupSymbol>()
|
||||||
|
|
||||||
|
for ((fqName, protoData) in snapshot.protos) {
|
||||||
|
if (!inScope(fqName, scopes)) continue
|
||||||
|
val newProtoData = actual.protos[fqName]
|
||||||
|
if (newProtoData == null) {
|
||||||
|
val (fqNames, symbols) = addProtoInfo(protoData, fqName)
|
||||||
|
dirtyFqNames.addAll(fqNames)
|
||||||
|
dirtyLookupSymbols.addAll(symbols)
|
||||||
|
} else {
|
||||||
|
if (protoData is ClassProtoData && newProtoData is ClassProtoData) {
|
||||||
|
ProtoCompareGenerated(
|
||||||
|
protoData.nameResolver, newProtoData.nameResolver,
|
||||||
|
protoData.proto.typeTable, newProtoData.proto.typeTable
|
||||||
|
)
|
||||||
|
val diff = DifferenceCalculatorForClass(protoData, newProtoData).difference()
|
||||||
|
|
||||||
|
if (diff.isClassAffected) {
|
||||||
|
//TODO(valtman) get cache to mark dirty all subtypes if subclass affected
|
||||||
|
// val fqNames = if (!diff.areSubclassesAffected) listOf(fqName) else withSubtypes(fqName, caches)
|
||||||
|
dirtyFqNames.add(fqName)
|
||||||
|
assert(!fqName.isRoot) { "$fqName is root" }
|
||||||
|
|
||||||
|
val scope = fqName.parent().asString()
|
||||||
|
val name = fqName.shortName().identifier
|
||||||
|
dirtyLookupSymbols.add(LookupSymbol(name, scope))
|
||||||
|
}
|
||||||
|
for (member in diff.changedMembersNames) {
|
||||||
|
//TODO(valtman) mark dirty symbols for subclasses
|
||||||
|
val subtypeFqNames = withSubtypes(fqName, listOf(caches))
|
||||||
|
dirtyFqNames.addAll(subtypeFqNames)
|
||||||
|
|
||||||
|
for (subtypeFqName in subtypeFqNames) {
|
||||||
|
dirtyLookupSymbols.add(LookupSymbol(member, subtypeFqName.asString()))
|
||||||
|
dirtyLookupSymbols.add(LookupSymbol(SAM_LOOKUP_NAME.asString(), subtypeFqName.asString()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (protoData is PackagePartProtoData && newProtoData is PackagePartProtoData) {
|
||||||
|
val diff = DifferenceCalculatorForPackageFacade(protoData, newProtoData).difference()
|
||||||
|
for (member in diff.changedMembersNames) {
|
||||||
|
dirtyLookupSymbols.add(LookupSymbol(member, fqName.asString()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//TODO(valtman) is it a valid case
|
||||||
|
throw IllegalStateException("package proto and class proto have the same fqName: $fqName")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fqNames.addAll(snapshot.protos.keys.removeAll(actual.protos.keys))
|
||||||
|
DirtyData(dirtyLookupSymbols, dirtyFqNames)
|
||||||
|
// .removeAll(actual.protos.keys)
|
||||||
|
val oldFqNames = snapshot.protos.keys
|
||||||
|
dirtyFqNames.addAll(actual.protos.keys.filter { !oldFqNames.contains(it) })
|
||||||
|
return DirtyData(dirtyLookupSymbols, dirtyFqNames)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO(valtman) change to return type
|
||||||
|
private fun addProtoInfo(
|
||||||
|
protoData: ProtoData,
|
||||||
|
fqName: FqName,
|
||||||
|
) : Pair<List<FqName>, List<LookupSymbol>>{
|
||||||
|
val fqNames = ArrayList<FqName>()
|
||||||
|
val symbols = ArrayList<LookupSymbol>()
|
||||||
|
when (protoData) {
|
||||||
|
is ClassProtoData -> {
|
||||||
|
fqNames.add(fqName)
|
||||||
|
symbols.addAll(protoData.getNonPrivateMemberNames().map { LookupSymbol(it, fqName.asString()) })
|
||||||
|
}
|
||||||
|
is PackagePartProtoData -> {
|
||||||
|
symbols.addAll(
|
||||||
|
protoData.proto.functionOrBuilderList.filterNot { Flags.VISIBILITY.get(it.flags) == PRIVATE }
|
||||||
|
.map { LookupSymbol(protoData.nameResolver.getString(it.name), fqName.asString()) }.toSet()
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Pair(fqNames, symbols)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
-16
@@ -95,6 +95,23 @@ data class BuildDiffsStorage(val buildDiffs: List<BuildDifference>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun ObjectInputStream.readDirtyData(): DirtyData {
|
private fun ObjectInputStream.readDirtyData(): DirtyData {
|
||||||
|
val lookupSymbols = readLookups()
|
||||||
|
val dirtyClassesFqNames = readFqNames()
|
||||||
|
|
||||||
|
return DirtyData(lookupSymbols, dirtyClassesFqNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ObjectInputStream.readFqNames(): ArrayList<FqName> {
|
||||||
|
val dirtyClassesSize = readInt()
|
||||||
|
val dirtyClassesFqNames = ArrayList<FqName>(dirtyClassesSize)
|
||||||
|
repeat(dirtyClassesSize) {
|
||||||
|
val fqNameString = readUTF()
|
||||||
|
dirtyClassesFqNames.add(FqName(fqNameString))
|
||||||
|
}
|
||||||
|
return dirtyClassesFqNames
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ObjectInputStream.readLookups(): ArrayList<LookupSymbol> {
|
||||||
val lookupSymbolSize = readInt()
|
val lookupSymbolSize = readInt()
|
||||||
val lookupSymbols = ArrayList<LookupSymbol>(lookupSymbolSize)
|
val lookupSymbols = ArrayList<LookupSymbol>(lookupSymbolSize)
|
||||||
repeat(lookupSymbolSize) {
|
repeat(lookupSymbolSize) {
|
||||||
@@ -102,30 +119,27 @@ data class BuildDiffsStorage(val buildDiffs: List<BuildDifference>) {
|
|||||||
val scope = readUTF()
|
val scope = readUTF()
|
||||||
lookupSymbols.add(LookupSymbol(name = name, scope = scope))
|
lookupSymbols.add(LookupSymbol(name = name, scope = scope))
|
||||||
}
|
}
|
||||||
|
return lookupSymbols
|
||||||
val dirtyClassesSize = readInt()
|
|
||||||
val dirtyClassesFqNames = ArrayList<FqName>(dirtyClassesSize)
|
|
||||||
repeat(dirtyClassesSize) {
|
|
||||||
val fqNameString = readUTF()
|
|
||||||
dirtyClassesFqNames.add(FqName(fqNameString))
|
|
||||||
}
|
|
||||||
|
|
||||||
return DirtyData(lookupSymbols, dirtyClassesFqNames)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ObjectOutputStream.writeDirtyData(dirtyData: DirtyData) {
|
private fun ObjectOutputStream.writeDirtyData(dirtyData: DirtyData) {
|
||||||
val lookupSymbols = dirtyData.dirtyLookupSymbols
|
writeLookups(dirtyData.dirtyLookupSymbols)
|
||||||
|
writeFqNames(dirtyData.dirtyClassesFqNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ObjectOutputStream.writeFqNames(dirtyClassesFqNames: Collection<FqName> ) {
|
||||||
|
writeInt(dirtyClassesFqNames.size)
|
||||||
|
for (fqName in dirtyClassesFqNames) {
|
||||||
|
writeUTF(fqName.asString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ObjectOutputStream.writeLookups(lookupSymbols: Collection<LookupSymbol>) {
|
||||||
writeInt(lookupSymbols.size)
|
writeInt(lookupSymbols.size)
|
||||||
for ((name, scope) in lookupSymbols) {
|
for ((name, scope) in lookupSymbols) {
|
||||||
writeUTF(name)
|
writeUTF(name)
|
||||||
writeUTF(scope)
|
writeUTF(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
val dirtyClassesFqNames = dirtyData.dirtyClassesFqNames
|
|
||||||
writeInt(dirtyClassesFqNames.size)
|
|
||||||
for (fqName in dirtyClassesFqNames) {
|
|
||||||
writeUTF(fqName.asString())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal const val MAX_DIFFS_ENTRIES: Int = 10
|
internal const val MAX_DIFFS_ENTRIES: Int = 10
|
||||||
|
|||||||
+26
-3
@@ -16,14 +16,37 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.incremental
|
package org.jetbrains.kotlin.incremental
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.incremental.AbiSnapshotImpl.Companion.readAbiSnapshot
|
||||||
|
import org.jetbrains.kotlin.incremental.AbiSnapshotImpl.Companion.writeAbiSnapshot
|
||||||
import java.io.*
|
import java.io.*
|
||||||
|
|
||||||
data class BuildInfo(val startTS: Long) : Serializable {
|
data class BuildInfo(val startTS: Long, val dependencyToAbiSnapshot: Map<String, AbiSnapshot> = mapOf()) : Serializable {
|
||||||
companion object {
|
companion object {
|
||||||
|
private fun ObjectInputStream.readBuildInfo() : BuildInfo {
|
||||||
|
val ts = readLong()
|
||||||
|
val size = readInt()
|
||||||
|
val abiSnapshots = HashMap<String, AbiSnapshot>(size)
|
||||||
|
repeat(size) {
|
||||||
|
val identifier = readUTF()
|
||||||
|
val snapshot = readAbiSnapshot()
|
||||||
|
abiSnapshots.put(identifier, snapshot)
|
||||||
|
}
|
||||||
|
return BuildInfo(ts, abiSnapshots)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ObjectOutputStream.writeBuildInfo(buildInfo: BuildInfo) {
|
||||||
|
writeLong(buildInfo.startTS)
|
||||||
|
writeInt(buildInfo.dependencyToAbiSnapshot.size)
|
||||||
|
for((identifier, abiSnapshot) in buildInfo.dependencyToAbiSnapshot) {
|
||||||
|
writeUTF(identifier)
|
||||||
|
writeAbiSnapshot(abiSnapshot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun read(file: File): BuildInfo? =
|
fun read(file: File): BuildInfo? =
|
||||||
try {
|
try {
|
||||||
ObjectInputStream(FileInputStream(file)).use {
|
ObjectInputStream(FileInputStream(file)).use {
|
||||||
it.readObject() as BuildInfo
|
it.readBuildInfo()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
null
|
null
|
||||||
@@ -31,7 +54,7 @@ data class BuildInfo(val startTS: Long) : Serializable {
|
|||||||
|
|
||||||
fun write(buildInfo: BuildInfo, file: File) {
|
fun write(buildInfo: BuildInfo, file: File) {
|
||||||
ObjectOutputStream(FileOutputStream(file)).use {
|
ObjectOutputStream(FileOutputStream(file)).use {
|
||||||
it.writeObject(buildInfo)
|
it.writeBuildInfo(buildInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -87,7 +87,7 @@ class IncrementalJvmCachesManager(
|
|||||||
|
|
||||||
class IncrementalJsCachesManager(
|
class IncrementalJsCachesManager(
|
||||||
cachesRootDir: File,
|
cachesRootDir: File,
|
||||||
rootProjectDir: File,
|
rootProjectDir: File?,
|
||||||
reporter: ICReporter,
|
reporter: ICReporter,
|
||||||
serializerProtocol: SerializerExtensionProtocol
|
serializerProtocol: SerializerExtensionProtocol
|
||||||
) : IncrementalCachesManager<IncrementalJsCache>(cachesRootDir, rootProjectDir, reporter) {
|
) : IncrementalCachesManager<IncrementalJsCache>(cachesRootDir, rootProjectDir, reporter) {
|
||||||
|
|||||||
+91
-13
@@ -22,9 +22,11 @@ import org.jetbrains.kotlin.build.report.BuildReporter
|
|||||||
import org.jetbrains.kotlin.build.report.metrics.BuildTime
|
import org.jetbrains.kotlin.build.report.metrics.BuildTime
|
||||||
import org.jetbrains.kotlin.build.report.metrics.BuildAttribute
|
import org.jetbrains.kotlin.build.report.metrics.BuildAttribute
|
||||||
import org.jetbrains.kotlin.build.report.metrics.measure
|
import org.jetbrains.kotlin.build.report.metrics.measure
|
||||||
|
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
|
||||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||||
|
import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
||||||
import org.jetbrains.kotlin.compilerRunner.MessageCollectorToOutputItemsCollectorAdapter
|
import org.jetbrains.kotlin.compilerRunner.MessageCollectorToOutputItemsCollectorAdapter
|
||||||
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
|
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
|
||||||
import org.jetbrains.kotlin.compilerRunner.SimpleOutputItem
|
import org.jetbrains.kotlin.compilerRunner.SimpleOutputItem
|
||||||
@@ -39,6 +41,7 @@ import org.jetbrains.kotlin.progress.CompilationCanceledStatus
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
abstract class IncrementalCompilerRunner<
|
abstract class IncrementalCompilerRunner<
|
||||||
Args : CommonCompilerArguments,
|
Args : CommonCompilerArguments,
|
||||||
@@ -56,8 +59,12 @@ abstract class IncrementalCompilerRunner<
|
|||||||
protected val cacheDirectory = File(workingDir, cacheDirName)
|
protected val cacheDirectory = File(workingDir, cacheDirName)
|
||||||
private val dirtySourcesSinceLastTimeFile = File(workingDir, DIRTY_SOURCES_FILE_NAME)
|
private val dirtySourcesSinceLastTimeFile = File(workingDir, DIRTY_SOURCES_FILE_NAME)
|
||||||
protected val lastBuildInfoFile = File(workingDir, LAST_BUILD_INFO_FILE_NAME)
|
protected val lastBuildInfoFile = File(workingDir, LAST_BUILD_INFO_FILE_NAME)
|
||||||
|
protected val abiSnapshotFile = File(workingDir, ABI_SNAPSHOT_FILE_NAME)
|
||||||
protected open val kotlinSourceFilesExtensions: List<String> = DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
|
protected open val kotlinSourceFilesExtensions: List<String> = DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
|
||||||
|
|
||||||
|
//TODO(valtman) temporal measure to ensure quick disable, should be deleted after successful release
|
||||||
|
protected val withSnapshot: Boolean = CompilerSystemProperties.COMPILE_INCREMENTAL_WITH_CLASSPATH_SHAPSHOTS.value.toBooleanLenient() ?: false
|
||||||
|
|
||||||
protected abstract fun isICEnabled(): Boolean
|
protected abstract fun isICEnabled(): Boolean
|
||||||
protected abstract fun createCacheManager(args: Args, projectDir: File?): CacheManager
|
protected abstract fun createCacheManager(args: Args, projectDir: File?): CacheManager
|
||||||
protected abstract fun destinationDir(args: Args): File
|
protected abstract fun destinationDir(args: Args): File
|
||||||
@@ -84,6 +91,19 @@ abstract class IncrementalCompilerRunner<
|
|||||||
assert(isICEnabled()) { "Incremental compilation is not enabled" }
|
assert(isICEnabled()) { "Incremental compilation is not enabled" }
|
||||||
var caches = createCacheManager(args, projectDir)
|
var caches = createCacheManager(args, projectDir)
|
||||||
|
|
||||||
|
if (withSnapshot) {
|
||||||
|
reporter.report { "Incremental compilation with ABI snapshot enabled" }
|
||||||
|
}
|
||||||
|
//TODO if abi-snapshot is corrupted unable to rebuild. Should roll back to withSnapshot = false?
|
||||||
|
val classpathAbiSnapshot =
|
||||||
|
if (withSnapshot) {
|
||||||
|
reporter.measure(BuildTime.SET_UP_ABI_SNAPSHOTS) {
|
||||||
|
setupJarDependencies(args, withSnapshot, reporter)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emptyMap()
|
||||||
|
}
|
||||||
|
|
||||||
fun rebuild(reason: BuildAttribute): ExitCode {
|
fun rebuild(reason: BuildAttribute): ExitCode {
|
||||||
reporter.report { "Non-incremental compilation will be performed: $reason" }
|
reporter.report { "Non-incremental compilation will be performed: $reason" }
|
||||||
caches.close(false)
|
caches.close(false)
|
||||||
@@ -96,7 +116,8 @@ abstract class IncrementalCompilerRunner<
|
|||||||
caches.inputsCache.sourceSnapshotMap.compareAndUpdate(allSourceFiles)
|
caches.inputsCache.sourceSnapshotMap.compareAndUpdate(allSourceFiles)
|
||||||
}
|
}
|
||||||
val allKotlinFiles = allSourceFiles.filter { it.isKotlinFile(kotlinSourceFilesExtensions) }
|
val allKotlinFiles = allSourceFiles.filter { it.isKotlinFile(kotlinSourceFilesExtensions) }
|
||||||
return compileIncrementally(args, caches, allKotlinFiles, CompilationMode.Rebuild(reason), messageCollector)
|
return compileIncrementally(args, caches, allKotlinFiles, CompilationMode.Rebuild(reason), messageCollector, withSnapshot,
|
||||||
|
classpathAbiSnapshot = classpathAbiSnapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If compilation has crashed or we failed to close caches we have to clear them
|
// If compilation has crashed or we failed to close caches we have to clear them
|
||||||
@@ -114,11 +135,36 @@ abstract class IncrementalCompilerRunner<
|
|||||||
else -> providedChangedFiles
|
else -> providedChangedFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
val compilationMode = sourcesToCompile(caches, changedFiles, args, messageCollector)
|
|
||||||
|
val compilationMode = sourcesToCompile(caches, changedFiles, args, messageCollector, classpathAbiSnapshot)
|
||||||
|
|
||||||
val exitCode = when (compilationMode) {
|
val exitCode = when (compilationMode) {
|
||||||
is CompilationMode.Incremental -> {
|
is CompilationMode.Incremental -> {
|
||||||
compileIncrementally(args, caches, allSourceFiles, compilationMode, messageCollector)
|
if (withSnapshot) {
|
||||||
|
val abiSnapshot = AbiSnapshotImpl.read(abiSnapshotFile, reporter)
|
||||||
|
if (abiSnapshot != null) {
|
||||||
|
compileIncrementally(
|
||||||
|
args,
|
||||||
|
caches,
|
||||||
|
allSourceFiles,
|
||||||
|
compilationMode,
|
||||||
|
messageCollector,
|
||||||
|
withSnapshot,
|
||||||
|
abiSnapshot,
|
||||||
|
classpathAbiSnapshot
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
rebuild(BuildAttribute.NO_ABI_SNAPSHOT)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
compileIncrementally(
|
||||||
|
args,
|
||||||
|
caches,
|
||||||
|
allSourceFiles,
|
||||||
|
compilationMode,
|
||||||
|
messageCollector,
|
||||||
|
withSnapshot)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
is CompilationMode.Rebuild -> {
|
is CompilationMode.Rebuild -> {
|
||||||
rebuild(compilationMode.reason)
|
rebuild(compilationMode.reason)
|
||||||
@@ -173,28 +219,33 @@ abstract class IncrementalCompilerRunner<
|
|||||||
caches: CacheManager,
|
caches: CacheManager,
|
||||||
changedFiles: ChangedFiles,
|
changedFiles: ChangedFiles,
|
||||||
args: Args,
|
args: Args,
|
||||||
messageCollector: MessageCollector
|
messageCollector: MessageCollector,
|
||||||
|
dependenciesAbiSnapshots: Map<String, AbiSnapshot>
|
||||||
): CompilationMode =
|
): CompilationMode =
|
||||||
when (changedFiles) {
|
when (changedFiles) {
|
||||||
is ChangedFiles.Known -> calculateSourcesToCompile(caches, changedFiles, args, messageCollector)
|
is ChangedFiles.Known -> calculateSourcesToCompile(caches, changedFiles, args, messageCollector, dependenciesAbiSnapshots)
|
||||||
is ChangedFiles.Unknown -> CompilationMode.Rebuild(BuildAttribute.UNKNOWN_CHANGES_IN_GRADLE_INPUTS)
|
is ChangedFiles.Unknown -> CompilationMode.Rebuild(BuildAttribute.UNKNOWN_CHANGES_IN_GRADLE_INPUTS)
|
||||||
is ChangedFiles.Dependencies -> error("Unexpected ChangedFiles type (ChangedFiles.Dependencies)")
|
is ChangedFiles.Dependencies -> error("Unexpected ChangedFiles type (ChangedFiles.Dependencies)")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun calculateSourcesToCompile(
|
private fun calculateSourcesToCompile(
|
||||||
caches: CacheManager, changedFiles: ChangedFiles.Known, args: Args, messageCollector: MessageCollector
|
caches: CacheManager, changedFiles: ChangedFiles.Known, args: Args, messageCollector: MessageCollector,
|
||||||
|
abiSnapshots: Map<String, AbiSnapshot>
|
||||||
): CompilationMode =
|
): CompilationMode =
|
||||||
reporter.measure(BuildTime.IC_CALCULATE_INITIAL_DIRTY_SET) {
|
reporter.measure(BuildTime.IC_CALCULATE_INITIAL_DIRTY_SET) {
|
||||||
calculateSourcesToCompileImpl(caches, changedFiles, args, messageCollector)
|
calculateSourcesToCompileImpl(caches, changedFiles, args, messageCollector, abiSnapshots)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun calculateSourcesToCompileImpl(
|
protected abstract fun calculateSourcesToCompileImpl(
|
||||||
caches: CacheManager,
|
caches: CacheManager,
|
||||||
changedFiles: ChangedFiles.Known,
|
changedFiles: ChangedFiles.Known,
|
||||||
args: Args,
|
args: Args,
|
||||||
messageCollector: MessageCollector
|
messageCollector: MessageCollector,
|
||||||
|
classpathAbiSnapshots: Map<String, AbiSnapshot>
|
||||||
): CompilationMode
|
): CompilationMode
|
||||||
|
|
||||||
|
protected open fun setupJarDependencies(args: Args, withSnapshot: Boolean, reporter: BuildReporter): Map<String, AbiSnapshot> = mapOf()
|
||||||
|
|
||||||
protected fun initDirtyFiles(dirtyFiles: DirtyFilesContainer, changedFiles: ChangedFiles.Known) {
|
protected fun initDirtyFiles(dirtyFiles: DirtyFilesContainer, changedFiles: ChangedFiles.Known) {
|
||||||
dirtyFiles.add(changedFiles.modified, "was modified since last time")
|
dirtyFiles.add(changedFiles.modified, "was modified since last time")
|
||||||
dirtyFiles.add(changedFiles.removed, "was removed since last time")
|
dirtyFiles.add(changedFiles.removed, "was removed since last time")
|
||||||
@@ -251,7 +302,10 @@ abstract class IncrementalCompilerRunner<
|
|||||||
caches: CacheManager,
|
caches: CacheManager,
|
||||||
allKotlinSources: List<File>,
|
allKotlinSources: List<File>,
|
||||||
compilationMode: CompilationMode,
|
compilationMode: CompilationMode,
|
||||||
originalMessageCollector: MessageCollector
|
originalMessageCollector: MessageCollector,
|
||||||
|
withSnapshot: Boolean,
|
||||||
|
abiSnapshot: AbiSnapshot = AbiSnapshotImpl(mutableMapOf()),
|
||||||
|
classpathAbiSnapshot: Map<String, AbiSnapshot> = HashMap()
|
||||||
): ExitCode {
|
): ExitCode {
|
||||||
preBuildHook(args, compilationMode)
|
preBuildHook(args, compilationMode)
|
||||||
|
|
||||||
@@ -268,7 +322,7 @@ abstract class IncrementalCompilerRunner<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentBuildInfo = BuildInfo(startTS = System.currentTimeMillis())
|
val currentBuildInfo = BuildInfo(startTS = System.currentTimeMillis(), classpathAbiSnapshot)
|
||||||
val buildDirtyLookupSymbols = HashSet<LookupSymbol>()
|
val buildDirtyLookupSymbols = HashSet<LookupSymbol>()
|
||||||
val buildDirtyFqNames = HashSet<FqName>()
|
val buildDirtyFqNames = HashSet<FqName>()
|
||||||
val allDirtySources = HashSet<File>()
|
val allDirtySources = HashSet<File>()
|
||||||
@@ -283,6 +337,7 @@ abstract class IncrementalCompilerRunner<
|
|||||||
|
|
||||||
val lookupTracker = LookupTrackerImpl(LookupTracker.DO_NOTHING)
|
val lookupTracker = LookupTrackerImpl(LookupTracker.DO_NOTHING)
|
||||||
val expectActualTracker = ExpectActualTrackerImpl()
|
val expectActualTracker = ExpectActualTrackerImpl()
|
||||||
|
//TODO(valtman) sourceToCompile calculate based on abiSnapshot
|
||||||
val (sourcesToCompile, removedKotlinSources) = dirtySources.partition(File::exists)
|
val (sourcesToCompile, removedKotlinSources) = dirtySources.partition(File::exists)
|
||||||
|
|
||||||
allDirtySources.addAll(dirtySources)
|
allDirtySources.addAll(dirtySources)
|
||||||
@@ -327,9 +382,15 @@ abstract class IncrementalCompilerRunner<
|
|||||||
caches.platformCache.updateComplementaryFiles(dirtySources, expectActualTracker)
|
caches.platformCache.updateComplementaryFiles(dirtySources, expectActualTracker)
|
||||||
caches.inputsCache.registerOutputForSourceFiles(generatedFiles)
|
caches.inputsCache.registerOutputForSourceFiles(generatedFiles)
|
||||||
caches.lookupCache.update(lookupTracker, sourcesToCompile, removedKotlinSources)
|
caches.lookupCache.update(lookupTracker, sourcesToCompile, removedKotlinSources)
|
||||||
updateCaches(services, caches, generatedFiles, changesCollector)
|
updateCaches(services, caches, generatedFiles, changesCollector)
|
||||||
|
|
||||||
|
}
|
||||||
|
if (compilationMode is CompilationMode.Rebuild) {
|
||||||
|
if (withSnapshot) {
|
||||||
|
abiSnapshot.protos.putAll(changesCollector.protoDataChanges())
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
if (compilationMode is CompilationMode.Rebuild) break
|
|
||||||
|
|
||||||
val (dirtyLookupSymbols, dirtyClassFqNames, forceRecompile) = changesCollector.getDirtyData(listOf(caches.platformCache), reporter)
|
val (dirtyLookupSymbols, dirtyClassFqNames, forceRecompile) = changesCollector.getDirtyData(listOf(caches.platformCache), reporter)
|
||||||
val compiledInThisIterationSet = sourcesToCompile.toHashSet()
|
val compiledInThisIterationSet = sourcesToCompile.toHashSet()
|
||||||
@@ -353,10 +414,25 @@ abstract class IncrementalCompilerRunner<
|
|||||||
|
|
||||||
buildDirtyLookupSymbols.addAll(dirtyLookupSymbols)
|
buildDirtyLookupSymbols.addAll(dirtyLookupSymbols)
|
||||||
buildDirtyFqNames.addAll(dirtyClassFqNames)
|
buildDirtyFqNames.addAll(dirtyClassFqNames)
|
||||||
|
|
||||||
|
//update
|
||||||
|
if (withSnapshot) {
|
||||||
|
//TODO(valtman) check method/ kts class remove
|
||||||
|
changesCollector.protoDataRemoved().forEach { abiSnapshot.protos.remove(it) }
|
||||||
|
abiSnapshot.protos.putAll(changesCollector.protoDataChanges())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exitCode == ExitCode.OK) {
|
if (exitCode == ExitCode.OK) {
|
||||||
BuildInfo.write(currentBuildInfo, lastBuildInfoFile)
|
reporter.measure(BuildTime.STORE_BUILD_INFO) {
|
||||||
|
BuildInfo.write(currentBuildInfo, lastBuildInfoFile)
|
||||||
|
|
||||||
|
//write abi snapshot
|
||||||
|
if (withSnapshot) {
|
||||||
|
//TODO(valtman) check method/class remove
|
||||||
|
AbiSnapshotImpl.write(abiSnapshot, abiSnapshotFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (exitCode == ExitCode.OK && compilationMode is CompilationMode.Incremental) {
|
if (exitCode == ExitCode.OK && compilationMode is CompilationMode.Incremental) {
|
||||||
buildDirtyLookupSymbols.addAll(additionalDirtyLookupSymbols())
|
buildDirtyLookupSymbols.addAll(additionalDirtyLookupSymbols())
|
||||||
@@ -407,12 +483,14 @@ abstract class IncrementalCompilerRunner<
|
|||||||
BuildDifference(currentBuildInfo.startTS, false, emptyDirtyData)
|
BuildDifference(currentBuildInfo.startTS, false, emptyDirtyData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO(valtman) old history build should be restored in case of build fail
|
||||||
BuildDiffsStorage.writeToFile(buildHistoryFile, BuildDiffsStorage(prevDiffs + newDiff), reporter)
|
BuildDiffsStorage.writeToFile(buildHistoryFile, BuildDiffsStorage(prevDiffs + newDiff), reporter)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val DIRTY_SOURCES_FILE_NAME = "dirty-sources.txt"
|
const val DIRTY_SOURCES_FILE_NAME = "dirty-sources.txt"
|
||||||
const val LAST_BUILD_INFO_FILE_NAME = "last-build.bin"
|
const val LAST_BUILD_INFO_FILE_NAME = "last-build.bin"
|
||||||
|
const val ABI_SNAPSHOT_FILE_NAME = "abi-snapshot.bin"
|
||||||
}
|
}
|
||||||
|
|
||||||
private object EmptyCompilationCanceledStatus : CompilationCanceledStatus {
|
private object EmptyCompilationCanceledStatus : CompilationCanceledStatus {
|
||||||
|
|||||||
+10
-4
@@ -75,7 +75,7 @@ inline fun <R> withJsIC(fn: () -> R): R {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class IncrementalJsCompilerRunner(
|
class IncrementalJsCompilerRunner(
|
||||||
private val workingDir: File,
|
workingDir: File,
|
||||||
reporter: BuildReporter,
|
reporter: BuildReporter,
|
||||||
buildHistoryFile: File,
|
buildHistoryFile: File,
|
||||||
private val modulesApiHistory: ModulesApiHistory,
|
private val modulesApiHistory: ModulesApiHistory,
|
||||||
@@ -91,7 +91,7 @@ class IncrementalJsCompilerRunner(
|
|||||||
|
|
||||||
override fun createCacheManager(args: K2JSCompilerArguments, projectDir: File?): IncrementalJsCachesManager {
|
override fun createCacheManager(args: K2JSCompilerArguments, projectDir: File?): IncrementalJsCachesManager {
|
||||||
val serializerProtocol = if (!args.isIrBackendEnabled()) JsSerializerProtocol else KlibMetadataSerializerProtocol
|
val serializerProtocol = if (!args.isIrBackendEnabled()) JsSerializerProtocol else KlibMetadataSerializerProtocol
|
||||||
return IncrementalJsCachesManager(cacheDirectory, workingDir, reporter, serializerProtocol)
|
return IncrementalJsCachesManager(cacheDirectory, projectDir, reporter, serializerProtocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun destinationDir(args: K2JSCompilerArguments): File =
|
override fun destinationDir(args: K2JSCompilerArguments): File =
|
||||||
@@ -101,7 +101,8 @@ class IncrementalJsCompilerRunner(
|
|||||||
caches: IncrementalJsCachesManager,
|
caches: IncrementalJsCachesManager,
|
||||||
changedFiles: ChangedFiles.Known,
|
changedFiles: ChangedFiles.Known,
|
||||||
args: K2JSCompilerArguments,
|
args: K2JSCompilerArguments,
|
||||||
messageCollector: MessageCollector
|
messageCollector: MessageCollector,
|
||||||
|
classpathAbiSnapshots: Map<String, AbiSnapshot> //Ignore for now
|
||||||
): CompilationMode {
|
): CompilationMode {
|
||||||
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile)
|
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile)
|
||||||
?: return CompilationMode.Rebuild(BuildAttribute.NO_BUILD_HISTORY)
|
?: return CompilationMode.Rebuild(BuildAttribute.NO_BUILD_HISTORY)
|
||||||
@@ -110,7 +111,12 @@ class IncrementalJsCompilerRunner(
|
|||||||
initDirtyFiles(dirtyFiles, changedFiles)
|
initDirtyFiles(dirtyFiles, changedFiles)
|
||||||
|
|
||||||
val libs = (args.libraries ?: "").split(File.pathSeparator).map { File(it) }
|
val libs = (args.libraries ?: "").split(File.pathSeparator).map { File(it) }
|
||||||
val classpathChanges = getClasspathChanges(libs, changedFiles, lastBuildInfo, modulesApiHistory, reporter)
|
//TODO(valtman) check for JS
|
||||||
|
val classpathChanges = getClasspathChanges(
|
||||||
|
libs, changedFiles, lastBuildInfo, modulesApiHistory, reporter,
|
||||||
|
mapOf(), false, caches.platformCache,
|
||||||
|
caches.lookupCache.lookupMap.keys.map { if (it.scope.isBlank()) it.name else it.scope }.distinct()
|
||||||
|
)
|
||||||
|
|
||||||
@Suppress("UNUSED_VARIABLE") // for sealed when
|
@Suppress("UNUSED_VARIABLE") // for sealed when
|
||||||
val unused = when (classpathChanges) {
|
val unused = when (classpathChanges) {
|
||||||
|
|||||||
+31
-5
@@ -45,6 +45,7 @@ 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.util.BufferingMessageCollector
|
import org.jetbrains.kotlin.incremental.util.BufferingMessageCollector
|
||||||
|
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
|
||||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
|
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
|
||||||
@@ -81,6 +82,7 @@ fun makeIncrementally(
|
|||||||
modulesApiHistory = EmptyModulesApiHistory,
|
modulesApiHistory = EmptyModulesApiHistory,
|
||||||
kotlinSourceFilesExtensions = kotlinExtensions
|
kotlinSourceFilesExtensions = kotlinExtensions
|
||||||
)
|
)
|
||||||
|
//TODO set properly
|
||||||
compiler.compile(sourceFiles, args, messageCollector, providedChangedFiles = null)
|
compiler.compile(sourceFiles, args, messageCollector, providedChangedFiles = null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,7 +127,6 @@ class IncrementalJvmCompilerRunner(
|
|||||||
override fun isICEnabled(): Boolean =
|
override fun isICEnabled(): Boolean =
|
||||||
IncrementalCompilation.isEnabledForJvm()
|
IncrementalCompilation.isEnabledForJvm()
|
||||||
|
|
||||||
//TODO
|
|
||||||
override fun createCacheManager(args: K2JVMCompilerArguments, projectDir: File?): IncrementalJvmCachesManager =
|
override fun createCacheManager(args: K2JVMCompilerArguments, projectDir: File?): IncrementalJvmCachesManager =
|
||||||
IncrementalJvmCachesManager(cacheDirectory, projectDir, File(args.destination), reporter)
|
IncrementalJvmCachesManager(cacheDirectory, projectDir, File(args.destination), reporter)
|
||||||
|
|
||||||
@@ -163,20 +164,43 @@ class IncrementalJvmCompilerRunner(
|
|||||||
caches: IncrementalJvmCachesManager,
|
caches: IncrementalJvmCachesManager,
|
||||||
changedFiles: ChangedFiles.Known,
|
changedFiles: ChangedFiles.Known,
|
||||||
args: K2JVMCompilerArguments,
|
args: K2JVMCompilerArguments,
|
||||||
messageCollector: MessageCollector
|
messageCollector: MessageCollector,
|
||||||
|
classpathAbiSnapshots: Map<String, AbiSnapshot>
|
||||||
): CompilationMode {
|
): CompilationMode {
|
||||||
return try {
|
return try {
|
||||||
calculateSourcesToCompileImpl(caches, changedFiles, args)
|
calculateSourcesToCompileImpl(caches, changedFiles, args, classpathAbiSnapshots)
|
||||||
} finally {
|
} finally {
|
||||||
psiFileProvider.messageCollector.flush(messageCollector)
|
psiFileProvider.messageCollector.flush(messageCollector)
|
||||||
psiFileProvider.messageCollector.clear()
|
psiFileProvider.messageCollector.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO can't use the same way as for build-history files because abi-snapshot for all dependencies should be stored into last-build
|
||||||
|
// and not only changed one
|
||||||
|
// (but possibly we dont need to read it all and may be it is possible to update only those who was changed)
|
||||||
|
override fun setupJarDependencies(args: K2JVMCompilerArguments, withSnapshot: Boolean, reporter: BuildReporter): Map<String, AbiSnapshot> {
|
||||||
|
//fill abiSnapshots
|
||||||
|
if (!withSnapshot) return emptyMap()
|
||||||
|
val abiSnapshots = HashMap<String, AbiSnapshot>()
|
||||||
|
args.classpathAsList
|
||||||
|
.filter { it.extension.equals("jar", ignoreCase = true) }
|
||||||
|
.forEach {
|
||||||
|
modulesApiHistory.abiSnapshot(it).let { result ->
|
||||||
|
if (result is Either.Success<Set<File>>) {
|
||||||
|
result.value.forEach { file ->
|
||||||
|
AbiSnapshotImpl.read(file, reporter)?.also { abiSnapshot -> abiSnapshots[it.absolutePath] = abiSnapshot }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return abiSnapshots
|
||||||
|
}
|
||||||
|
|
||||||
private fun calculateSourcesToCompileImpl(
|
private fun calculateSourcesToCompileImpl(
|
||||||
caches: IncrementalJvmCachesManager,
|
caches: IncrementalJvmCachesManager,
|
||||||
changedFiles: ChangedFiles.Known,
|
changedFiles: ChangedFiles.Known,
|
||||||
args: K2JVMCompilerArguments
|
args: K2JVMCompilerArguments,
|
||||||
|
abiSnapshots: Map<String, AbiSnapshot> = HashMap()
|
||||||
): CompilationMode {
|
): CompilationMode {
|
||||||
val dirtyFiles = DirtyFilesContainer(caches, reporter, kotlinSourceFilesExtensions)
|
val dirtyFiles = DirtyFilesContainer(caches, reporter, kotlinSourceFilesExtensions)
|
||||||
initDirtyFiles(dirtyFiles, changedFiles)
|
initDirtyFiles(dirtyFiles, changedFiles)
|
||||||
@@ -185,7 +209,9 @@ class IncrementalJvmCompilerRunner(
|
|||||||
reporter.reportVerbose { "Last Kotlin Build info -- $lastBuildInfo" }
|
reporter.reportVerbose { "Last Kotlin Build info -- $lastBuildInfo" }
|
||||||
|
|
||||||
val classpathChanges = reporter.measure(BuildTime.IC_ANALYZE_CHANGES_IN_DEPENDENCIES) {
|
val classpathChanges = reporter.measure(BuildTime.IC_ANALYZE_CHANGES_IN_DEPENDENCIES) {
|
||||||
getClasspathChanges(args.classpathAsList, changedFiles, lastBuildInfo, modulesApiHistory, reporter)
|
val scopes = caches.lookupCache.lookupMap.keys.map { if (it.scope.isBlank()) it.name else it.scope }.distinct()
|
||||||
|
getClasspathChanges(args.classpathAsList, changedFiles, lastBuildInfo, modulesApiHistory, reporter,
|
||||||
|
abiSnapshots, withSnapshot, caches.platformCache, scopes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNUSED_VARIABLE") // for sealed when
|
@Suppress("UNUSED_VARIABLE") // for sealed when
|
||||||
|
|||||||
+70
-39
@@ -19,7 +19,11 @@ internal fun getClasspathChanges(
|
|||||||
changedFiles: ChangedFiles.Known,
|
changedFiles: ChangedFiles.Known,
|
||||||
lastBuildInfo: BuildInfo,
|
lastBuildInfo: BuildInfo,
|
||||||
modulesApiHistory: ModulesApiHistory,
|
modulesApiHistory: ModulesApiHistory,
|
||||||
reporter: BuildReporter
|
reporter: BuildReporter,
|
||||||
|
abiSnapshots: Map<String, AbiSnapshot>,
|
||||||
|
withSnapshot: Boolean,
|
||||||
|
caches: IncrementalCacheCommon,
|
||||||
|
scopes: Collection<String>
|
||||||
): ChangesEither {
|
): ChangesEither {
|
||||||
val classpathSet = HashSet<File>()
|
val classpathSet = HashSet<File>()
|
||||||
for (file in classpath) {
|
for (file in classpath) {
|
||||||
@@ -40,52 +44,79 @@ internal fun getClasspathChanges(
|
|||||||
|
|
||||||
if (modifiedClasspath.isEmpty()) return ChangesEither.Known()
|
if (modifiedClasspath.isEmpty()) return ChangesEither.Known()
|
||||||
|
|
||||||
val lastBuildTS = lastBuildInfo.startTS
|
if (withSnapshot) {
|
||||||
|
fun analyzeJarFiles(): ChangesEither {
|
||||||
|
val symbols = HashSet<LookupSymbol>()
|
||||||
|
val fqNames = HashSet<FqName>()
|
||||||
|
|
||||||
val symbols = HashSet<LookupSymbol>()
|
for ((module, abiSnapshot) in abiSnapshots) {
|
||||||
val fqNames = HashSet<FqName>()
|
val actualAbiSnapshot = lastBuildInfo.dependencyToAbiSnapshot[module]
|
||||||
|
if (actualAbiSnapshot == null) {
|
||||||
|
|
||||||
val historyFilesEither =
|
reporter.report { "Some jar are removed from classpath $module" }
|
||||||
reporter.measure(BuildTime.IC_FIND_HISTORY_FILES) {
|
return ChangesEither.Unknown(BuildAttribute.DEP_CHANGE_REMOVED_ENTRY)
|
||||||
modulesApiHistory.historyFilesForChangedFiles(modifiedClasspath)
|
|
||||||
}
|
|
||||||
val historyFiles = when (historyFilesEither) {
|
|
||||||
is Either.Success<Set<File>> -> historyFilesEither.value
|
|
||||||
is Either.Error -> {
|
|
||||||
reporter.report { "Could not find history files: ${historyFilesEither.reason}" }
|
|
||||||
return ChangesEither.Unknown(BuildAttribute.DEP_CHANGE_HISTORY_IS_NOT_FOUND)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun analyzeHistoryFiles(): ChangesEither {
|
|
||||||
for (historyFile in historyFiles) {
|
|
||||||
val allBuilds = BuildDiffsStorage.readDiffsFromFile(historyFile, reporter = reporter)
|
|
||||||
?: return run {
|
|
||||||
reporter.report { "Could not read diffs from $historyFile" }
|
|
||||||
ChangesEither.Unknown(BuildAttribute.DEP_CHANGE_HISTORY_CANNOT_BE_READ)
|
|
||||||
}
|
}
|
||||||
val (knownBuilds, newBuilds) = allBuilds.partition { it.ts <= lastBuildTS }
|
val diffData = AbiSnapshotDiffService.doCompute(abiSnapshot, actualAbiSnapshot, caches, scopes)
|
||||||
if (knownBuilds.isEmpty()) {
|
symbols.addAll(diffData.dirtyLookupSymbols)
|
||||||
reporter.report { "No previously known builds for $historyFile" }
|
fqNames.addAll(diffData.dirtyClassesFqNames)
|
||||||
return ChangesEither.Unknown(BuildAttribute.DEP_CHANGE_HISTORY_NO_KNOWN_BUILDS)
|
|
||||||
|
}
|
||||||
|
return ChangesEither.Known(symbols, fqNames)
|
||||||
|
}
|
||||||
|
return reporter.measure(BuildTime.IC_ANALYZE_JAR_FILES) {
|
||||||
|
analyzeJarFiles()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val lastBuildTS = lastBuildInfo.startTS
|
||||||
|
|
||||||
|
val symbols = HashSet<LookupSymbol>()
|
||||||
|
val fqNames = HashSet<FqName>()
|
||||||
|
|
||||||
|
val historyFilesEither =
|
||||||
|
reporter.measure(BuildTime.IC_FIND_HISTORY_FILES) {
|
||||||
|
modulesApiHistory.historyFilesForChangedFiles(modifiedClasspath)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (buildDiff in newBuilds) {
|
val historyFiles = when (historyFilesEither) {
|
||||||
if (!buildDiff.isIncremental) {
|
is Either.Success<Set<File>> -> historyFilesEither.value
|
||||||
reporter.report { "Non-incremental build from dependency $historyFile" }
|
is Either.Error -> {
|
||||||
return ChangesEither.Unknown(BuildAttribute.DEP_CHANGE_NON_INCREMENTAL_BUILD_IN_DEP)
|
reporter.report { "Could not find history files: ${historyFilesEither.reason}" }
|
||||||
|
return ChangesEither.Unknown(BuildAttribute.DEP_CHANGE_HISTORY_IS_NOT_FOUND)
|
||||||
}
|
|
||||||
val dirtyData = buildDiff.dirtyData
|
|
||||||
symbols.addAll(dirtyData.dirtyLookupSymbols)
|
|
||||||
fqNames.addAll(dirtyData.dirtyClassesFqNames)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ChangesEither.Known(symbols, fqNames)
|
fun analyzeHistoryFiles(): ChangesEither {
|
||||||
}
|
for (historyFile in historyFiles) {
|
||||||
|
val allBuilds = BuildDiffsStorage.readDiffsFromFile(historyFile, reporter = reporter)
|
||||||
|
?: return run {
|
||||||
|
reporter.report { "Could not read diffs from $historyFile" }
|
||||||
|
ChangesEither.Unknown(BuildAttribute.DEP_CHANGE_HISTORY_CANNOT_BE_READ)
|
||||||
|
}
|
||||||
|
|
||||||
return reporter.measure(BuildTime.IC_ANALYZE_HISTORY_FILES) {
|
val (knownBuilds, newBuilds) = allBuilds.partition { it.ts <= lastBuildTS }
|
||||||
analyzeHistoryFiles()
|
if (knownBuilds.isEmpty()) {
|
||||||
|
reporter.report { "No previously known builds for $historyFile" }
|
||||||
|
return ChangesEither.Unknown(BuildAttribute.DEP_CHANGE_HISTORY_NO_KNOWN_BUILDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (buildDiff in newBuilds) {
|
||||||
|
if (!buildDiff.isIncremental) {
|
||||||
|
reporter.report { "Non-incremental build from dependency $historyFile" }
|
||||||
|
return ChangesEither.Unknown(BuildAttribute.DEP_CHANGE_NON_INCREMENTAL_BUILD_IN_DEP)
|
||||||
|
|
||||||
|
}
|
||||||
|
val dirtyData = buildDiff.dirtyData
|
||||||
|
symbols.addAll(dirtyData.dirtyLookupSymbols)
|
||||||
|
fqNames.addAll(dirtyData.dirtyClassesFqNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ChangesEither.Known(symbols, fqNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
return reporter.measure(BuildTime.IC_ANALYZE_HISTORY_FILES) {
|
||||||
|
analyzeHistoryFiles()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+29
-4
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.incremental.multiproject
|
package org.jetbrains.kotlin.incremental.multiproject
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.incremental.IncrementalModuleEntry
|
||||||
import org.jetbrains.kotlin.incremental.IncrementalModuleInfo
|
import org.jetbrains.kotlin.incremental.IncrementalModuleInfo
|
||||||
import org.jetbrains.kotlin.incremental.util.Either
|
import org.jetbrains.kotlin.incremental.util.Either
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -14,11 +15,14 @@ import java.util.zip.ZipFile
|
|||||||
|
|
||||||
interface ModulesApiHistory {
|
interface ModulesApiHistory {
|
||||||
fun historyFilesForChangedFiles(changedFiles: Set<File>): Either<Set<File>>
|
fun historyFilesForChangedFiles(changedFiles: Set<File>): Either<Set<File>>
|
||||||
|
fun abiSnapshot(jar: File): Either<Set<File>>
|
||||||
}
|
}
|
||||||
|
|
||||||
object EmptyModulesApiHistory : ModulesApiHistory {
|
object EmptyModulesApiHistory : ModulesApiHistory {
|
||||||
override fun historyFilesForChangedFiles(changedFiles: Set<File>): Either<Set<File>> =
|
override fun historyFilesForChangedFiles(changedFiles: Set<File>): Either<Set<File>> =
|
||||||
Either.Error("Multi-module IC is not configured")
|
Either.Error("Multi-module IC is not configured")
|
||||||
|
|
||||||
|
override fun abiSnapshot(jar: File): Either<Set<File>> = Either.Error("Not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ModulesApiHistoryBase(protected val modulesInfo: IncrementalModuleInfo) : ModulesApiHistory {
|
abstract class ModulesApiHistoryBase(protected val modulesInfo: IncrementalModuleInfo) : ModulesApiHistory {
|
||||||
@@ -130,6 +134,14 @@ class ModulesApiHistoryJvm(modulesInfo: IncrementalModuleInfo) : ModulesApiHisto
|
|||||||
|
|
||||||
return Either.Success(result)
|
return Either.Success(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun abiSnapshot(jar: File): Either<Set<File>> {
|
||||||
|
val abiSnapshot = modulesInfo.jarToModule[jar]?.abiSnapshot ?: modulesInfo.jarToAbiSnapshot[jar]
|
||||||
|
return if (abiSnapshot != null)
|
||||||
|
Either.Success(setOf(abiSnapshot))
|
||||||
|
else
|
||||||
|
Either.Error("Failed to find abi snapshot for file ${jar.absolutePath}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ModulesApiHistoryJs(modulesInfo: IncrementalModuleInfo) : ModulesApiHistoryBase(modulesInfo) {
|
class ModulesApiHistoryJs(modulesInfo: IncrementalModuleInfo) : ModulesApiHistoryBase(modulesInfo) {
|
||||||
@@ -141,6 +153,11 @@ class ModulesApiHistoryJs(modulesInfo: IncrementalModuleInfo) : ModulesApiHistor
|
|||||||
else -> Either.Error("No module is found for jar $jar")
|
else -> Either.Error("No module is found for jar $jar")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun abiSnapshot(jar: File): Either<Set<File>> {
|
||||||
|
return modulesInfo.jarToModule[jar]?.abiSnapshot?.let { Either.Success(setOf(it)) } ?: Either.Error("Failed to find snapshot for file ${jar.absolutePath}")
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ModulesApiHistoryAndroid(modulesInfo: IncrementalModuleInfo) : ModulesApiHistoryBase(modulesInfo) {
|
class ModulesApiHistoryAndroid(modulesInfo: IncrementalModuleInfo) : ModulesApiHistoryBase(modulesInfo) {
|
||||||
@@ -158,7 +175,15 @@ class ModulesApiHistoryAndroid(modulesInfo: IncrementalModuleInfo) : ModulesApiH
|
|||||||
if (!isInProjectBuildDir(jar)) return Either.Error("Non-project jar is modified $jar")
|
if (!isInProjectBuildDir(jar)) return Either.Error("Non-project jar is modified $jar")
|
||||||
|
|
||||||
val jarPath = Paths.get(jar.absolutePath)
|
val jarPath = Paths.get(jar.absolutePath)
|
||||||
return getHistoryForModuleNames(jarPath, getPossibleModuleNamesFromJar(jarPath))
|
return getHistoryForModuleNames(jarPath, getPossibleModuleNamesFromJar(jarPath), IncrementalModuleEntry::buildHistoryFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun abiSnapshot(jar: File): Either<Set<File>> {
|
||||||
|
val jarPath = Paths.get(jar.absolutePath)
|
||||||
|
return when (val result = getHistoryForModuleNames(jarPath, getPossibleModuleNamesFromJar(jarPath), IncrementalModuleEntry::abiSnapshot)) {
|
||||||
|
is Either.Success -> Either.Success(result.value)
|
||||||
|
is Either.Error -> Either.Error(result.reason)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getBuildHistoryForDir(file: File): Either<Set<File>> {
|
override fun getBuildHistoryForDir(file: File): Either<Set<File>> {
|
||||||
@@ -175,7 +200,7 @@ class ModulesApiHistoryAndroid(modulesInfo: IncrementalModuleInfo) : ModulesApiH
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return getHistoryForModuleNames(file.toPath(), moduleNames)
|
return getHistoryForModuleNames(file.toPath(), moduleNames, IncrementalModuleEntry::buildHistoryFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPossibleModuleNamesFromJar(path: Path): Collection<String> {
|
private fun getPossibleModuleNamesFromJar(path: Path): Collection<String> {
|
||||||
@@ -205,13 +230,13 @@ class ModulesApiHistoryAndroid(modulesInfo: IncrementalModuleInfo) : ModulesApiH
|
|||||||
return path.listFiles().filter { it.name.endsWith(".kotlin_module", ignoreCase = true) }.map { it.nameWithoutExtension }
|
return path.listFiles().filter { it.name.endsWith(".kotlin_module", ignoreCase = true) }.map { it.nameWithoutExtension }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getHistoryForModuleNames(path: Path, moduleNames: Iterable<String>): Either<Set<File>> {
|
private fun getHistoryForModuleNames(path: Path, moduleNames: Iterable<String>, fileLocation: (IncrementalModuleEntry) -> File): Either<Set<File>> {
|
||||||
val possibleModules =
|
val possibleModules =
|
||||||
moduleNames.flatMapTo(HashSet()) { modulesInfo.nameToModules[it] ?: emptySet() }
|
moduleNames.flatMapTo(HashSet()) { modulesInfo.nameToModules[it] ?: emptySet() }
|
||||||
val modules = possibleModules.filter { Paths.get(it.buildDir.absolutePath).isParentOf(path) }
|
val modules = possibleModules.filter { Paths.get(it.buildDir.absolutePath).isParentOf(path) }
|
||||||
if (modules.isEmpty()) return Either.Error("Unknown module for $path (candidates: ${possibleModules.joinToString()})")
|
if (modules.isEmpty()) return Either.Error("Unknown module for $path (candidates: ${possibleModules.joinToString()})")
|
||||||
|
|
||||||
val result = modules.mapTo(HashSet()) { it.buildHistoryFile }
|
val result = modules.mapTo(HashSet()) { fileLocation(it) }
|
||||||
return Either.Success(result)
|
return Either.Success(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -30,9 +30,10 @@ abstract class AbstractIncrementalMultiModuleCompilerRunnerTest<Args : CommonCom
|
|||||||
private val nameToModules = mutableMapOf<String, MutableSet<IncrementalModuleEntry>>()
|
private val nameToModules = mutableMapOf<String, MutableSet<IncrementalModuleEntry>>()
|
||||||
private val jarToClassListFile = mutableMapOf<File, File>()
|
private val jarToClassListFile = mutableMapOf<File, File>()
|
||||||
private val jarToModule = mutableMapOf<File, IncrementalModuleEntry>()
|
private val jarToModule = mutableMapOf<File, IncrementalModuleEntry>()
|
||||||
|
private val jarToAbiSnapshot = mutableMapOf<File, File>()
|
||||||
|
|
||||||
protected val incrementalModuleInfo: IncrementalModuleInfo by lazy {
|
protected val incrementalModuleInfo: IncrementalModuleInfo by lazy {
|
||||||
IncrementalModuleInfo(workingDir, workingDir, dirToModule, nameToModules, jarToClassListFile, jarToModule)
|
IncrementalModuleInfo(workingDir, workingDir, dirToModule, nameToModules, jarToClassListFile, jarToModule, jarToAbiSnapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract val modulesApiHistory: ApiHistory
|
protected abstract val modulesApiHistory: ApiHistory
|
||||||
@@ -97,8 +98,9 @@ abstract class AbstractIncrementalMultiModuleCompilerRunnerTest<Args : CommonCom
|
|||||||
val moduleBuildDir = File(outDir, moduleName)
|
val moduleBuildDir = File(outDir, moduleName)
|
||||||
val moduleCacheDir = File(cacheDir, moduleName)
|
val moduleCacheDir = File(cacheDir, moduleName)
|
||||||
val moduleBuildHistoryFile = buildHistoryFile(moduleCacheDir)
|
val moduleBuildHistoryFile = buildHistoryFile(moduleCacheDir)
|
||||||
|
val abiSnapshotFile = abiSnapshotFile(moduleCacheDir)
|
||||||
|
|
||||||
val moduleEntry = IncrementalModuleEntry(workingDir.absolutePath, moduleName, outDir, moduleBuildHistoryFile)
|
val moduleEntry = IncrementalModuleEntry(workingDir.absolutePath, moduleName, outDir, moduleBuildHistoryFile, abiSnapshotFile)
|
||||||
|
|
||||||
dirToModule[moduleBuildDir] = moduleEntry
|
dirToModule[moduleBuildDir] = moduleEntry
|
||||||
nameToModules.getOrPut(moduleName) { mutableSetOf() }.add(moduleEntry)
|
nameToModules.getOrPut(moduleName) { mutableSetOf() }.add(moduleEntry)
|
||||||
|
|||||||
+3
@@ -177,6 +177,9 @@ abstract class AbstractIncrementalCompilerRunnerTestBase<Args : CommonCompilerAr
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
protected fun buildHistoryFile(cacheDir: File): File = File(cacheDir, "build-history.bin")
|
protected fun buildHistoryFile(cacheDir: File): File = File(cacheDir, "build-history.bin")
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
protected fun abiSnapshotFile(cacheDir: File): File = File(cacheDir, IncrementalCompilerRunner.ABI_SNAPSHOT_FILE_NAME)
|
||||||
|
|
||||||
private const val ARGUMENTS_FILE_NAME = "args.txt"
|
private const val ARGUMENTS_FILE_NAME = "args.txt"
|
||||||
|
|
||||||
private fun parseAdditionalArgs(testDir: File): List<String> {
|
private fun parseAdditionalArgs(testDir: File): List<String> {
|
||||||
|
|||||||
+8
-3
@@ -26,9 +26,11 @@ class ModulesApiHistoryAndroidTest {
|
|||||||
private lateinit var appRoot: File
|
private lateinit var appRoot: File
|
||||||
private lateinit var appKotlinDestination: File
|
private lateinit var appKotlinDestination: File
|
||||||
private lateinit var appHistory: File
|
private lateinit var appHistory: File
|
||||||
|
private lateinit var appAbiSnapshot: File
|
||||||
private lateinit var libRoot: File
|
private lateinit var libRoot: File
|
||||||
private lateinit var libKotlinDestination: File
|
private lateinit var libKotlinDestination: File
|
||||||
private lateinit var libHistory: File
|
private lateinit var libHistory: File
|
||||||
|
private lateinit var libAbiSnapshot: File
|
||||||
|
|
||||||
private lateinit var androidHistory: ModulesApiHistoryAndroid
|
private lateinit var androidHistory: ModulesApiHistoryAndroid
|
||||||
|
|
||||||
@@ -38,8 +40,9 @@ class ModulesApiHistoryAndroidTest {
|
|||||||
|
|
||||||
appRoot = projectRoot.resolve("app")
|
appRoot = projectRoot.resolve("app")
|
||||||
appHistory = appRoot.resolve("build/tmp/kotlin/app_history.bin")
|
appHistory = appRoot.resolve("build/tmp/kotlin/app_history.bin")
|
||||||
|
appAbiSnapshot = appRoot.resolve("build/tmp/kotlin/app_abi_snapshot.bin")
|
||||||
appKotlinDestination = appRoot.resolve("build/tmp/kotlin-classes").apply { mkdirs() }
|
appKotlinDestination = appRoot.resolve("build/tmp/kotlin-classes").apply { mkdirs() }
|
||||||
val appEntry = IncrementalModuleEntry(":app", "app", appRoot.resolve("build"), appHistory)
|
val appEntry = IncrementalModuleEntry(":app", "app", appRoot.resolve("build"), appHistory, appAbiSnapshot)
|
||||||
appRoot.resolve("build/intermediates/classes/meta-inf/").apply {
|
appRoot.resolve("build/intermediates/classes/meta-inf/").apply {
|
||||||
mkdirs()
|
mkdirs()
|
||||||
resolve("app.kotlin_module").createNewFile()
|
resolve("app.kotlin_module").createNewFile()
|
||||||
@@ -47,8 +50,9 @@ class ModulesApiHistoryAndroidTest {
|
|||||||
|
|
||||||
libRoot = projectRoot.resolve("lib")
|
libRoot = projectRoot.resolve("lib")
|
||||||
libHistory = libRoot.resolve("lib/build/tmp/kotlin/lib_history.bin")
|
libHistory = libRoot.resolve("lib/build/tmp/kotlin/lib_history.bin")
|
||||||
|
libAbiSnapshot = libRoot.resolve("lib/build/tmp/kotlin/lib_abi_snapshot.bin")
|
||||||
libKotlinDestination = libRoot.resolve("build/tmp/kotlin-classes").apply { mkdirs() }
|
libKotlinDestination = libRoot.resolve("build/tmp/kotlin-classes").apply { mkdirs() }
|
||||||
val libEntry = IncrementalModuleEntry(":lib", "lib", libRoot.resolve("build"), libHistory)
|
val libEntry = IncrementalModuleEntry(":lib", "lib", libRoot.resolve("build"), libHistory, libAbiSnapshot)
|
||||||
libRoot.resolve("build/intermediates/classes/meta-inf/").apply {
|
libRoot.resolve("build/intermediates/classes/meta-inf/").apply {
|
||||||
mkdirs()
|
mkdirs()
|
||||||
resolve("lib.kotlin_module").createNewFile()
|
resolve("lib.kotlin_module").createNewFile()
|
||||||
@@ -60,7 +64,8 @@ class ModulesApiHistoryAndroidTest {
|
|||||||
dirToModule = mapOf(appKotlinDestination to appEntry, libKotlinDestination to libEntry),
|
dirToModule = mapOf(appKotlinDestination to appEntry, libKotlinDestination to libEntry),
|
||||||
nameToModules = mapOf("app" to setOf(appEntry), "lib" to setOf(libEntry)),
|
nameToModules = mapOf("app" to setOf(appEntry), "lib" to setOf(libEntry)),
|
||||||
jarToClassListFile = mapOf(),
|
jarToClassListFile = mapOf(),
|
||||||
jarToModule = mapOf()
|
jarToModule = mapOf(),
|
||||||
|
jarToAbiSnapshot = mapOf()
|
||||||
)
|
)
|
||||||
|
|
||||||
androidHistory = ModulesApiHistoryAndroid(info)
|
androidHistory = ModulesApiHistoryAndroid(info)
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import org.jetbrains.kotlin.metadata.ProtoBuf.QualifiedNameTable.QualifiedName
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class NameResolverImpl(
|
class NameResolverImpl(
|
||||||
private val strings: ProtoBuf.StringTable,
|
val strings: ProtoBuf.StringTable,
|
||||||
private val qualifiedNames: ProtoBuf.QualifiedNameTable
|
val qualifiedNames: ProtoBuf.QualifiedNameTable
|
||||||
) : NameResolver {
|
) : NameResolver {
|
||||||
override fun getString(index: Int): String = strings.getString(index)
|
override fun getString(index: Int): String = strings.getString(index)
|
||||||
|
|
||||||
|
|||||||
+4
@@ -251,6 +251,7 @@ abstract class BaseGradleIT {
|
|||||||
val useFir: Boolean = false,
|
val useFir: Boolean = false,
|
||||||
val customEnvironmentVariables: Map<String, String> = mapOf(),
|
val customEnvironmentVariables: Map<String, String> = mapOf(),
|
||||||
val dryRun: Boolean = false,
|
val dryRun: Boolean = false,
|
||||||
|
val abiSnapshot: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
enum class ConfigurationCacheProblems {
|
enum class ConfigurationCacheProblems {
|
||||||
@@ -953,6 +954,9 @@ Finished executing task ':$taskName'|
|
|||||||
if (options.dryRun) {
|
if (options.dryRun) {
|
||||||
add("--dry-run")
|
add("--dry-run")
|
||||||
}
|
}
|
||||||
|
if (options.abiSnapshot) {
|
||||||
|
add("-Dkotlin.incremental.classpath.snapshot.enabled=true")
|
||||||
|
}
|
||||||
|
|
||||||
add("-Dorg.gradle.unsafe.configuration-cache=${options.configurationCache}")
|
add("-Dorg.gradle.unsafe.configuration-cache=${options.configurationCache}")
|
||||||
add("-Dorg.gradle.unsafe.configuration-cache-problems=${options.configurationCacheProblems.name.toLowerCase()}")
|
add("-Dorg.gradle.unsafe.configuration-cache-problems=${options.configurationCacheProblems.name.toLowerCase()}")
|
||||||
|
|||||||
+9
-1
@@ -27,6 +27,14 @@ abstract class IncrementalCompilationBaseIT : BaseGradleIT() {
|
|||||||
protected fun doTest(
|
protected fun doTest(
|
||||||
modifyProject: (Project) -> Unit,
|
modifyProject: (Project) -> Unit,
|
||||||
expectedAffectedFileNames: Collection<String>,
|
expectedAffectedFileNames: Collection<String>,
|
||||||
|
) {
|
||||||
|
doTest(defaultBuildOptions(), modifyProject, expectedAffectedFileNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun doTest(
|
||||||
|
options: BuildOptions,
|
||||||
|
modifyProject: (Project) -> Unit,
|
||||||
|
expectedAffectedFileNames: Collection<String>,
|
||||||
) {
|
) {
|
||||||
val project = defaultProject()
|
val project = defaultProject()
|
||||||
project.build("build") {
|
project.build("build") {
|
||||||
@@ -35,7 +43,7 @@ abstract class IncrementalCompilationBaseIT : BaseGradleIT() {
|
|||||||
|
|
||||||
modifyProject(project)
|
modifyProject(project)
|
||||||
|
|
||||||
project.build("build") {
|
project.build("build", options = options) {
|
||||||
assertSuccessful()
|
assertSuccessful()
|
||||||
val expectedAffectedFiles = project.projectDir.getFilesByNames(*expectedAffectedFileNames.toTypedArray())
|
val expectedAffectedFiles = project.projectDir.getFilesByNames(*expectedAffectedFileNames.toTypedArray())
|
||||||
val expectedAffectedFileRelativePaths = project.relativize(expectedAffectedFiles)
|
val expectedAffectedFileRelativePaths = project.relativize(expectedAffectedFiles)
|
||||||
|
|||||||
+84
@@ -189,6 +189,30 @@ abstract class BaseIncrementalCompilationMultiProjectIT : IncrementalCompilation
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAddNewMethodToLib() {
|
||||||
|
doTest(
|
||||||
|
options = defaultBuildOptions().copy(abiSnapshot = true),
|
||||||
|
{ project ->
|
||||||
|
val aKt = project.projectDir.getFileByName("A.kt")
|
||||||
|
aKt.writeText(
|
||||||
|
"""
|
||||||
|
package bar
|
||||||
|
|
||||||
|
open class A {
|
||||||
|
fun a() {}
|
||||||
|
fun newA() {}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
},
|
||||||
|
//TODO for abi-snapshot "BB.kt" should not be recompiled
|
||||||
|
expectedAffectedFileNames = listOf(
|
||||||
|
"A.kt", "B.kt", "AA.kt", "BB.kt", "AAA.kt"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testLibClassBecameFinal() {
|
fun testLibClassBecameFinal() {
|
||||||
// TODO: fix fir IC and remove
|
// TODO: fix fir IC and remove
|
||||||
@@ -229,6 +253,8 @@ abstract class BaseIncrementalCompilationMultiProjectIT : IncrementalCompilation
|
|||||||
project.projectFile("BarDummy.kt").modify {
|
project.projectFile("BarDummy.kt").modify {
|
||||||
it.replace("class BarDummy", "open class BarDummy")
|
it.replace("class BarDummy", "open class BarDummy")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//don't need to recompile app classes because lib's proto stays the same
|
||||||
project.build("build") {
|
project.build("build") {
|
||||||
assertSuccessful()
|
assertSuccessful()
|
||||||
val affectedSources = project.projectDir.allKotlinFiles()
|
val affectedSources = project.projectDir.allKotlinFiles()
|
||||||
@@ -244,9 +270,67 @@ abstract class BaseIncrementalCompilationMultiProjectIT : IncrementalCompilation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCleanBuildLibForAbiSnapshot() {
|
||||||
|
val options = defaultBuildOptions().copy(abiSnapshot = true)
|
||||||
|
val project = defaultProject()
|
||||||
|
|
||||||
|
project.build("build", options = options) {
|
||||||
|
assertSuccessful()
|
||||||
|
}
|
||||||
|
|
||||||
|
project.build(":lib:clean", options = options) {
|
||||||
|
assertSuccessful()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change file so Gradle won't skip :app:compile
|
||||||
|
project.projectFile("BarDummy.kt").modify {
|
||||||
|
it.replace("class BarDummy", "open class BarDummy")
|
||||||
|
}
|
||||||
|
|
||||||
|
//don't need to recompile app classes because lib's proto stays the same
|
||||||
|
project.build("build", options = options) {
|
||||||
|
val affectedSources = project.projectDir.allKotlinFiles()
|
||||||
|
val relativePaths = project.relativize(affectedSources)
|
||||||
|
assertCompiledKotlinSources(relativePaths)
|
||||||
|
}
|
||||||
|
|
||||||
|
val aaKt = project.projectFile("AA.kt")
|
||||||
|
aaKt.modify { "$it " }
|
||||||
|
project.build("build", options = options) {
|
||||||
|
assertSuccessful()
|
||||||
|
assertCompiledKotlinSources(project.relativize(aaKt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract val additionalLibDependencies: String
|
protected abstract val additionalLibDependencies: String
|
||||||
protected abstract val compileKotlinTaskName: String
|
protected abstract val compileKotlinTaskName: String
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAddMethodToLibForAbiSnapshot() {
|
||||||
|
val project = defaultProject()
|
||||||
|
val options = defaultBuildOptions().copy(abiSnapshot = true)
|
||||||
|
|
||||||
|
project.build("build", options = options) {
|
||||||
|
assertSuccessful()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change file so Gradle won't skip :app:compile
|
||||||
|
project.projectFile("BarDummy.kt").modify {
|
||||||
|
it.replace("class BarDummy", "open class BarDummy")
|
||||||
|
}
|
||||||
|
|
||||||
|
val barDummyClassFile = project.projectFile("BarDummy.kt")
|
||||||
|
barDummyClassFile.modify { "$it { fun m() = 42}" }
|
||||||
|
|
||||||
|
project.build("build", options = options) {
|
||||||
|
assertSuccessful()
|
||||||
|
val relativePaths = project.relativize(barDummyClassFile)
|
||||||
|
assertCompiledKotlinSources(relativePaths)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testAddDependencyToLib() {
|
fun testAddDependencyToLib() {
|
||||||
val project = defaultProject()
|
val project = defaultProject()
|
||||||
|
|||||||
+25
-3
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
|||||||
import org.jetbrains.kotlin.gradle.logging.kotlinInfo
|
import org.jetbrains.kotlin.gradle.logging.kotlinInfo
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||||
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
|
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget
|
||||||
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
|
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
|
||||||
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
|
||||||
import org.jetbrains.kotlin.gradle.tasks.GradleCompileTaskProvider
|
import org.jetbrains.kotlin.gradle.tasks.GradleCompileTaskProvider
|
||||||
@@ -212,6 +213,7 @@ internal open class GradleCompilerRunner(
|
|||||||
val nameToModules = HashMap<String, HashSet<IncrementalModuleEntry>>()
|
val nameToModules = HashMap<String, HashSet<IncrementalModuleEntry>>()
|
||||||
val jarToClassListFile = HashMap<File, File>()
|
val jarToClassListFile = HashMap<File, File>()
|
||||||
val jarToModule = HashMap<File, IncrementalModuleEntry>()
|
val jarToModule = HashMap<File, IncrementalModuleEntry>()
|
||||||
|
val jarToAbiSnapshot = HashMap<File, File>()
|
||||||
|
|
||||||
val multiplatformProjectTasks = mutableMapOf<Project, MutableSet<String>>()
|
val multiplatformProjectTasks = mutableMapOf<Project, MutableSet<String>>()
|
||||||
|
|
||||||
@@ -229,7 +231,8 @@ internal open class GradleCompilerRunner(
|
|||||||
project.path,
|
project.path,
|
||||||
task.moduleName.get(),
|
task.moduleName.get(),
|
||||||
project.buildDir,
|
project.buildDir,
|
||||||
task.buildHistoryFile.get().asFile
|
task.buildHistoryFile.get().asFile,
|
||||||
|
task.abiSnapshotFile.get().asFile
|
||||||
)
|
)
|
||||||
dirToModule[task.destinationDir] = module
|
dirToModule[task.destinationDir] = module
|
||||||
task.javaOutputDir.orNull?.asFile?.let { dirToModule[it] = module }
|
task.javaOutputDir.orNull?.asFile?.let { dirToModule[it] = module }
|
||||||
@@ -240,6 +243,15 @@ internal open class GradleCompilerRunner(
|
|||||||
jarToModule[it] = module
|
jarToModule[it] = module
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// for (target in task.targets) {
|
||||||
|
// if (target is KotlinWithJavaTarget<*>) {
|
||||||
|
// val jar = project.tasks.getByName(target.artifactsTaskName) as Jar
|
||||||
|
// jarToClassListFile[jar.archivePathCompatible.canonicalFile] = target.defaultArtifactClassesListFile.get()
|
||||||
|
// //configure abiSnapshot mapping for jars
|
||||||
|
// jarToAbiSnapshot[jar.archivePathCompatible.canonicalFile] =
|
||||||
|
// target.buildDir.get().file(task.abiSnapshotRelativePath).asFile
|
||||||
|
// }
|
||||||
|
// }
|
||||||
} else if (task is InspectClassesForMultiModuleIC) {
|
} else if (task is InspectClassesForMultiModuleIC) {
|
||||||
jarToClassListFile[File(task.archivePath.get())] = task.classesListFile
|
jarToClassListFile[File(task.archivePath.get())] = task.classesListFile
|
||||||
}
|
}
|
||||||
@@ -260,10 +272,19 @@ internal open class GradleCompilerRunner(
|
|||||||
project.path,
|
project.path,
|
||||||
kotlinTask.moduleName.get(),
|
kotlinTask.moduleName.get(),
|
||||||
project.buildDir,
|
project.buildDir,
|
||||||
kotlinTask.buildHistoryFile.get().asFile
|
kotlinTask.buildHistoryFile.get().asFile,
|
||||||
|
kotlinTask.abiSnapshotFile.get().asFile
|
||||||
)
|
)
|
||||||
val jarTask = project.tasks.findByName(target.artifactsTaskName) as? AbstractArchiveTask ?: continue
|
val jarTask = project.tasks.findByName(target.artifactsTaskName) as? AbstractArchiveTask ?: continue
|
||||||
jarToModule[jarTask.archivePathCompatible.canonicalFile] = module
|
jarToModule[jarTask.archivePathCompatible.canonicalFile] = module
|
||||||
|
if (target is KotlinWithJavaTarget<*>) {
|
||||||
|
val jar = project.tasks.getByName(target.artifactsTaskName) as Jar
|
||||||
|
jarToClassListFile[jar.archivePathCompatible.canonicalFile] = target.defaultArtifactClassesListFile.get()
|
||||||
|
//configure abiSnapshot mapping for jars
|
||||||
|
jarToAbiSnapshot[jar.archivePathCompatible.canonicalFile] =
|
||||||
|
target.buildDir.get().file(kotlinTask.abiSnapshotRelativePath).get().asFile
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,7 +295,8 @@ internal open class GradleCompilerRunner(
|
|||||||
dirToModule = dirToModule,
|
dirToModule = dirToModule,
|
||||||
nameToModules = nameToModules,
|
nameToModules = nameToModules,
|
||||||
jarToClassListFile = jarToClassListFile,
|
jarToClassListFile = jarToClassListFile,
|
||||||
jarToModule = jarToModule
|
jarToModule = jarToModule,
|
||||||
|
jarToAbiSnapshot = jarToAbiSnapshot
|
||||||
).also {
|
).also {
|
||||||
cachedGradle = WeakReference(gradle)
|
cachedGradle = WeakReference(gradle)
|
||||||
cachedModulesInfo = it
|
cachedModulesInfo = it
|
||||||
|
|||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* 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.gradle.plugin
|
||||||
|
|
||||||
|
import org.gradle.api.services.BuildService
|
||||||
|
import org.gradle.api.services.BuildServiceParameters
|
||||||
|
import org.jetbrains.kotlin.build.report.BuildReporter
|
||||||
|
import org.jetbrains.kotlin.incremental.*
|
||||||
|
|
||||||
|
abstract class AbiSnapshotDiffService() : BuildService<AbiSnapshotDiffService.Parameters> {
|
||||||
|
abstract class Parameters : BuildServiceParameters {
|
||||||
|
abstract val caches: IncrementalCachesManager<*>
|
||||||
|
abstract val reporter: BuildReporter
|
||||||
|
abstract val sourceFilesExtensions: List<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
val caches: IncrementalCachesManager<*> = parameters.caches
|
||||||
|
val reporter: BuildReporter = parameters.reporter
|
||||||
|
val sourceFilesExtensions: List<String> = parameters.sourceFilesExtensions
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
// //Store list of changed lookups
|
||||||
|
// val diffCache: MutableMap<Pair<AbiSnapshot, AbiSnapshot>, DirtyFilesContainer> = mutableMapOf()
|
||||||
|
// @TestOnly
|
||||||
|
// fun compareJarsInternal(caches: IncrementalCachesManager<*>, reporter: ICReporter, sourceFilesExtensions: List<String>,
|
||||||
|
// snapshot: AbiSnapshot, newJar: AbiSnapshot) =
|
||||||
|
// diffCache.computeIfAbsent(Pair(snapshot, newJar)) { (snapshotJar, actualJar) ->
|
||||||
|
// DirtyFilesContainer(caches, reporter, sourceFilesExtensions)
|
||||||
|
// .also {
|
||||||
|
// it.addByDirtyClasses(snapshotJar.fqNames.minus(actualJar.fqNames))
|
||||||
|
// it.addByDirtyClasses(actualJar.fqNames.minus(snapshotJar.fqNames))
|
||||||
|
// it.addByDirtySymbols(snapshotJar.symbols.minus(actualJar.symbols))
|
||||||
|
// it.addByDirtySymbols(actualJar.symbols.minus(snapshotJar.symbols))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Synchronized
|
||||||
|
// fun compareJarsInternal(snapshot: AbiSnapshot, newJar: AbiSnapshot): DirtyFilesContainer {
|
||||||
|
// return diffCache.computeIfAbsent(Pair(snapshot, newJar)) { (snapshotJar, actualJar) ->
|
||||||
|
// DirtyFilesContainer(caches, reporter, sourceFilesExtensions)
|
||||||
|
// .also {
|
||||||
|
// it.addByDirtyClasses(snapshotJar.fqNames.minus(actualJar.fqNames))
|
||||||
|
// it.addByDirtyClasses(actualJar.fqNames.minus(snapshotJar.fqNames))
|
||||||
|
// it.addByDirtySymbols(snapshotJar.symbols.minus(actualJar.symbols))
|
||||||
|
// it.addByDirtySymbols(actualJar.symbols.minus(snapshotJar.symbols))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
+3
@@ -9,6 +9,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp
|
|||||||
import org.gradle.api.NamedDomainObjectContainer
|
import org.gradle.api.NamedDomainObjectContainer
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.artifacts.Dependency
|
import org.gradle.api.artifacts.Dependency
|
||||||
|
import org.gradle.api.file.Directory
|
||||||
import org.gradle.api.plugins.JavaPlugin
|
import org.gradle.api.plugins.JavaPlugin
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
import org.gradle.jvm.tasks.Jar
|
import org.gradle.jvm.tasks.Jar
|
||||||
@@ -52,6 +53,8 @@ open class KotlinWithJavaTarget<KotlinOptionsType : KotlinCommonOptions>(
|
|||||||
val jarTask = project.tasks.getByName(artifactsTaskName) as Jar
|
val jarTask = project.tasks.getByName(artifactsTaskName) as Jar
|
||||||
it.file("${sanitizeFileName(jarTask.archiveFileName.get())}-classes.txt").asFile
|
it.file("${sanitizeFileName(jarTask.archiveFileName.get())}-classes.txt").asFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val buildDir: Provider<Directory> = layout.buildDirectory.dir(KOTLIN_BUILD_DIR_NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sanitizeFileName(candidate: String): String = candidate.filter { it.isLetterOrDigit() }
|
private fun sanitizeFileName(candidate: String): String = candidate.filter { it.isLetterOrDigit() }
|
||||||
|
|||||||
+38
-25
@@ -54,6 +54,7 @@ import org.jetbrains.kotlin.gradle.utils.*
|
|||||||
import org.jetbrains.kotlin.gradle.utils.isParentOf
|
import org.jetbrains.kotlin.gradle.utils.isParentOf
|
||||||
import org.jetbrains.kotlin.gradle.utils.pathsAsStringRelativeTo
|
import org.jetbrains.kotlin.gradle.utils.pathsAsStringRelativeTo
|
||||||
import org.jetbrains.kotlin.incremental.ChangedFiles
|
import org.jetbrains.kotlin.incremental.ChangedFiles
|
||||||
|
import org.jetbrains.kotlin.incremental.IncrementalCompilerRunner
|
||||||
import org.jetbrains.kotlin.library.impl.isKotlinLibrary
|
import org.jetbrains.kotlin.library.impl.isKotlinLibrary
|
||||||
import org.jetbrains.kotlin.utils.JsLibraryUtils
|
import org.jetbrains.kotlin.utils.JsLibraryUtils
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -274,6 +275,16 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments> : AbstractKotl
|
|||||||
@get:Input
|
@get:Input
|
||||||
internal val moduleName: Property<String> = objects.property(String::class.java)
|
internal val moduleName: Property<String> = objects.property(String::class.java)
|
||||||
|
|
||||||
|
@get:Internal
|
||||||
|
val abiSnapshotFile
|
||||||
|
get() = taskBuildDirectory.file(IncrementalCompilerRunner.ABI_SNAPSHOT_FILE_NAME)
|
||||||
|
|
||||||
|
@get:Input
|
||||||
|
val abiSnapshotRelativePath: Property<String> = objects.property(String::class.java).value(
|
||||||
|
//TODO update to support any jar changes
|
||||||
|
"$name/${IncrementalCompilerRunner.ABI_SNAPSHOT_FILE_NAME}"
|
||||||
|
)
|
||||||
|
|
||||||
@get:Internal
|
@get:Internal
|
||||||
internal val friendSourceSets = objects.listProperty(String::class.java)
|
internal val friendSourceSets = objects.listProperty(String::class.java)
|
||||||
|
|
||||||
@@ -300,34 +311,36 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments> : AbstractKotl
|
|||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
fun execute(inputs: IncrementalTaskInputs) {
|
fun execute(inputs: IncrementalTaskInputs) {
|
||||||
systemPropertiesService.get().startIntercept()
|
metrics.measure(BuildTime.GRADLE_TASK_ACTION) {
|
||||||
CompilerSystemProperties.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY.value = "true"
|
systemPropertiesService.get().startIntercept()
|
||||||
|
CompilerSystemProperties.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY.value = "true"
|
||||||
|
|
||||||
// If task throws exception, but its outputs are changed during execution,
|
// If task throws exception, but its outputs are changed during execution,
|
||||||
// then Gradle forces next build to be non-incremental (see Gradle's DefaultTaskArtifactStateRepository#persistNewOutputs)
|
// then Gradle forces next build to be non-incremental (see Gradle's DefaultTaskArtifactStateRepository#persistNewOutputs)
|
||||||
// To prevent this, we backup outputs before incremental build and restore when exception is thrown
|
// To prevent this, we backup outputs before incremental build and restore when exception is thrown
|
||||||
val outputsBackup: TaskOutputsBackup? =
|
val outputsBackup: TaskOutputsBackup? =
|
||||||
if (isIncrementalCompilationEnabled() && inputs.isIncremental)
|
if (isIncrementalCompilationEnabled() && inputs.isIncremental)
|
||||||
metrics.measure(BuildTime.BACKUP_OUTPUT) {
|
metrics.measure(BuildTime.BACKUP_OUTPUT) {
|
||||||
TaskOutputsBackup(allOutputFiles())
|
TaskOutputsBackup(allOutputFiles())
|
||||||
}
|
}
|
||||||
else null
|
else null
|
||||||
|
|
||||||
if (!isIncrementalCompilationEnabled()) {
|
if (!isIncrementalCompilationEnabled()) {
|
||||||
clearLocalState("IC is disabled")
|
clearLocalState("IC is disabled")
|
||||||
} else if (!inputs.isIncremental) {
|
} else if (!inputs.isIncremental) {
|
||||||
clearLocalState("Task cannot run incrementally")
|
clearLocalState("Task cannot run incrementally")
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
executeImpl(inputs)
|
executeImpl(inputs)
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
if (outputsBackup != null) {
|
if (outputsBackup != null) {
|
||||||
metrics.measure(BuildTime.RESTORE_OUTPUT_FROM_BACKUP) {
|
metrics.measure(BuildTime.RESTORE_OUTPUT_FROM_BACKUP) {
|
||||||
outputsBackup.restoreOutputs()
|
outputsBackup.restoreOutputs()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
throw t
|
||||||
}
|
}
|
||||||
throw t
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user