[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:
+9
-6
@@ -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 -> {
|
||||
|
||||
+31
-14
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user