[IR] Enhance error reporting for IR linking issues

^KT-44626

Support correct error reporting both with native static caches and without native static caches.
This commit is contained in:
Dmitriy Dolovov
2021-08-12 11:09:32 +03:00
committed by Space
parent 3cd43dced4
commit af12d61388
9 changed files with 381 additions and 149 deletions
@@ -67,6 +67,7 @@ class SignatureIdNotFoundInModuleWithDependencies(
allModules = allModules,
problemModuleIds = setOf(problemModuleId),
problemCause = "This module requires symbol ${idSignature.render()}",
sourceCodeModuleId = userVisibleIrModulesSupport.sourceCodeModuleId,
moduleIdComparator = userVisibleIrModulesSupport.moduleIdComparator
)
}
@@ -113,6 +114,7 @@ class SymbolTypeMismatch(
potentiallyConflictingDependencies = findPotentiallyConflictingIncomingDependencies(
problemModuleId = declaringModuleId,
allModules = allModules,
sourceCodeModuleId = userVisibleIrModulesSupport.sourceCodeModuleId,
),
moduleIdComparator = userVisibleIrModulesSupport.moduleIdComparator
)
@@ -125,6 +127,7 @@ class SymbolTypeMismatch(
problemCause = "This module contains ${
idSignature?.render()?.let { "symbol $it" } ?: "a symbol"
} that is the cause of the conflict",
sourceCodeModuleId = userVisibleIrModulesSupport.sourceCodeModuleId,
moduleIdComparator = userVisibleIrModulesSupport.moduleIdComparator
)
}
@@ -159,6 +162,7 @@ private fun StringBuilder.appendProjectDependencies(
allModules: Map<ResolvedDependencyId, ResolvedDependency>,
problemModuleIds: Set<ResolvedDependencyId>,
problemCause: String,
sourceCodeModuleId: ResolvedDependencyId,
moduleIdComparator: Comparator<ResolvedDependencyId>
) {
append("\n\nProject dependencies:")
@@ -195,8 +199,7 @@ private fun StringBuilder.appendProjectDependencies(
append('\n').append(data.regularLinePrefix)
append(module.id)
val incomingDependencyId: ResolvedDependencyId = parentData?.incomingDependencyId
?: ResolvedDependencyId.SOURCE_CODE_MODULE_ID
val incomingDependencyId: ResolvedDependencyId = parentData?.incomingDependencyId ?: sourceCodeModuleId
val requestedVersion: ResolvedDependencyVersion = module.requestedVersionsByIncomingDependencies.getValue(incomingDependencyId)
if (!requestedVersion.isEmpty() || !module.selectedVersion.isEmpty()) {
append(": ")
@@ -240,8 +243,7 @@ private fun StringBuilder.appendProjectDependencies(
}
// Find first-level dependencies. I.e. the modules that the source code module directly depends on.
val firstLevelDependencies: Collection<ResolvedDependency> =
incomingDependencyIdToDependencies.getValue(ResolvedDependencyId.SOURCE_CODE_MODULE_ID)
val firstLevelDependencies: Collection<ResolvedDependency> = incomingDependencyIdToDependencies.getValue(sourceCodeModuleId)
renderModules(firstLevelDependencies, parentData = null)
@@ -375,7 +377,8 @@ private fun findPotentiallyConflictingOutgoingDependencies(
*/
private fun findPotentiallyConflictingIncomingDependencies(
problemModuleId: ResolvedDependencyId,
allModules: Map<ResolvedDependencyId, ResolvedDependency>
allModules: Map<ResolvedDependencyId, ResolvedDependency>,
sourceCodeModuleId: ResolvedDependencyId
): Map<ResolvedDependencyId, PotentialConflictDescription> {
val dependencyStatesMap: MutableMap<ResolvedDependencyId, MutableSet<DependencyState>> = mutableMapOf()
@@ -384,7 +387,7 @@ private fun findPotentiallyConflictingIncomingDependencies(
val module = allModules.findMatchingModule(moduleId)
module.requestedVersionsByIncomingDependencies.forEach { (incomingDependencyId, requestedVersion) ->
if (incomingDependencyId == ResolvedDependencyId.SOURCE_CODE_MODULE_ID) return@forEach
if (incomingDependencyId == sourceCodeModuleId) return@forEach
val dependencyState: DependencyState = when {
aboveConflictingDependency -> {
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.uniqueName
import org.jetbrains.kotlin.utils.*
open class UserVisibleIrModulesSupport(protected val externalDependenciesLoader: ExternalDependenciesLoader) {
open class UserVisibleIrModulesSupport(externalDependenciesLoader: ExternalDependenciesLoader) {
/**
* Load external [ResolvedDependency]s provided by the build system. These dependencies:
* - all have [ResolvedDependency.selectedVersion] specified
@@ -23,24 +23,24 @@ open class UserVisibleIrModulesSupport(protected val externalDependenciesLoader:
* not visible to the build system
*/
interface ExternalDependenciesLoader {
fun load(): Collection<ResolvedDependency>
fun load(): ResolvedDependencies
companion object {
val EMPTY = object : ExternalDependenciesLoader {
override fun load(): Collection<ResolvedDependency> = emptyList()
override fun load(): ResolvedDependencies = ResolvedDependencies.EMPTY
}
fun from(externalDependenciesFile: File?, onMalformedExternalDependencies: (String) -> Unit): ExternalDependenciesLoader =
if (externalDependenciesFile != null)
object : ExternalDependenciesLoader {
override fun load(): Collection<ResolvedDependency> {
override fun load(): ResolvedDependencies {
return if (externalDependenciesFile.exists) {
// Deserialize external dependencies from the [externalDependenciesFile].
val externalDependenciesText = String(externalDependenciesFile.readBytes())
ResolvedDependenciesSupport.deserialize(externalDependenciesText) { lineNo, line ->
onMalformedExternalDependencies("Malformed external dependencies at $externalDependenciesFile:$lineNo: $line")
}
} else emptyList()
} else ResolvedDependencies.EMPTY
}
}
else
@@ -48,6 +48,16 @@ open class UserVisibleIrModulesSupport(protected val externalDependenciesLoader:
}
}
private val externalDependencies: ResolvedDependencies by lazy {
externalDependenciesLoader.load()
}
private val externalDependencyModules: Collection<ResolvedDependency>
get() = externalDependencies.modules
val sourceCodeModuleId: ResolvedDependencyId
get() = externalDependencies.sourceCodeModuleId
fun getUserVisibleModuleId(deserializer: IrModuleDeserializer): ResolvedDependencyId {
val nameFromMetadataModuleHeader: String = deserializer.moduleFragment.name.asStringStripSpecialMarkers()
val nameFromKlibManifest: String? = deserializer.kotlinLibrary?.uniqueName
@@ -65,23 +75,30 @@ open class UserVisibleIrModulesSupport(protected val externalDependenciesLoader:
* (and therefore are missing in [ExternalDependenciesLoader.load]) but are provided by the compiler. For Kotlin/Native such
* libraries are stdlib, endorsed and platform libraries.
*/
protected open fun modulesFromDeserializers(deserializers: Collection<IrModuleDeserializer>): Map<ResolvedDependencyId, ResolvedDependency> {
val modules: Map<ResolvedDependencyId, ModuleWithUninitializedDependencies> = deserializers.associate { deserializer ->
protected open fun modulesFromDeserializers(
deserializers: Collection<IrModuleDeserializer>,
excludedModuleIds: Set<ResolvedDependencyId>
): Map<ResolvedDependencyId, ResolvedDependency> {
val modules: Map<ResolvedDependencyId, ModuleWithUninitializedDependencies> = deserializers.mapNotNull { deserializer ->
val moduleId = getUserVisibleModuleId(deserializer)
if (moduleId in excludedModuleIds) return@mapNotNull null
val module = ResolvedDependency(
id = moduleId,
// TODO: support extracting all the necessary details for non-Native libs: selectedVersion, requestedVersions, artifacts
selectedVersion = ResolvedDependencyVersion.EMPTY,
// Assumption: As we don't know for sure which modules the source code module depends on directly and which modules
// it depends on transitively, so let's assume it depends on all modules directly.
requestedVersionsByIncomingDependencies = mutableMapOf(ResolvedDependencyId.SOURCE_CODE_MODULE_ID to ResolvedDependencyVersion.EMPTY),
requestedVersionsByIncomingDependencies = mutableMapOf(
ResolvedDependencyId.DEFAULT_SOURCE_CODE_MODULE_ID to ResolvedDependencyVersion.EMPTY
),
artifactPaths = mutableSetOf()
)
val outgoingDependencyIds = deserializer.moduleDependencies.map { getUserVisibleModuleId(it) }
moduleId to ModuleWithUninitializedDependencies(module, outgoingDependencyIds)
}
}.toMap()
// Stamp dependencies.
return modules.stampDependenciesWithRequestedVersionEqualToSelectedVersion()
@@ -91,9 +108,6 @@ open class UserVisibleIrModulesSupport(protected val externalDependenciesLoader:
* The result of the merge of [ExternalDependenciesLoader.load] and [modulesFromDeserializers].
*/
protected fun mergedModules(deserializers: Collection<IrModuleDeserializer>): MutableMap<ResolvedDependencyId, ResolvedDependency> {
// First, load external dependencies.
val externalDependencyModules: Collection<ResolvedDependency> = externalDependenciesLoader.load()
val externalDependencyModulesByNames: Map</* unique name */ String, ResolvedDependency> =
mutableMapOf<String, ResolvedDependency>().apply {
externalDependencyModules.forEach { externalDependency ->
@@ -118,7 +132,10 @@ open class UserVisibleIrModulesSupport(protected val externalDependenciesLoader:
val providedModules = mutableListOf<ResolvedDependency>()
// Next, merge external dependencies with dependencies from deserializers.
modulesFromDeserializers(deserializers).forEach { (moduleId, module) ->
modulesFromDeserializers(
deserializers = deserializers,
excludedModuleIds = setOf(sourceCodeModuleId)
).forEach { (moduleId, module) ->
val externalDependencyModule = findMatchingExternalDependencyModule(moduleId)
if (externalDependencyModule != null) {
// Just add missing dependencies to the same module in [mergedModules].
@@ -146,7 +163,7 @@ open class UserVisibleIrModulesSupport(protected val externalDependenciesLoader:
// Just keep the module as is. If it has no incoming dependencies, then treat it as
// the first-level dependency module (i.e. the module that only the source code module depends on).
if (module.requestedVersionsByIncomingDependencies.isEmpty()) {
module.requestedVersionsByIncomingDependencies[ResolvedDependencyId.SOURCE_CODE_MODULE_ID] = module.selectedVersion
module.requestedVersionsByIncomingDependencies[sourceCodeModuleId] = module.selectedVersion
}
}