[JPS] Rebuild module on facet change

The logic of detecting changes in Kotlin facets was changed from "Include selected fields" to "Include all compiler arguments and exclude selected". This will help to avoid multiple IC issues when new change-sensitive compiler arguments will be added

(#KTIJ-17137, #KT-51536, #KTIJ-17170, #KTIJ-17300, #KT-47983) Fixed

Merge-request: KT-MR-7455
Merged-by: Aleksei Cherepanov <aleksei.cherepanov@jetbrains.com>
This commit is contained in:
Aleksei.Cherepanov
2022-10-26 09:45:27 +00:00
committed by Space Team
parent 50cd560d09
commit 26e7c29a91
17 changed files with 416 additions and 355 deletions
@@ -6,70 +6,132 @@
package org.jetbrains.kotlin.build package org.jetbrains.kotlin.build
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.config.KotlinCompilerVersion import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.LanguageVersion import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.config.PluginClasspaths import org.jetbrains.kotlin.config.PluginClasspaths
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion import org.jetbrains.kotlin.config.PluginClasspathsComparator
import kotlin.reflect.KClass
interface BuildMetaInfo { abstract class BuildMetaInfo {
val isEAP: Boolean enum class CustomKeys {
val compilerBuildVersion: String LANGUAGE_VERSION_STRING, IS_EAP, METADATA_VERSION_STRING, PLUGIN_CLASSPATHS, API_VERSION_STRING
val languageVersionString: String
val apiVersionString: String
val multiplatformEnable: Boolean
val metadataVersionMajor: Int
val metadataVersionMinor: Int
val metadataVersionPatch: Int
val ownVersion: Int
val coroutinesVersion: Int
val multiplatformVersion: Int
val pluginClasspaths: String
}
abstract class BuildMetaInfoFactory<T : BuildMetaInfo>(private val metaInfoClass: KClass<T>) {
protected abstract fun create(
isEAP: Boolean,
compilerBuildVersion: String,
languageVersionString: String,
apiVersionString: String,
multiplatformEnable: Boolean,
ownVersion: Int,
coroutinesVersion: Int,
multiplatformVersion: Int,
metadataVersionArray: IntArray?,
pluginClasspaths: String
): T
fun create(args: CommonCompilerArguments): T {
val languageVersion = args.languageVersion?.let { LanguageVersion.fromVersionString(it) } ?: LanguageVersion.LATEST_STABLE
return create(
isEAP = !languageVersion.isStable,
compilerBuildVersion = KotlinCompilerVersion.VERSION,
languageVersionString = languageVersion.versionString,
apiVersionString = args.apiVersion ?: languageVersion.versionString,
multiplatformEnable = args.multiPlatform,
ownVersion = OWN_VERSION,
coroutinesVersion = COROUTINES_VERSION,
multiplatformVersion = MULTIPLATFORM_VERSION,
metadataVersionArray = args.metadataVersion?.let { BinaryVersion.parseVersionArray(it) },
pluginClasspaths = PluginClasspaths(args.pluginClasspaths).serialize()
)
} }
fun serializeToString(args: CommonCompilerArguments): String = fun obtainReasonForRebuild(currentCompilerArgumentsMap: Map<String, String>, previousCompilerArgsMap: Map<String, String>): String? {
serializeToString(create(args)) if (currentCompilerArgumentsMap.keys != previousCompilerArgsMap.keys) {
return "Compiler arguments version was changed"
}
fun serializeToString(info: T): String = val changedCompilerArguments = currentCompilerArgumentsMap.mapNotNull {
serializeToPlainText(info, metaInfoClass) val key = it.key
val previousValue = previousCompilerArgsMap[it.key] ?: return@mapNotNull key
val currentValue = it.value
return@mapNotNull if (compareIsChanged(key, currentValue, previousValue)) key else null
}
fun deserializeFromString(str: String): T? = if (changedCompilerArguments.isNotEmpty()) {
deserializeFromPlainText(str, metaInfoClass) val rebuildReason = when (changedCompilerArguments.size) {
1 -> "One of compiler arguments was changed: "
companion object { else -> "Some compiler arguments were changed: "
const val OWN_VERSION: Int = 0 } + changedCompilerArguments.joinToReadableString()
const val COROUTINES_VERSION: Int = 0 return rebuildReason
const val MULTIPLATFORM_VERSION: Int = 0 }
return null
} }
private fun compareIsChanged(key: String, currentValue: String, previousValue: String): Boolean {
// check for specific key changes
checkIfPlatformSpecificCompilerArgumentWasChanged(key, currentValue, previousValue)?.let { comparisonResult ->
return comparisonResult
}
when (key) {
CustomKeys.LANGUAGE_VERSION_STRING.name ->
return LanguageVersion.fromVersionString(currentValue) != LanguageVersion.fromVersionString(previousValue)
CustomKeys.API_VERSION_STRING.name -> return ApiVersion.parse(currentValue) != ApiVersion.parse(previousValue)
CustomKeys.PLUGIN_CLASSPATHS.name -> return !PluginClasspathsComparator(previousValue, currentValue).equals()
}
// check keys that are sensitive for true -> false change
if (key in argumentsListForSpecialCheck) {
return previousValue == "true" && currentValue != "true"
}
// compare all other change-sensitive values
if (previousValue != currentValue) {
return true
}
return false
}
open fun checkIfPlatformSpecificCompilerArgumentWasChanged(key: String, currentValue: String, previousValue: String): Boolean? {
return null
}
open fun createPropertiesMapFromCompilerArguments(args: CommonCompilerArguments): Map<String, String> {
val resultMap = transformClassToPropertiesMap(args, excludedProperties).toMutableMap()
val languageVersion = args.languageVersion?.let { LanguageVersion.fromVersionString(it) }
?: LanguageVersion.LATEST_STABLE
val languageVersionSting = languageVersion.versionString
resultMap[CustomKeys.LANGUAGE_VERSION_STRING.name] = languageVersionSting
val isEAP = !languageVersion.isStable
resultMap[CustomKeys.IS_EAP.name] = isEAP.toString()
val apiVersionString = args.apiVersion ?: languageVersionSting
resultMap[CustomKeys.API_VERSION_STRING.name] = apiVersionString
val pluginClasspaths = PluginClasspaths(args.pluginClasspaths).serialize()
resultMap[CustomKeys.PLUGIN_CLASSPATHS.name] = pluginClasspaths
return resultMap
}
fun deserializeMapFromString(inputString: String): Map<String, String> = inputString
.split("\n")
.filter(String::isNotBlank)
.associate { it.substringBefore("=") to it.substringAfter("=") }
private fun serializeMapToString(myList: Map<String, String>) = myList.map { "${it.key}=${it.value}" }.joinToString("\n")
fun serializeArgsToString(args: CommonCompilerArguments) = serializeMapToString(createPropertiesMapFromCompilerArguments(args))
open val excludedProperties = listOf(
"languageVersion",
"apiVersion",
"pluginClasspaths",
"metadataVersion",
"dumpDirectory",
"dumpOnlyFqName",
"dumpPerf",
"errors",
"extraHelp",
"freeArgs",
"help",
"intellijPluginRoot",
"kotlinHome",
"listPhases",
"namesExcludedFromDumping",
"phasesToDump",
"phasesToDumpAfter",
"phasesToDumpBefore",
"profilePhases",
"renderInternalDiagnosticNames",
"reportOutputFiles",
"reportPerf",
"script",
"verbose",
"verbosePhases",
"version"
)
open val argumentsListForSpecialCheck = listOf(
"allowAnyScriptsInSourceRoots",
"allowKotlinPackage",
"allowResultReturnType",
"noCheckActual",
"skipMetadataVersionCheck",
"skipPrereleaseCheck",
"suppressVersionWarnings",
"suppressWarnings",
CustomKeys.IS_EAP.name
)
} }
@@ -5,53 +5,34 @@
package org.jetbrains.kotlin.build package org.jetbrains.kotlin.build
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
/** class CommonBuildMetaInfo : BuildMetaInfo() {
* If you want to add a new field, check its type is supported by [serializeToPlainText], [deserializeFromPlainText] override fun checkIfPlatformSpecificCompilerArgumentWasChanged(key: String, currentValue: String, previousValue: String): Boolean? {
*/ when (key) {
data class CommonBuildMetaInfo( CustomKeys.METADATA_VERSION_STRING.name -> {
override val isEAP: Boolean, val currentVersionIntArray = BinaryVersion.parseVersionArray(currentValue)
override val compilerBuildVersion: String, if (currentVersionIntArray?.size != 3) return null
override val languageVersionString: String, val currentVersion = JvmMetadataVersion(currentVersionIntArray[0], currentVersionIntArray[1], currentVersionIntArray[2])
override val apiVersionString: String,
override val multiplatformEnable: Boolean, val previousVersionIntArray = BinaryVersion.parseVersionArray(previousValue)
override val metadataVersionMajor: Int, if (previousVersionIntArray?.size != 3) return null
override val metadataVersionMinor: Int, val previousVersion = JvmMetadataVersion(previousVersionIntArray[0], previousVersionIntArray[1], previousVersionIntArray[2])
override val metadataVersionPatch: Int, return currentVersion == previousVersion
override val ownVersion: Int, }
override val coroutinesVersion: Int,
override val multiplatformVersion: Int,
override val pluginClasspaths: String
) : BuildMetaInfo {
companion object : BuildMetaInfoFactory<CommonBuildMetaInfo>(CommonBuildMetaInfo::class) {
override fun create(
isEAP: Boolean,
compilerBuildVersion: String,
languageVersionString: String,
apiVersionString: String,
multiplatformEnable: Boolean,
ownVersion: Int,
coroutinesVersion: Int,
multiplatformVersion: Int,
metadataVersionArray: IntArray?,
pluginClasspaths: String
): CommonBuildMetaInfo {
val metadataVersion = metadataVersionArray?.let(::JvmMetadataVersion) ?: JvmMetadataVersion.INSTANCE
return CommonBuildMetaInfo(
isEAP = isEAP,
compilerBuildVersion = compilerBuildVersion,
languageVersionString = languageVersionString,
apiVersionString = apiVersionString,
multiplatformEnable = multiplatformEnable,
metadataVersionMajor = metadataVersion.major,
metadataVersionMinor = metadataVersion.minor,
metadataVersionPatch = metadataVersion.patch,
ownVersion = ownVersion,
coroutinesVersion = coroutinesVersion,
multiplatformVersion = multiplatformVersion,
pluginClasspaths = pluginClasspaths
)
} }
return null
}
override fun createPropertiesMapFromCompilerArguments(args: CommonCompilerArguments): Map<String, String> {
val resultMap = mutableMapOf<String, String>()
val metadataVersionArray = args.metadataVersion?.let { BinaryVersion.parseVersionArray(it) }
val metadataVersion = metadataVersionArray?.let(::JvmMetadataVersion) ?: JvmMetadataVersion.INSTANCE
val metadataVersionString = metadataVersion.toString()
resultMap[CustomKeys.METADATA_VERSION_STRING.name] = metadataVersionString
return super.createPropertiesMapFromCompilerArguments(args) + resultMap
} }
} }
@@ -5,53 +5,37 @@
package org.jetbrains.kotlin.build package org.jetbrains.kotlin.build
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
import org.jetbrains.kotlin.utils.JsMetadataVersion import org.jetbrains.kotlin.utils.JsMetadataVersion
/** class JsBuildMetaInfo : BuildMetaInfo() {
* If you want to add a new field, check its type is supported by [serializeToPlainText], [deserializeFromPlainText] override fun checkIfPlatformSpecificCompilerArgumentWasChanged(key: String, currentValue: String, previousValue: String): Boolean? {
*/ when (key) {
data class JsBuildMetaInfo( CustomKeys.METADATA_VERSION_STRING.name -> {
override val isEAP: Boolean, val currentValueIntArray = BinaryVersion.parseVersionArray(currentValue)
override val compilerBuildVersion: String, if (currentValueIntArray?.size != 3) return null
override val languageVersionString: String, val currentVersion = JsMetadataVersion(currentValueIntArray[0], currentValueIntArray[1], currentValueIntArray[2])
override val apiVersionString: String,
override val multiplatformEnable: Boolean, val previousValueIntArray = BinaryVersion.parseVersionArray(previousValue)
override val metadataVersionMajor: Int, if (previousValueIntArray?.size != 3) return null
override val metadataVersionMinor: Int, val previousVersion = JsMetadataVersion(previousValueIntArray[0], previousValueIntArray[1], previousValueIntArray[2])
override val metadataVersionPatch: Int, return currentVersion == previousVersion
override val ownVersion: Int, }
override val coroutinesVersion: Int,
override val multiplatformVersion: Int,
override val pluginClasspaths: String
) : BuildMetaInfo {
companion object : BuildMetaInfoFactory<JsBuildMetaInfo>(JsBuildMetaInfo::class) {
override fun create(
isEAP: Boolean,
compilerBuildVersion: String,
languageVersionString: String,
apiVersionString: String,
multiplatformEnable: Boolean,
ownVersion: Int,
coroutinesVersion: Int,
multiplatformVersion: Int,
metadataVersionArray: IntArray?,
pluginClasspaths: String
): JsBuildMetaInfo {
val metadataVersion = metadataVersionArray?.let(::JsMetadataVersion) ?: JsMetadataVersion.INSTANCE
return JsBuildMetaInfo(
isEAP = isEAP,
compilerBuildVersion = compilerBuildVersion,
languageVersionString = languageVersionString,
apiVersionString = apiVersionString,
multiplatformEnable = multiplatformEnable,
metadataVersionMajor = metadataVersion.major,
metadataVersionMinor = metadataVersion.minor,
metadataVersionPatch = metadataVersion.patch,
ownVersion = ownVersion,
coroutinesVersion = coroutinesVersion,
multiplatformVersion = multiplatformVersion,
pluginClasspaths = pluginClasspaths
)
} }
return null
} }
override fun createPropertiesMapFromCompilerArguments(args: CommonCompilerArguments): Map<String, String> {
val resultMap = mutableMapOf<String, String>()
val metadataVersionArray = args.metadataVersion?.let { BinaryVersion.parseVersionArray(it) }
val metadataVersion = metadataVersionArray?.let(::JsMetadataVersion) ?: JsMetadataVersion.INSTANCE
val metadataVersionString = metadataVersion.toInteger().toString()
resultMap[CustomKeys.METADATA_VERSION_STRING.name] = metadataVersionString
return super.createPropertiesMapFromCompilerArguments(args) + resultMap
}
override val argumentsListForSpecialCheck: List<String>
get() = super.argumentsListForSpecialCheck + listOf("sourceMap", "metaInfo" + "partialLinkage" + "wasmDebug")
} }
@@ -16,60 +16,62 @@
package org.jetbrains.kotlin.build package org.jetbrains.kotlin.build
import org.jetbrains.kotlin.load.kotlin.JvmBytecodeBinaryVersion import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
/** class JvmBuildMetaInfo : BuildMetaInfo() {
* If you want to add a new field, check its type is supported by [serializeToPlainText], [deserializeFromPlainText] override fun checkIfPlatformSpecificCompilerArgumentWasChanged(key: String, currentValue: String, previousValue: String): Boolean? {
*/ when (key) {
data class JvmBuildMetaInfo( CustomKeys.METADATA_VERSION_STRING.name -> {
override val isEAP: Boolean, val currentVersionIntArray = BinaryVersion.parseVersionArray(currentValue)
override val compilerBuildVersion: String, if (currentVersionIntArray?.size != 3) return null
override val languageVersionString: String, val currentVersion = JvmMetadataVersion(currentVersionIntArray[0], currentVersionIntArray[1], currentVersionIntArray[2])
override val apiVersionString: String,
override val multiplatformEnable: Boolean, val previousVersionIntArray = BinaryVersion.parseVersionArray(previousValue)
override val metadataVersionMajor: Int, if (previousVersionIntArray?.size != 3) return null
override val metadataVersionMinor: Int, val previousVersion = JvmMetadataVersion(previousVersionIntArray[0], previousVersionIntArray[1], previousVersionIntArray[2])
override val metadataVersionPatch: Int, return currentVersion != previousVersion
val bytecodeVersionMajor: Int, }
val bytecodeVersionMinor: Int,
val bytecodeVersionPatch: Int,
override val ownVersion: Int,
override val coroutinesVersion: Int,
override val multiplatformVersion: Int,
override val pluginClasspaths: String
) : BuildMetaInfo {
companion object : BuildMetaInfoFactory<JvmBuildMetaInfo>(JvmBuildMetaInfo::class) {
override fun create(
isEAP: Boolean,
compilerBuildVersion: String,
languageVersionString: String,
apiVersionString: String,
multiplatformEnable: Boolean,
ownVersion: Int,
coroutinesVersion: Int,
multiplatformVersion: Int,
metadataVersionArray: IntArray?,
pluginClasspaths: String
): JvmBuildMetaInfo {
val metadataVersion = metadataVersionArray?.let(::JvmMetadataVersion) ?: JvmMetadataVersion.INSTANCE
return JvmBuildMetaInfo(
isEAP = isEAP,
compilerBuildVersion = compilerBuildVersion,
languageVersionString = languageVersionString,
apiVersionString = apiVersionString,
multiplatformEnable = multiplatformEnable,
metadataVersionMajor = metadataVersion.major,
metadataVersionMinor = metadataVersion.minor,
metadataVersionPatch = metadataVersion.patch,
bytecodeVersionMajor = JvmBytecodeBinaryVersion.INSTANCE.major,
bytecodeVersionMinor = JvmBytecodeBinaryVersion.INSTANCE.minor,
bytecodeVersionPatch = JvmBytecodeBinaryVersion.INSTANCE.patch,
ownVersion = ownVersion,
coroutinesVersion = coroutinesVersion,
multiplatformVersion = multiplatformVersion,
pluginClasspaths = pluginClasspaths
)
} }
return null
} }
override fun createPropertiesMapFromCompilerArguments(args: CommonCompilerArguments): Map<String, String> {
val resultMap = mutableMapOf<String, String>()
val metadataVersionArray = args.metadataVersion?.let { BinaryVersion.parseVersionArray(it) }
val metadataVersion = metadataVersionArray?.let(::JvmMetadataVersion) ?: JvmMetadataVersion.INSTANCE
val metadataVersionString = metadataVersion.toString()
resultMap[CustomKeys.METADATA_VERSION_STRING.name] = metadataVersionString
return super.createPropertiesMapFromCompilerArguments(args) + resultMap
}
override val excludedProperties: List<String>
get() = super.excludedProperties + listOf(
"excludedProperties",
"backendThreads",
"buildFile",
"classpath",
"declarationsOutputPath",
"defaultScriptExtension",
"enableDebugMode",
"expression",
"internalArguments",
"profileCompilerCommand",
"repeatCompileModules",
"scriptResolverEnvironment",
"scriptTemplates",
"suppressDeprecatedJvmTargetWarning",
"useFastJarFileSystem",
)
override val argumentsListForSpecialCheck: List<String>
get() = super.argumentsListForSpecialCheck + listOf(
"allowNoSourceFiles",
"allowUnstableDependencies",
"enableJvmPreview",
"ignoreConstOptimizationErrors",
"suppressMissingBuiltinsError",
)
} }
@@ -16,6 +16,8 @@
package org.jetbrains.kotlin.build package org.jetbrains.kotlin.build
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.collectProperties
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.full.memberProperties import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.primaryConstructor import kotlin.reflect.full.primaryConstructor
@@ -38,12 +40,12 @@ inline fun <reified T : Any> deserializeFromPlainText(str: String): T? = deseria
fun <T : Any> deserializeFromPlainText(str: String, klass: KClass<T>): T? { fun <T : Any> deserializeFromPlainText(str: String, klass: KClass<T>): T? {
val args = ArrayList<Any?>() val args = ArrayList<Any?>()
val properties = str val properties = str
.split("\n") .split("\n")
.filter(String::isNotBlank) .filter(String::isNotBlank)
.associate { it.substringBefore("=") to it.substringAfter("=") } .associate { it.substringBefore("=") to it.substringAfter("=") }
val primaryConstructor = klass.primaryConstructor val primaryConstructor = klass.primaryConstructor
?: throw IllegalStateException("${klass.java} does not have primary constructor") ?: throw IllegalStateException("${klass.java} does not have primary constructor")
for (param in primaryConstructor.parameters.sortedBy { it.index }) { for (param in primaryConstructor.parameters.sortedBy { it.index }) {
val argumentString = properties[param.name] val argumentString = properties[param.name]
@@ -51,8 +53,7 @@ fun <T : Any> deserializeFromPlainText(str: String, klass: KClass<T>): T? {
if (param.type.isMarkedNullable) { if (param.type.isMarkedNullable) {
args.add(null) args.add(null)
continue continue
} } else {
else {
return null return null
} }
} }
@@ -69,3 +70,18 @@ fun <T : Any> deserializeFromPlainText(str: String, klass: KClass<T>): T? {
return primaryConstructor.call(*args.toTypedArray()) return primaryConstructor.call(*args.toTypedArray())
} }
@Suppress("UNCHECKED_CAST")
fun <T : Any> transformClassToPropertiesMap(classToTransform: T, excludedProperties: List<String> = emptyList()) =
collectProperties(classToTransform::class as KClass<T>, false)
.filter { property -> property.name !in excludedProperties }
.associateBy(
keySelector = { property -> property.name },
valueTransform = { property -> property.get(classToTransform).toString() })
fun List<String>.joinToReadableString(): String = when {
size > 5 -> take(5).joinToString() + " and ${size - 5} more"
size > 1 -> dropLast(1).joinToString() + " and ${last()}"
size == 1 -> single()
else -> ""
}
@@ -1,79 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.build
import junit.framework.TestCase
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.junit.Assert.assertNotEquals
import org.junit.Test
class BuildMetaInfoTest : TestCase() {
@Test
fun testJvmSerialization() {
val args = K2JVMCompilerArguments()
val info = JvmBuildMetaInfo.create(args)
val actual = JvmBuildMetaInfo.serializeToString(info)
val expectedKeys = listOf(
"apiVersionString",
"bytecodeVersionMajor",
"bytecodeVersionMinor",
"bytecodeVersionPatch",
"compilerBuildVersion",
"coroutinesVersion",
"isEAP",
"languageVersionString",
"metadataVersionMajor",
"metadataVersionMinor",
"metadataVersionPatch",
"multiplatformEnable",
"multiplatformVersion",
"ownVersion",
"pluginClasspaths"
)
assertEquals(expectedKeys, actual.split("\r\n", "\n").map { line -> line.split("=").first() })
}
@Test
fun testJvmSerializationDeserialization() {
val args = K2JVMCompilerArguments()
val info = JvmBuildMetaInfo.create(args)
val serialized = JvmBuildMetaInfo.serializeToString(info)
val deserialized = JvmBuildMetaInfo.deserializeFromString(serialized)
assertEquals(info, deserialized)
}
@Test
fun testJsSerializationDeserialization() {
val args = K2JVMCompilerArguments()
val info = JvmBuildMetaInfo.create(args)
val serialized = JvmBuildMetaInfo.serializeToString(info)
val deserialized = JvmBuildMetaInfo.deserializeFromString(serialized)
assertEquals(info, deserialized)
}
@Test
fun testJvmEquals() {
val args1 = K2JVMCompilerArguments()
val info1 = JvmBuildMetaInfo.create(args1)
val args2 = K2JVMCompilerArguments()
val info2 = JvmBuildMetaInfo.create(args2)
assertEquals(info1, info2)
assertEquals(info1, info2.copy())
}
}
@@ -1,9 +1,9 @@
/* /*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. * 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package org.jetbrains.kotlin.jps.build package org.jetbrains.kotlin.build
import junit.framework.TestCase import junit.framework.TestCase
@@ -112,4 +112,3 @@ fun CommonCompilerArguments.setApiVersionToLanguageVersionIfNeeded() {
apiVersion = languageVersion apiVersion = languageVersion
} }
} }
@@ -108,11 +108,11 @@ abstract class AbstractIncrementalLazyCachesTest : AbstractIncrementalJpsTest()
}.sortedBy { it.target.jpsModuleBuildTarget.presentableName } }.sortedBy { it.target.jpsModuleBuildTarget.presentableName }
allTargets.forEach { (chunk, target) -> allTargets.forEach { (chunk, target) ->
val metaBuildInfo = chunk.buildMetaInfoFile(target.jpsModuleBuildTarget) val compilerArgumentsFile = chunk.compilerArgumentsFile(target.jpsModuleBuildTarget)
dumpCachesForTarget( dumpCachesForTarget(
printer, paths, target.jpsModuleBuildTarget, printer, paths, target.jpsModuleBuildTarget,
target.localCacheVersionManager.versionFileForTesting, target.localCacheVersionManager.versionFileForTesting,
metaBuildInfo.toFile(), compilerArgumentsFile.toFile(),
subdirectory = KOTLIN_CACHE_DIRECTORY_NAME subdirectory = KOTLIN_CACHE_DIRECTORY_NAME
) )
} }
@@ -45,6 +45,7 @@ import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.JvmCodegenUtil import org.jetbrains.kotlin.codegen.JvmCodegenUtil
import org.jetbrains.kotlin.config.IncrementalCompilation import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.config.JvmDefaultMode
import org.jetbrains.kotlin.config.KotlinFacetSettings import org.jetbrains.kotlin.config.KotlinFacetSettings
import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.jps.build.KotlinJpsBuildTestBase.LibraryDependency.* import org.jetbrains.kotlin.jps.build.KotlinJpsBuildTestBase.LibraryDependency.*
@@ -828,6 +829,121 @@ open class KotlinJpsBuildTest : KotlinJpsBuildTestBase() {
checkWhen(emptyArray(), null, packageClasses("kotlinProject", "src/test1.kt", "Test1Kt")) checkWhen(emptyArray(), null, packageClasses("kotlinProject", "src/test1.kt", "Test1Kt"))
} }
@WorkingDir("KotlinProject")
fun testModuleRebuildOnJvmTargetChange() {
initProject(JVM_MOCK_RUNTIME)
myProject.modules.forEach {
val facet = KotlinFacetSettings()
facet.useProjectSettings = false
facet.compilerArguments = K2JVMCompilerArguments()
(facet.compilerArguments as K2JVMCompilerArguments).jvmTarget = "1.8"
it.container.setChild(
JpsKotlinFacetModuleExtension.KIND,
JpsKotlinFacetModuleExtension(facet)
)
}
buildAllModules().assertSuccessful()
myProject.modules.forEach {
val facet = KotlinFacetSettings()
facet.useProjectSettings = false
facet.compilerArguments = K2JVMCompilerArguments()
(facet.compilerArguments as K2JVMCompilerArguments).jvmTarget = "9"
it.container.setChild(
JpsKotlinFacetModuleExtension.KIND,
JpsKotlinFacetModuleExtension(facet)
)
}
checkWhen(emptyArray(), null, packageClasses("kotlinProject", "src/test1.kt", "Test1Kt"))
}
@WorkingDir("KotlinProject")
fun testModuleRebuildOnBackendChange() {
initProject(JVM_MOCK_RUNTIME)
myProject.modules.forEach {
val facet = KotlinFacetSettings()
facet.useProjectSettings = false
facet.compilerArguments = K2JVMCompilerArguments()
(facet.compilerArguments as K2JVMCompilerArguments).useK2 = false
it.container.setChild(
JpsKotlinFacetModuleExtension.KIND,
JpsKotlinFacetModuleExtension(facet)
)
}
buildAllModules().assertSuccessful()
myProject.modules.forEach {
val facet = KotlinFacetSettings()
facet.useProjectSettings = false
facet.compilerArguments = K2JVMCompilerArguments()
(facet.compilerArguments as K2JVMCompilerArguments).useK2 = true
it.container.setChild(
JpsKotlinFacetModuleExtension.KIND,
JpsKotlinFacetModuleExtension(facet)
)
}
checkWhen(emptyArray(), null, packageClasses("kotlinProject", "src/test1.kt", "Test1Kt"))
}
@WorkingDir("KotlinProject")
fun testModuleRebuildOnJvmDefaultChange() {
initProject(JVM_MOCK_RUNTIME)
myProject.modules.forEach {
val facet = KotlinFacetSettings()
facet.useProjectSettings = false
facet.compilerArguments = K2JVMCompilerArguments()
(facet.compilerArguments as K2JVMCompilerArguments).jvmDefault = JvmDefaultMode.DEFAULT.description
it.container.setChild(
JpsKotlinFacetModuleExtension.KIND,
JpsKotlinFacetModuleExtension(facet)
)
}
buildAllModules().assertSuccessful()
myProject.modules.forEach {
val facet = KotlinFacetSettings()
facet.useProjectSettings = false
facet.compilerArguments = K2JVMCompilerArguments()
(facet.compilerArguments as K2JVMCompilerArguments).jvmDefault = JvmDefaultMode.ALL_COMPATIBILITY.description
it.container.setChild(
JpsKotlinFacetModuleExtension.KIND,
JpsKotlinFacetModuleExtension(facet)
)
}
checkWhen(emptyArray(), null, packageClasses("kotlinProject", "src/test1.kt", "Test1Kt"))
}
@WorkingDir("KotlinProject")
fun testModuleRebuildOnAddJavaMoudlesChange() {
initProject(JVM_MOCK_RUNTIME)
myProject.modules.forEach {
val facet = KotlinFacetSettings()
facet.useProjectSettings = false
facet.compilerArguments = K2JVMCompilerArguments()
it.container.setChild(
JpsKotlinFacetModuleExtension.KIND,
JpsKotlinFacetModuleExtension(facet)
)
}
buildAllModules().assertSuccessful()
myProject.modules.forEach {
val facet = KotlinFacetSettings()
facet.useProjectSettings = false
facet.compilerArguments = K2JVMCompilerArguments()
(facet.compilerArguments as K2JVMCompilerArguments).additionalJavaModules = arrayOf("ALL-MODULE-PATH")
it.container.setChild(
JpsKotlinFacetModuleExtension.KIND,
JpsKotlinFacetModuleExtension(facet)
)
}
checkWhen(emptyArray(), null, packageClasses("kotlinProject", "src/test1.kt", "Test1Kt"))
}
fun testBuildAfterGdwBuild() { fun testBuildAfterGdwBuild() {
initProject(JVM_FULL_RUNTIME) initProject(JVM_FULL_RUNTIME)
findModule("module2").let { findModule("module2").let {
@@ -139,10 +139,8 @@ class KotlinChunk internal constructor(val context: KotlinCompileContext, val ta
} }
fun shouldRebuild(): Boolean { fun shouldRebuild(): Boolean {
val buildMetaInfo = representativeTarget.buildMetaInfoFactory.create(compilerArguments)
targets.forEach { target -> targets.forEach { target ->
if (target.isVersionChanged(this, buildMetaInfo)) { if (target.isVersionChanged(this, compilerArguments)) {
KotlinBuilder.LOG.info("$target version changed, rebuilding $this") KotlinBuilder.LOG.info("$target version changed, rebuilding $this")
return true return true
} }
@@ -157,10 +155,10 @@ class KotlinChunk internal constructor(val context: KotlinCompileContext, val ta
return false return false
} }
fun buildMetaInfoFile(target: ModuleBuildTarget): Path = context.dataPaths fun compilerArgumentsFile(target: ModuleBuildTarget): Path = context.dataPaths
.getTargetDataRoot(target) .getTargetDataRoot(target)
.toPath() .toPath()
.resolve(representativeTarget.buildMetaInfoFileName) .resolve(representativeTarget.compilerArgumentsFileName)
fun saveVersions() { fun saveVersions() {
context.ensureLookupsCacheAttributesSaved() context.ensureLookupsCacheAttributesSaved()
@@ -169,10 +167,10 @@ class KotlinChunk internal constructor(val context: KotlinCompileContext, val ta
it.initialLocalCacheAttributesDiff.manager.writeVersion() it.initialLocalCacheAttributesDiff.manager.writeVersion()
} }
val serializedMetaInfo = representativeTarget.buildMetaInfoFactory.serializeToString(compilerArguments) val serializedCompilerArguments = representativeTarget.buildMetaInfo.serializeArgsToString(compilerArguments)
targets.forEach { target -> targets.forEach { target ->
Files.newOutputStream(buildMetaInfoFile(target.jpsModuleBuildTarget)).bufferedWriter().use { it.append(serializedMetaInfo) } Files.newOutputStream(compilerArgumentsFile(target.jpsModuleBuildTarget)).bufferedWriter()
.use { it.append(serializedCompilerArguments) }
} }
} }
@@ -12,6 +12,7 @@ import org.jetbrains.jps.incremental.GlobalContextKey
import org.jetbrains.jps.incremental.fs.CompilationRound import org.jetbrains.jps.incremental.fs.CompilationRound
import org.jetbrains.jps.incremental.messages.BuildMessage import org.jetbrains.jps.incremental.messages.BuildMessage
import org.jetbrains.jps.incremental.messages.CompilerMessage import org.jetbrains.jps.incremental.messages.CompilerMessage
import org.jetbrains.kotlin.build.joinToReadableString
import org.jetbrains.kotlin.config.CompilerRunnerConstants.KOTLIN_COMPILER_NAME import org.jetbrains.kotlin.config.CompilerRunnerConstants.KOTLIN_COMPILER_NAME
import org.jetbrains.kotlin.incremental.LookupSymbol import org.jetbrains.kotlin.incremental.LookupSymbol
import org.jetbrains.kotlin.incremental.storage.FileToPathConverter import org.jetbrains.kotlin.incremental.storage.FileToPathConverter
@@ -303,10 +304,3 @@ class KotlinCompileContext(val jpsContext: CompileContext) {
} }
} }
} }
fun List<String>.joinToReadableString(): String = when {
size > 5 -> take(5).joinToString() + " and ${size - 5} more"
size > 1 -> dropLast(1).joinToString() + " and ${last()}"
size == 1 -> single()
else -> ""
}
@@ -34,12 +34,12 @@ class KotlinCommonModuleBuildTarget(kotlinContext: KotlinCompileContext, jpsModu
override val isIncrementalCompilationEnabled: Boolean override val isIncrementalCompilationEnabled: Boolean
get() = false get() = false
override val buildMetaInfoFactory override val compilerArgumentsFileName
get() = CommonBuildMetaInfo
override val buildMetaInfoFileName
get() = COMMON_BUILD_META_INFO_FILE_NAME get() = COMMON_BUILD_META_INFO_FILE_NAME
override val buildMetaInfo: CommonBuildMetaInfo
get() = CommonBuildMetaInfo()
override val globalLookupCacheId: String override val globalLookupCacheId: String
get() = "metadata-compiler" get() = "metadata-compiler"
@@ -54,12 +54,12 @@ class KotlinJsModuleBuildTarget(kotlinContext: KotlinCompileContext, jpsModuleBu
override val isIncrementalCompilationEnabled: Boolean override val isIncrementalCompilationEnabled: Boolean
get() = IncrementalCompilation.isEnabledForJs() get() = IncrementalCompilation.isEnabledForJs()
override val buildMetaInfoFactory override val compilerArgumentsFileName: String
get() = JsBuildMetaInfo
override val buildMetaInfoFileName: String
get() = JS_BUILD_META_INFO_FILE_NAME get() = JS_BUILD_META_INFO_FILE_NAME
override val buildMetaInfo: JsBuildMetaInfo
get() = JsBuildMetaInfo()
val isFirstBuild: Boolean val isFirstBuild: Boolean
get() { get() {
val targetDataRoot = jpsGlobalContext.projectDescriptor.dataManager.dataPaths.getTargetDataRoot(jpsModuleBuildTarget) val targetDataRoot = jpsGlobalContext.projectDescriptor.dataManager.dataPaths.getTargetDataRoot(jpsModuleBuildTarget)
@@ -62,12 +62,12 @@ class KotlinJvmModuleBuildTarget(kotlinContext: KotlinCompileContext, jpsModuleB
override fun createCacheStorage(paths: BuildDataPaths) = override fun createCacheStorage(paths: BuildDataPaths) =
JpsIncrementalJvmCache(jpsModuleBuildTarget, paths, kotlinContext.fileToPathConverter) JpsIncrementalJvmCache(jpsModuleBuildTarget, paths, kotlinContext.fileToPathConverter)
override val buildMetaInfoFactory override val compilerArgumentsFileName
get() = JvmBuildMetaInfo
override val buildMetaInfoFileName
get() = JVM_BUILD_META_INFO_FILE_NAME get() = JVM_BUILD_META_INFO_FILE_NAME
override val buildMetaInfo: JvmBuildMetaInfo
get() = JvmBuildMetaInfo()
override val targetId: TargetId override val targetId: TargetId
get() { get() {
val moduleName = module.k2JvmCompilerArguments.moduleName val moduleName = module.k2JvmCompilerArguments.moduleName
@@ -15,9 +15,7 @@ import org.jetbrains.jps.model.java.JpsJavaClasspathKind
import org.jetbrains.jps.model.java.JpsJavaExtensionService import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.model.module.JpsModule import org.jetbrains.jps.model.module.JpsModule
import org.jetbrains.jps.util.JpsPathUtil import org.jetbrains.jps.util.JpsPathUtil
import org.jetbrains.kotlin.build.BuildMetaInfo import org.jetbrains.kotlin.build.*
import org.jetbrains.kotlin.build.BuildMetaInfoFactory
import org.jetbrains.kotlin.build.GeneratedFile
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment
@@ -306,7 +304,7 @@ abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo> intern
) )
inner class SourcesToCompile( inner class SourcesToCompile(
sources: Collection<KotlinModuleBuildTarget.Source>, sources: Collection<Source>,
val removedFiles: Collection<File> val removedFiles: Collection<File>
) { ) {
val allFiles = sources.map { it.file } val allFiles = sources.map { it.file }
@@ -330,45 +328,36 @@ abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo> intern
} }
} }
abstract val buildMetaInfoFactory: BuildMetaInfoFactory<BuildMetaInfoType> abstract val compilerArgumentsFileName: String
abstract val buildMetaInfoFileName: String abstract val buildMetaInfo: BuildMetaInfoType
fun isVersionChanged(chunk: KotlinChunk, buildMetaInfo: BuildMetaInfo): Boolean { fun isVersionChanged(chunk: KotlinChunk, compilerArguments: CommonCompilerArguments): Boolean {
val file = chunk.buildMetaInfoFile(jpsModuleBuildTarget) fun printReasonToRebuild(reasonToRebuild: String) {
KotlinBuilder.LOG.info("$reasonToRebuild. Performing non-incremental rebuild (kotlin only)")
}
val currentCompilerArgumentsMap = buildMetaInfo.createPropertiesMapFromCompilerArguments(compilerArguments)
val file = chunk.compilerArgumentsFile(jpsModuleBuildTarget)
if (Files.notExists(file)) return false if (Files.notExists(file)) return false
val prevBuildMetaInfo = val previousCompilerArgsMap =
try { try {
buildMetaInfoFactory.deserializeFromString(Files.newInputStream(file).bufferedReader().use { it.readText() }) buildMetaInfo.deserializeMapFromString(Files.newInputStream(file).bufferedReader().use { it.readText() })
?: return false
} catch (e: Exception) { } catch (e: Exception) {
KotlinBuilder.LOG.error("Could not deserialize build meta info", e) KotlinBuilder.LOG.error("Could not deserialize previous compiler arguments info", e)
return false return false
} }
val prevLangVersion = LanguageVersion.fromVersionString(prevBuildMetaInfo.languageVersionString) val rebuildReason = buildMetaInfo.obtainReasonForRebuild(currentCompilerArgumentsMap, previousCompilerArgsMap)
val prevApiVersion = ApiVersion.parse(prevBuildMetaInfo.apiVersionString)
val reasonToRebuild = when { return if (rebuildReason != null) {
chunk.langVersion != prevLangVersion -> "Language version was changed ($prevLangVersion -> ${chunk.langVersion})" printReasonToRebuild(rebuildReason)
chunk.apiVersion != prevApiVersion -> "Api version was changed ($prevApiVersion -> ${chunk.apiVersion})" true
prevLangVersion != LanguageVersion.KOTLIN_1_0 && prevBuildMetaInfo.isEAP && !buildMetaInfo.isEAP -> { } else {
// If EAP->Non-EAP build with IC, then rebuild all kotlin false
"Last build was compiled with EAP-plugin"
}
else -> PluginClasspathsComparator(
prevBuildMetaInfo.pluginClasspaths,
buildMetaInfo.pluginClasspaths
).describeDifferencesOrNull()
} }
if (reasonToRebuild != null) {
KotlinBuilder.LOG.info("$reasonToRebuild. Performing non-incremental rebuild (kotlin only)")
return true
}
return false
} }
private fun checkRepresentativeTarget(chunk: KotlinChunk) { private fun checkRepresentativeTarget(chunk: KotlinChunk) {
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.jps.targets
import org.jetbrains.jps.builders.storage.BuildDataPaths import org.jetbrains.jps.builders.storage.BuildDataPaths
import org.jetbrains.jps.incremental.ModuleBuildTarget import org.jetbrains.jps.incremental.ModuleBuildTarget
import org.jetbrains.kotlin.build.BuildMetaInfo import org.jetbrains.kotlin.build.BuildMetaInfo
import org.jetbrains.kotlin.build.BuildMetaInfoFactory
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment
import org.jetbrains.kotlin.jps.build.KotlinCompileContext import org.jetbrains.kotlin.jps.build.KotlinCompileContext
@@ -50,9 +49,9 @@ class KotlinUnsupportedModuleBuildTarget(
shouldNotBeCalled() shouldNotBeCalled()
} }
override val buildMetaInfoFactory: BuildMetaInfoFactory<BuildMetaInfo> override val compilerArgumentsFileName: String
get() = shouldNotBeCalled() get() = shouldNotBeCalled()
override val buildMetaInfoFileName: String override val buildMetaInfo: BuildMetaInfo
get() = shouldNotBeCalled() get() = shouldNotBeCalled()
} }