Rebuild module on plugin classpaths change

Add another one reason for rebuild: if pluginClasspaths was changed or even its jar content, module will be marked for rebuild

Merge-request: KT-MR-5839
Merged-by: Aleksei Cherepanov <aleksei.cherepanov@jetbrains.com>
This commit is contained in:
Aleksei.Cherepanov
2022-04-18 11:06:15 +00:00
committed by Space
parent 281e381223
commit e0029b14ee
8 changed files with 133 additions and 17 deletions
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.build
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.config.PluginClasspaths
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
import kotlin.reflect.KClass
@@ -23,6 +24,7 @@ interface BuildMetaInfo {
val ownVersion: Int
val coroutinesVersion: Int
val multiplatformVersion: Int
val pluginClasspaths: String
}
abstract class BuildMetaInfoFactory<T : BuildMetaInfo>(private val metaInfoClass: KClass<T>) {
@@ -35,7 +37,8 @@ abstract class BuildMetaInfoFactory<T : BuildMetaInfo>(private val metaInfoClass
ownVersion: Int,
coroutinesVersion: Int,
multiplatformVersion: Int,
metadataVersionArray: IntArray?
metadataVersionArray: IntArray?,
pluginClasspaths: String
): T
fun create(args: CommonCompilerArguments): T {
@@ -50,7 +53,8 @@ abstract class BuildMetaInfoFactory<T : BuildMetaInfo>(private val metaInfoClass
ownVersion = OWN_VERSION,
coroutinesVersion = COROUTINES_VERSION,
multiplatformVersion = MULTIPLATFORM_VERSION,
metadataVersionArray = args.metadataVersion?.let { BinaryVersion.parseVersionArray(it) }
metadataVersionArray = args.metadataVersion?.let { BinaryVersion.parseVersionArray(it) },
pluginClasspaths = PluginClasspaths(args.pluginClasspaths).serialize()
)
}
@@ -21,7 +21,8 @@ data class CommonBuildMetaInfo(
override val metadataVersionPatch: Int,
override val ownVersion: Int,
override val coroutinesVersion: Int,
override val multiplatformVersion: Int
override val multiplatformVersion: Int,
override val pluginClasspaths: String
) : BuildMetaInfo {
companion object : BuildMetaInfoFactory<CommonBuildMetaInfo>(CommonBuildMetaInfo::class) {
override fun create(
@@ -33,7 +34,8 @@ data class CommonBuildMetaInfo(
ownVersion: Int,
coroutinesVersion: Int,
multiplatformVersion: Int,
metadataVersionArray: IntArray?
metadataVersionArray: IntArray?,
pluginClasspaths: String
): CommonBuildMetaInfo {
val metadataVersion = metadataVersionArray?.let(::JvmMetadataVersion) ?: JvmMetadataVersion.INSTANCE
return CommonBuildMetaInfo(
@@ -47,7 +49,8 @@ data class CommonBuildMetaInfo(
metadataVersionPatch = metadataVersion.patch,
ownVersion = ownVersion,
coroutinesVersion = coroutinesVersion,
multiplatformVersion = multiplatformVersion
multiplatformVersion = multiplatformVersion,
pluginClasspaths = pluginClasspaths
)
}
}
@@ -21,7 +21,8 @@ data class JsBuildMetaInfo(
override val metadataVersionPatch: Int,
override val ownVersion: Int,
override val coroutinesVersion: Int,
override val multiplatformVersion: Int
override val multiplatformVersion: Int,
override val pluginClasspaths: String
) : BuildMetaInfo {
companion object : BuildMetaInfoFactory<JsBuildMetaInfo>(JsBuildMetaInfo::class) {
override fun create(
@@ -33,7 +34,8 @@ data class JsBuildMetaInfo(
ownVersion: Int,
coroutinesVersion: Int,
multiplatformVersion: Int,
metadataVersionArray: IntArray?
metadataVersionArray: IntArray?,
pluginClasspaths: String
): JsBuildMetaInfo {
val metadataVersion = metadataVersionArray?.let(::JsMetadataVersion) ?: JsMetadataVersion.INSTANCE
return JsBuildMetaInfo(
@@ -47,7 +49,8 @@ data class JsBuildMetaInfo(
metadataVersionPatch = metadataVersion.patch,
ownVersion = ownVersion,
coroutinesVersion = coroutinesVersion,
multiplatformVersion = multiplatformVersion
multiplatformVersion = multiplatformVersion,
pluginClasspaths = pluginClasspaths
)
}
}
@@ -36,7 +36,8 @@ data class JvmBuildMetaInfo(
val bytecodeVersionPatch: Int,
override val ownVersion: Int,
override val coroutinesVersion: Int,
override val multiplatformVersion: Int
override val multiplatformVersion: Int,
override val pluginClasspaths: String
) : BuildMetaInfo {
companion object : BuildMetaInfoFactory<JvmBuildMetaInfo>(JvmBuildMetaInfo::class) {
override fun create(
@@ -48,7 +49,8 @@ data class JvmBuildMetaInfo(
ownVersion: Int,
coroutinesVersion: Int,
multiplatformVersion: Int,
metadataVersionArray: IntArray?
metadataVersionArray: IntArray?,
pluginClasspaths: String
): JvmBuildMetaInfo {
val metadataVersion = metadataVersionArray?.let(::JvmMetadataVersion) ?: JvmMetadataVersion.INSTANCE
return JvmBuildMetaInfo(
@@ -65,7 +67,8 @@ data class JvmBuildMetaInfo(
bytecodeVersionPatch = JvmBytecodeBinaryVersion.INSTANCE.patch,
ownVersion = ownVersion,
coroutinesVersion = coroutinesVersion,
multiplatformVersion = multiplatformVersion
multiplatformVersion = multiplatformVersion,
pluginClasspaths = pluginClasspaths
)
}
}
@@ -41,7 +41,8 @@ class BuildMetaInfoTest : TestCase() {
"metadataVersionPatch",
"multiplatformEnable",
"multiplatformVersion",
"ownVersion"
"ownVersion",
"pluginClasspaths"
)
assertEquals(expectedKeys, actual.split("\r\n", "\n").map { line -> line.split("=").first() })
}
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2022 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.config
import java.io.File
import java.security.DigestInputStream
import java.security.MessageDigest
class PluginClasspaths(val classpaths: Array<String>?) {
companion object {
fun deserializeWithHashes(str: String): List<Pair<String, String>> =
str.split(":")
.filter(String::isNotBlank)
.map { Pair(File(it.substringBeforeLast("-")).name, it.substringAfterLast("-")) }
}
fun serialize() = classpaths?.mapNotNull { it ->
val jar = File(it).takeIf { it.exists() } ?: return@mapNotNull null
val jarPath = jar.absolutePath
val jarHash = jar.sha256()
"$jarPath-$jarHash"
}?.joinToString(":") ?: ""
private fun File.sha256(): String {
val digest = MessageDigest.getInstance("SHA-256")
DigestInputStream(this.inputStream(), digest).use { dis ->
val buffer = ByteArray(8192)
var bytesRead = 0
while (bytesRead != -1) {
bytesRead = dis.read(buffer, 0, buffer.size)
}
}
// Convert to hex:
return digest.digest().joinToString("") {
Integer.toHexString((it.toInt() and 0xff) + 0x100).substring(1)
}
}
}
class PluginClasspathsComparator(old: String, new: String) {
private val oldPluginClasspath: List<Pair<String, String>> by lazy { PluginClasspaths.deserializeWithHashes(old) }
private val newPluginClasspath: List<Pair<String, String>> by lazy { PluginClasspaths.deserializeWithHashes(new) }
fun equals() = oldPluginClasspath == newPluginClasspath
fun describeDifferencesOrNull(): String? {
val changed = oldPluginClasspath != newPluginClasspath
if (!changed) return null
val added = newPluginClasspath.subtract(oldPluginClasspath).takeIf { it.isNotEmpty() }?.toList()
val deleted = oldPluginClasspath.subtract(newPluginClasspath).takeIf { it.isNotEmpty() }?.toList()
return StringBuilder().apply {
if (added == null && deleted == null) {
this.append("Plugin classpaths was reordered.")
return@apply
}
this.append("Plugin classpaths was changed.")
if (added != null) this.append(" Added: ${added.joinToString(", ")}.")
if (deleted != null) this.append(" Deleted: ${deleted.joinToString(", ")}.")
this.append(" Rebuild initiated.")
}.toString()
}
}
@@ -45,9 +45,11 @@ import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.JvmCodegenUtil
import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.config.KotlinFacetSettings
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.jps.build.KotlinJpsBuildTestBase.LibraryDependency.*
import org.jetbrains.kotlin.jps.incremental.CacheAttributesDiff
import org.jetbrains.kotlin.jps.model.JpsKotlinFacetModuleExtension
import org.jetbrains.kotlin.jps.model.kotlinCommonCompilerArguments
import org.jetbrains.kotlin.jps.model.kotlinCompilerArguments
import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
@@ -952,6 +954,38 @@ open class KotlinJpsBuildTest : KotlinJpsBuildTestBase() {
buildAllModules().assertSuccessful()
}
@WorkingDir("KotlinProject")
fun testModuleRebuildOnPluginClasspathsChange() {
initProject(JVM_MOCK_RUNTIME)
myProject.modules.forEach {
val facet = KotlinFacetSettings()
facet.useProjectSettings = false
facet.compilerArguments = K2JVMCompilerArguments()
facet.compilerArguments?.pluginClasspaths = arrayOf(PathUtil.kotlinPathsForDistDirectory.lombokPluginJarPath.path)
it.container.setChild(
JpsKotlinFacetModuleExtension.KIND,
JpsKotlinFacetModuleExtension(facet)
)
}
buildAllModules().assertSuccessful()
myProject.modules.forEach {
val facet = KotlinFacetSettings()
facet.useProjectSettings = false
facet.compilerArguments = K2JVMCompilerArguments()
facet.compilerArguments?.pluginClasspaths = arrayOf(
PathUtil.kotlinPathsForDistDirectory.lombokPluginJarPath.path,
PathUtil.kotlinPathsForDistDirectory.allOpenPluginJarPath.path
)
it.container.setChild(
JpsKotlinFacetModuleExtension.KIND,
JpsKotlinFacetModuleExtension(facet)
)
}
checkWhen(emptyArray(), null, packageClasses("kotlinProject", "src/test1.kt", "Test1Kt"))
}
private fun BuildResult.checkErrors() {
val actualErrors = getMessages(BuildMessage.Kind.ERROR)
.map { it as CompilerMessage }
@@ -21,9 +21,7 @@ import org.jetbrains.kotlin.build.GeneratedFile
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.incremental.ChangesCollector
import org.jetbrains.kotlin.incremental.ExpectActualTrackerImpl
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
@@ -339,7 +337,8 @@ abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo> intern
val prevBuildMetaInfo =
try {
buildMetaInfoFactory.deserializeFromString(Files.newInputStream(file).bufferedReader().use { it.readText() }) ?: return false
buildMetaInfoFactory.deserializeFromString(Files.newInputStream(file).bufferedReader().use { it.readText() })
?: return false
} catch (e: Exception) {
KotlinBuilder.LOG.error("Could not deserialize build meta info", e)
return false
@@ -355,7 +354,10 @@ abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo> intern
// If EAP->Non-EAP build with IC, then rebuild all kotlin
"Last build was compiled with EAP-plugin"
}
else -> null
else -> PluginClasspathsComparator(
prevBuildMetaInfo.pluginClasspaths,
buildMetaInfo.pluginClasspaths
).describeDifferencesOrNull()
}
if (reasonToRebuild != null) {