mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-10 08:31:29 +00:00
[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:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.util
|
||||
|
||||
import org.jetbrains.kotlin.utils.ResolvedDependencies
|
||||
import org.jetbrains.kotlin.utils.ResolvedDependenciesSupport
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.fail
|
||||
@@ -14,6 +15,7 @@ class ResolvedDependenciesSupportTest {
|
||||
@Test
|
||||
fun success1() {
|
||||
val originalText = """
|
||||
|0 \
|
||||
|1 io.ktor:ktor-io,io.ktor:ktor-io-macosx64[1.5.4] #0[1.5.4]
|
||||
|${'\t'}/some/path/ktor-io.klib
|
||||
|${'\t'}/some/path/ktor-io-cinterop-bits.klib
|
||||
@@ -28,10 +30,10 @@ class ResolvedDependenciesSupportTest {
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
val deserializedModules = ResolvedDependenciesSupport.deserialize(originalText) { lineNo, line ->
|
||||
val (deserializedModules, sourceCodeModuleId) = ResolvedDependenciesSupport.deserialize(originalText) { lineNo, line ->
|
||||
fail("Unexpected failure at line $lineNo: $line")
|
||||
}
|
||||
val restoredText = ResolvedDependenciesSupport.serialize(deserializedModules)
|
||||
val restoredText = ResolvedDependenciesSupport.serialize(ResolvedDependencies(deserializedModules, sourceCodeModuleId))
|
||||
|
||||
assertEquals(originalText, restoredText)
|
||||
}
|
||||
@@ -39,6 +41,7 @@ class ResolvedDependenciesSupportTest {
|
||||
@Test
|
||||
fun success2() {
|
||||
val originalText = """
|
||||
|0 \
|
||||
|1 org.sample:liba,org.sample:liba-native[2.0] #0[2.0] #2[1.0]
|
||||
|${'\t'}/some/path/liba.klib
|
||||
|2 org.sample:libb,org.sample:libb-native[1.0] #0[1.0]
|
||||
@@ -46,10 +49,10 @@ class ResolvedDependenciesSupportTest {
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
val deserializedModules = ResolvedDependenciesSupport.deserialize(originalText) { lineNo, line ->
|
||||
val (deserializedModules, sourceCodeModuleId) = ResolvedDependenciesSupport.deserialize(originalText) { lineNo, line ->
|
||||
fail("Unexpected failure at line $lineNo: $line")
|
||||
}
|
||||
val restoredText = ResolvedDependenciesSupport.serialize(deserializedModules)
|
||||
val restoredText = ResolvedDependenciesSupport.serialize(ResolvedDependencies(deserializedModules, sourceCodeModuleId))
|
||||
|
||||
assertEquals(originalText, restoredText)
|
||||
}
|
||||
@@ -57,6 +60,7 @@ class ResolvedDependenciesSupportTest {
|
||||
@Test
|
||||
fun success3() {
|
||||
val originalText = """
|
||||
|0 \
|
||||
|1 org.jetbrains.kotlin:kotlin-stdlib-common[1.5.0] #2[1.4.32] #3[1.5.0] #4[1.5.0] #5[1.4.32] #6[1.4.32] #7[1.4.32] #8[1.4.32]
|
||||
|${'\t'}/some/path/kotlin-stdlib-common-1.5.0.jar
|
||||
|2 io.ktor:ktor-client-core,io.ktor:ktor-client-core-macosx64[1.5.4] #0[1.5.4]
|
||||
@@ -80,10 +84,29 @@ class ResolvedDependenciesSupportTest {
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
val deserializedModules = ResolvedDependenciesSupport.deserialize(originalText) { lineNo, line ->
|
||||
val (deserializedModules, sourceCodeModuleId) = ResolvedDependenciesSupport.deserialize(originalText) { lineNo, line ->
|
||||
fail("Unexpected failure at line $lineNo: $line")
|
||||
}
|
||||
val restoredText = ResolvedDependenciesSupport.serialize(deserializedModules)
|
||||
val restoredText = ResolvedDependenciesSupport.serialize(ResolvedDependencies(deserializedModules, sourceCodeModuleId))
|
||||
|
||||
assertEquals(originalText, restoredText)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun success4() {
|
||||
val originalText = """
|
||||
|0 baz-native,foo,org.sample.bar
|
||||
|1 org.sample:liba,org.sample:liba-native[2.0] #0[2.0] #2[1.0]
|
||||
|${'\t'}/some/path/liba.klib
|
||||
|2 org.sample:libb,org.sample:libb-native[1.0] #0[1.0]
|
||||
|${'\t'}/some/path/libb.klib
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
val (deserializedModules, sourceCodeModuleId) = ResolvedDependenciesSupport.deserialize(originalText) { lineNo, line ->
|
||||
fail("Unexpected failure at line $lineNo: $line")
|
||||
}
|
||||
val restoredText = ResolvedDependenciesSupport.serialize(ResolvedDependencies(deserializedModules, sourceCodeModuleId))
|
||||
|
||||
assertEquals(originalText, restoredText)
|
||||
}
|
||||
@@ -92,6 +115,7 @@ class ResolvedDependenciesSupportTest {
|
||||
fun failure1() {
|
||||
// There is no record with number 42!
|
||||
val originalText = """
|
||||
|0 \
|
||||
|1 org.sample:liba,org.sample:liba-native[2.0] #0[2.0] #2[1.0] #42[42.42]
|
||||
|${'\t'}/some/path/liba.klib
|
||||
|2 org.sample:libb,org.sample:libb-native[1.0] #0[1.0]
|
||||
@@ -99,13 +123,19 @@ class ResolvedDependenciesSupportTest {
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
ResolvedDependenciesSupport.deserialize(originalText) { _, _ -> throw MyException() }
|
||||
ResolvedDependenciesSupport.deserialize(originalText) { lineNo, _ ->
|
||||
assertEquals(1, lineNo)
|
||||
throw MyException()
|
||||
}
|
||||
|
||||
fail()
|
||||
}
|
||||
|
||||
@Test(expected = MyException::class)
|
||||
fun failure2() {
|
||||
// Name not specified.
|
||||
val originalText = """
|
||||
|0 \
|
||||
|1 org.sample:liba,org.sample:liba-native[2.0] #0[2.0] #2[1.0]
|
||||
|${'\t'}/some/path/liba.klib
|
||||
|2 [1.0] #0[1.0]
|
||||
@@ -113,13 +143,58 @@ class ResolvedDependenciesSupportTest {
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
ResolvedDependenciesSupport.deserialize(originalText) { _, _ -> throw MyException() }
|
||||
ResolvedDependenciesSupport.deserialize(originalText) { lineNo, _ ->
|
||||
assertEquals(3, lineNo)
|
||||
throw MyException()
|
||||
}
|
||||
|
||||
fail()
|
||||
}
|
||||
|
||||
@Test(expected = MyException::class)
|
||||
fun failure3() {
|
||||
// Version not specified.
|
||||
val originalText = """
|
||||
|0 \
|
||||
|1 org.sample:liba,org.sample:liba-native[2.0] #0[2.0] #2[1.0]
|
||||
|${'\t'}/some/path/liba.klib
|
||||
|2 org.sample:libb,org.sample:libb-native #0[1.0]
|
||||
|${'\t'}/some/path/libb.klib
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
ResolvedDependenciesSupport.deserialize(originalText) { lineNo, _ ->
|
||||
assertEquals(3, lineNo)
|
||||
throw MyException()
|
||||
}
|
||||
|
||||
fail()
|
||||
}
|
||||
|
||||
@Test(expected = MyException::class)
|
||||
fun failure4() {
|
||||
// Source code module ID not specified.
|
||||
val originalText = """
|
||||
|0
|
||||
|1 org.sample:liba,org.sample:liba-native[2.0] #0[2.0] #2[1.0]
|
||||
|${'\t'}/some/path/liba.klib
|
||||
|2 org.sample:libb,org.sample:libb-native #0[1.0]
|
||||
|${'\t'}/some/path/libb.klib
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
ResolvedDependenciesSupport.deserialize(originalText) { lineNo, _ ->
|
||||
assertEquals(0, lineNo)
|
||||
throw MyException()
|
||||
}
|
||||
|
||||
fail()
|
||||
}
|
||||
|
||||
@Test(expected = MyException::class)
|
||||
fun failure5() {
|
||||
// Source code module ID not specified.
|
||||
val originalText = """
|
||||
|1 org.sample:liba,org.sample:liba-native[2.0] #0[2.0] #2[1.0]
|
||||
|${'\t'}/some/path/liba.klib
|
||||
|2 org.sample:libb,org.sample:libb-native #0[1.0]
|
||||
@@ -127,7 +202,12 @@ class ResolvedDependenciesSupportTest {
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
ResolvedDependenciesSupport.deserialize(originalText) { _, _ -> throw MyException() }
|
||||
ResolvedDependenciesSupport.deserialize(originalText) { lineNo, _ ->
|
||||
assertEquals(0, lineNo)
|
||||
throw MyException()
|
||||
}
|
||||
|
||||
fail()
|
||||
}
|
||||
|
||||
private class MyException : Exception()
|
||||
|
||||
@@ -47,7 +47,7 @@ class ResolvedDependencyIdTest {
|
||||
|
||||
@Test
|
||||
fun toStringImplementation() {
|
||||
assertEquals("/", ResolvedDependencyId.SOURCE_CODE_MODULE_ID.toString())
|
||||
assertEquals("/", ResolvedDependencyId.DEFAULT_SOURCE_CODE_MODULE_ID.toString())
|
||||
assertEquals("foo", ResolvedDependencyId("foo").toString())
|
||||
assertEquals("bar (foo)", ResolvedDependencyId("foo", "bar").toString())
|
||||
assertEquals("bar (baz, foo)", ResolvedDependencyId("foo", "bar", "baz").toString())
|
||||
|
||||
@@ -45,7 +45,7 @@ class ResolvedDependencyId private constructor(val uniqueNames: Set<String>) {
|
||||
}
|
||||
|
||||
companion object {
|
||||
val SOURCE_CODE_MODULE_ID = ResolvedDependencyId("/")
|
||||
val DEFAULT_SOURCE_CODE_MODULE_ID = ResolvedDependencyId("/")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +58,8 @@ class ResolvedDependencyId private constructor(val uniqueNames: Set<String>) {
|
||||
* - [requestedVersionsByIncomingDependencies] the mapping between ID of the dependee module (i.e. the module that depends on this module)
|
||||
* and the version (requested version) that the dependee module requires from this module. The requested version can be different
|
||||
* for different dependee modules, which is absolutely legal. The [selectedVersion] is always equal to one of the requested versions
|
||||
* (the one that wins among others, which is typically handled inside of the build system). If the module is the top-level module,
|
||||
* then it has [ResolvedDependencyId.SOURCE_CODE_MODULE_ID] in the mapping.
|
||||
* (the one that wins among others, which is typically handled inside the build system). If the module is the top-level module,
|
||||
* then it has [ResolvedDependencies.sourceCodeModuleId] in the mapping.
|
||||
* - [artifactPaths] absolute paths to every artifact represented by this module
|
||||
*/
|
||||
class ResolvedDependency(
|
||||
@@ -75,14 +75,27 @@ class ResolvedDependency(
|
||||
override fun toString() = moduleIdWithVersion
|
||||
}
|
||||
|
||||
data class ResolvedDependencies(
|
||||
val modules: Collection<ResolvedDependency>,
|
||||
val sourceCodeModuleId: ResolvedDependencyId
|
||||
) {
|
||||
companion object {
|
||||
val EMPTY = ResolvedDependencies(emptyList(), ResolvedDependencyId.DEFAULT_SOURCE_CODE_MODULE_ID)
|
||||
}
|
||||
}
|
||||
|
||||
object ResolvedDependenciesSupport {
|
||||
fun serialize(modules: Collection<ResolvedDependency>): String {
|
||||
val moduleIdToIndex = mutableMapOf(ResolvedDependencyId.SOURCE_CODE_MODULE_ID to 0).apply {
|
||||
modules.forEachIndexed { index, module -> this[module.id] = index + 1 }
|
||||
fun serialize(dependencies: ResolvedDependencies): String {
|
||||
val moduleIdToIndex = mutableMapOf(dependencies.sourceCodeModuleId to 0).apply {
|
||||
dependencies.modules.forEachIndexed { index, module -> this[module.id] = index + 1 }
|
||||
}
|
||||
|
||||
return buildString {
|
||||
modules.forEach { module ->
|
||||
append("0 ")
|
||||
dependencies.sourceCodeModuleId.uniqueNames.joinTo(this, separator = ",")
|
||||
append('\n')
|
||||
|
||||
dependencies.modules.forEach { module ->
|
||||
val moduleIndex = moduleIdToIndex.getValue(module.id)
|
||||
append(moduleIndex.toString()).append(' ')
|
||||
module.id.uniqueNames.joinTo(this, separator = ",")
|
||||
@@ -100,17 +113,18 @@ object ResolvedDependenciesSupport {
|
||||
}
|
||||
}
|
||||
|
||||
fun deserialize(source: String, onMalformedLine: (lineNo: Int, line: String) -> Unit): Collection<ResolvedDependency> {
|
||||
val moduleIndexToId: MutableMap<Int, ResolvedDependencyId> = mutableMapOf(0 to ResolvedDependencyId.SOURCE_CODE_MODULE_ID)
|
||||
fun deserialize(source: String, onMalformedLine: (lineNo: Int, line: String) -> Unit): ResolvedDependencies {
|
||||
val moduleIndexToId: MutableMap<Int, ResolvedDependencyId> = mutableMapOf()
|
||||
val requestedVersionsByIncomingDependenciesIndices: MutableMap<ResolvedDependencyId, Map<Int, ResolvedDependencyVersion>> =
|
||||
mutableMapOf()
|
||||
|
||||
var sourceCodeModuleId: ResolvedDependencyId? = null
|
||||
val modules = mutableListOf<ResolvedDependency>()
|
||||
|
||||
source.lines().forEachIndexed { lineNo, line ->
|
||||
fun malformedLine(): Collection<ResolvedDependency> {
|
||||
fun malformedLine(): ResolvedDependencies {
|
||||
onMalformedLine(lineNo, line)
|
||||
return emptyList()
|
||||
return ResolvedDependencies.EMPTY
|
||||
}
|
||||
|
||||
when {
|
||||
@@ -123,7 +137,13 @@ object ResolvedDependenciesSupport {
|
||||
else
|
||||
return malformedLine()
|
||||
}
|
||||
line[0] == '0' -> {
|
||||
val result = SOURCE_CODE_MODULE_REGEX.matchEntire(line) ?: return malformedLine()
|
||||
sourceCodeModuleId = ResolvedDependencyId(result.groupValues[1].split(','))
|
||||
}
|
||||
else -> {
|
||||
if (sourceCodeModuleId == null) return malformedLine()
|
||||
|
||||
val result = DEPENDENCY_MODULE_REGEX.matchEntire(line) ?: return malformedLine()
|
||||
val moduleIndex = result.groupValues[1].toInt()
|
||||
val moduleId = ResolvedDependencyId(result.groupValues[2].split(','))
|
||||
@@ -156,14 +176,18 @@ object ResolvedDependenciesSupport {
|
||||
// Stamp incoming dependencies & requested versions.
|
||||
modules.forEach { module ->
|
||||
requestedVersionsByIncomingDependenciesIndices[module.id]?.forEach { (incomingDependencyIndex, requestedVersion) ->
|
||||
val incomingDependencyId = moduleIndexToId.getValue(incomingDependencyIndex)
|
||||
val incomingDependencyId = if (incomingDependencyIndex == 0)
|
||||
sourceCodeModuleId!!
|
||||
else
|
||||
moduleIndexToId.getValue(incomingDependencyIndex)
|
||||
module.requestedVersionsByIncomingDependencies[incomingDependencyId] = requestedVersion
|
||||
}
|
||||
}
|
||||
|
||||
return modules
|
||||
return ResolvedDependencies(modules, sourceCodeModuleId!!)
|
||||
}
|
||||
|
||||
private val SOURCE_CODE_MODULE_REGEX = Regex("^0 ([^\\[]+)$")
|
||||
private val DEPENDENCY_MODULE_REGEX = Regex("^(\\d+) ([^\\[]+)\\[([^]]+)](.*)?$")
|
||||
private val REQUESTED_VERSION_BY_INCOMING_DEPENDENCY_REGEX = Regex("^#(\\d+)\\[(.*)]$")
|
||||
}
|
||||
|
||||
@@ -26,7 +26,10 @@ class KonanUserVisibleIrModulesSupport(
|
||||
return compressedModules(deserializers)
|
||||
}
|
||||
|
||||
override fun modulesFromDeserializers(deserializers: Collection<IrModuleDeserializer>): Map<ResolvedDependencyId, ResolvedDependency> {
|
||||
override fun modulesFromDeserializers(
|
||||
deserializers: Collection<IrModuleDeserializer>,
|
||||
excludedModuleIds: Set<ResolvedDependencyId>
|
||||
): Map<ResolvedDependencyId, ResolvedDependency> {
|
||||
val moduleToCompilerVersion: MutableMap<ResolvedDependencyId, ResolvedDependencyVersion> = mutableMapOf()
|
||||
val kotlinNativeBundledLibraries: MutableSet<ResolvedDependencyId> = mutableSetOf()
|
||||
|
||||
@@ -34,6 +37,9 @@ class KonanUserVisibleIrModulesSupport(
|
||||
val modules: Map<ResolvedDependencyId, ModuleWithUninitializedDependencies> = deserializers.mapNotNull { deserializer ->
|
||||
val library: KotlinLibrary = deserializer.kotlinLibrary ?: return@mapNotNull null
|
||||
|
||||
val moduleId = getUserVisibleModuleId(deserializer)
|
||||
if (moduleId in excludedModuleIds) return@mapNotNull null
|
||||
|
||||
val compilerVersion: ResolvedDependencyVersion = library.compilerVersion
|
||||
val isDefaultLibrary = library.isDefaultLibrary
|
||||
|
||||
@@ -41,7 +47,6 @@ class KonanUserVisibleIrModulesSupport(
|
||||
// Note: Empty string means missing (unknown) version.
|
||||
val libraryVersion: ResolvedDependencyVersion = if (isDefaultLibrary) compilerVersion else ResolvedDependencyVersion.EMPTY
|
||||
|
||||
val moduleId = getUserVisibleModuleId(deserializer)
|
||||
val module = ResolvedDependency(
|
||||
id = moduleId,
|
||||
selectedVersion = libraryVersion,
|
||||
@@ -54,7 +59,9 @@ class KonanUserVisibleIrModulesSupport(
|
||||
|
||||
// Don't rely on dependencies in IrModuleDeserializer. In Kotlin/Native each module depends on all other modules,
|
||||
// and this contradicts with the real module dependencies as written in KLIB manifest files.
|
||||
val outgoingDependencyIds = library.unresolvedDependencies.map { it.moduleId }
|
||||
val outgoingDependencyIds = library.unresolvedDependencies.mapNotNull { unresolvedDependency ->
|
||||
unresolvedDependency.moduleId.takeIf { it !in excludedModuleIds }
|
||||
}
|
||||
|
||||
moduleId to ModuleWithUninitializedDependencies(module, outgoingDependencyIds)
|
||||
}.toMap()
|
||||
@@ -87,7 +94,7 @@ class KonanUserVisibleIrModulesSupport(
|
||||
|
||||
for ((moduleId, module) in compressedModules) {
|
||||
if (moduleId.isKonanPlatformLibrary) {
|
||||
if (ResolvedDependencyId.SOURCE_CODE_MODULE_ID !in module.requestedVersionsByIncomingDependencies) {
|
||||
if (sourceCodeModuleId !in module.requestedVersionsByIncomingDependencies) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -116,7 +123,7 @@ class KonanUserVisibleIrModulesSupport(
|
||||
val compressedModule = ResolvedDependency(
|
||||
id = compressedModuleId,
|
||||
selectedVersion = platformLibrariesVersion!!,
|
||||
requestedVersionsByIncomingDependencies = mutableMapOf(ResolvedDependencyId.SOURCE_CODE_MODULE_ID to platformLibrariesVersion),
|
||||
requestedVersionsByIncomingDependencies = mutableMapOf(sourceCodeModuleId to platformLibrariesVersion),
|
||||
artifactPaths = mutableSetOf()
|
||||
)
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ class NativeExternalDependenciesIT : BaseGradleIT() {
|
||||
|
||||
assertEquals(
|
||||
"""
|
||||
|0 native-external-dependencies
|
||||
|1 io.ktor:ktor-io,io.ktor:ktor-io-$MASKED_TARGET_NAME[1.5.4] #0[1.5.4]
|
||||
|${'\t'}/some/path/ktor-io.klib
|
||||
|${'\t'}/some/path/ktor-io-cinterop-bits.klib
|
||||
@@ -55,6 +56,7 @@ class NativeExternalDependenciesIT : BaseGradleIT() {
|
||||
|
||||
assertEquals(
|
||||
"""
|
||||
|0 native-external-dependencies
|
||||
|1 io.ktor:ktor-io,io.ktor:ktor-io-$MASKED_TARGET_NAME[1.5.4] #0[1.5.4]
|
||||
|${'\t'}/some/path/ktor-io.klib
|
||||
|${'\t'}/some/path/ktor-io-cinterop-bits.klib
|
||||
|
||||
@@ -10,8 +10,9 @@ import org.jetbrains.kotlin.gradle.GradleVersionRequired
|
||||
import org.jetbrains.kotlin.gradle.native.NativeExternalDependenciesIT.Companion.MASKED_TARGET_NAME
|
||||
import org.jetbrains.kotlin.gradle.native.NativeExternalDependenciesIT.Companion.findKotlinNativeTargetName
|
||||
import org.jetbrains.kotlin.gradle.native.NativeExternalDependenciesIT.Companion.findParameterInOutput
|
||||
import org.jetbrains.kotlin.konan.library.KONAN_PLATFORM_LIBS_NAME_PREFIX
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
import org.junit.Assume
|
||||
import org.junit.Assume.assumeFalse
|
||||
import org.junit.Assume.assumeTrue
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
@@ -23,84 +24,16 @@ class NativeIrLinkerIssuesIT : BaseGradleIT() {
|
||||
override val defaultGradleVersion: GradleVersionRequired
|
||||
get() = GradleVersionRequired.FOR_MPP_SUPPORT
|
||||
|
||||
@Test
|
||||
fun `declaration that is gone (KT-41378)`() {
|
||||
val repo = setupLocalRepo()
|
||||
|
||||
buildAndPublishLibrary("native-ir-linker-issues-gone-declaration", "liba-v1.0", repo)
|
||||
buildAndPublishLibrary("native-ir-linker-issues-gone-declaration", "liba-v2.0", repo)
|
||||
buildAndPublishLibrary("native-ir-linker-issues-gone-declaration", "libb", repo)
|
||||
|
||||
buildApplicationAndFail(
|
||||
directory = "native-ir-linker-issues-gone-declaration",
|
||||
projectName = "app",
|
||||
localRepo = repo
|
||||
) { kotlinNativeCompilerVersion ->
|
||||
"""
|
||||
|e: Module "org.sample:libb (org.sample:libb-native)" has a reference to symbol sample.liba/C|null[0]. Neither the module itself nor its dependencies contain such declaration.
|
||||
|
|
||||
|This could happen if the required dependency is missing in the project. Or if there is a dependency of "org.sample:libb (org.sample:libb-native)" that has a different version in the project than the version that "org.sample:libb (org.sample:libb-native): 1.0" was initially compiled with. Please check that the project configuration is correct and has consistent versions of all required dependencies.
|
||||
|
|
||||
|The list of "org.sample:libb (org.sample:libb-native): 1.0" dependencies that may lead to conflicts:
|
||||
|1. "org.sample:liba (org.sample:liba-native): 2.0" (was initially compiled with "org.sample:liba (org.sample:liba-native): 1.0")
|
||||
|
|
||||
|Project dependencies:
|
||||
|├─── org.sample:liba (org.sample:liba-native): 2.0
|
||||
|│ └─── stdlib: $kotlinNativeCompilerVersion
|
||||
|└─── org.sample:libb (org.sample:libb-native): 1.0
|
||||
| ^^^ This module requires symbol sample.liba/C|null[0].
|
||||
| ├─── org.sample:liba (org.sample:liba-native): 1.0 -> 2.0 (*)
|
||||
| └─── stdlib: $kotlinNativeCompilerVersion
|
||||
|
|
||||
|(*) - dependencies omitted (listed previously)
|
||||
""".trimMargin()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `symbol type mismatch (KT-47285)`() {
|
||||
val repo = setupLocalRepo()
|
||||
|
||||
buildAndPublishLibrary("native-ir-linker-issues-symbol-mismatch", "liba-v1.0", repo)
|
||||
buildAndPublishLibrary("native-ir-linker-issues-symbol-mismatch", "liba-v2.0", repo)
|
||||
buildAndPublishLibrary("native-ir-linker-issues-symbol-mismatch", "libb", repo)
|
||||
|
||||
buildApplicationAndFail(
|
||||
directory = "native-ir-linker-issues-symbol-mismatch",
|
||||
projectName = "app",
|
||||
localRepo = repo
|
||||
) { kotlinNativeCompilerVersion ->
|
||||
"""
|
||||
|e: The symbol of unexpected type encountered during IR deserialization: IrClassPublicSymbolImpl, sample.liba/B|null[0]. IrTypeAliasSymbol is expected.
|
||||
|
|
||||
|This could happen if there are two libraries, where one library was compiled against the different version of the other library than the one currently used in the project. Please check that the project configuration is correct and has consistent versions of dependencies.
|
||||
|
|
||||
|The list of libraries that depend on "org.sample:liba (org.sample:liba-native)" and may lead to conflicts:
|
||||
|1. "org.sample:libb (org.sample:libb-native): 1.0" (was compiled against "org.sample:liba (org.sample:liba-native): 1.0" but "org.sample:liba (org.sample:liba-native): 2.0" is used in the project)
|
||||
|
|
||||
|Project dependencies:
|
||||
|├─── org.sample:liba (org.sample:liba-native): 2.0
|
||||
|│ ^^^ This module contains symbol sample.liba/B|null[0] that is the cause of the conflict.
|
||||
|│ └─── stdlib: $kotlinNativeCompilerVersion
|
||||
|└─── org.sample:libb (org.sample:libb-native): 1.0
|
||||
| ├─── org.sample:liba (org.sample:liba-native): 1.0 -> 2.0 (*)
|
||||
| │ ^^^ This module contains symbol sample.liba/B|null[0] that is the cause of the conflict.
|
||||
| └─── stdlib: $kotlinNativeCompilerVersion
|
||||
|
|
||||
|(*) - dependencies omitted (listed previously)
|
||||
""".trimMargin()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ktor 1_5_4 and coroutines 1_5_0-RC-native-mt (KT-46697)`() {
|
||||
// Run this test only on macOS.
|
||||
assumeTrue(HostManager.hostIsMac)
|
||||
|
||||
buildApplicationAndFail(
|
||||
directory = null,
|
||||
directoryPrefix = null,
|
||||
projectName = "native-ir-linker-issues-ktor-and-coroutines",
|
||||
localRepo = null
|
||||
localRepo = null,
|
||||
useCache = true
|
||||
) { kotlinNativeCompilerVersion ->
|
||||
"""
|
||||
|e: The symbol of unexpected type encountered during IR deserialization: IrClassPublicSymbolImpl, kotlinx.coroutines/CancellationException|null[0]. IrTypeAliasSymbol is expected.
|
||||
@@ -173,13 +106,154 @@ class NativeIrLinkerIssuesIT : BaseGradleIT() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildApplicationAndFail(
|
||||
directory: String?,
|
||||
projectName: String,
|
||||
localRepo: File?,
|
||||
@Test
|
||||
fun `declaration that is gone (KT-41378) - with cache`() {
|
||||
// Don't run it on Windows. Caches are not supported there yet.
|
||||
assumeFalse(HostManager.hostIsMingw)
|
||||
|
||||
buildConflictingLibrariesAndApplication(
|
||||
directoryPrefix = "native-ir-linker-issues-gone-declaration",
|
||||
useCache = true
|
||||
) { kotlinNativeCompilerVersion ->
|
||||
"""
|
||||
|e: Module "org.sample:libb (org.sample:libb-native)" has a reference to symbol sample.liba/C|null[0]. Neither the module itself nor its dependencies contain such declaration.
|
||||
|
|
||||
|This could happen if the required dependency is missing in the project. Or if there is a dependency of "org.sample:libb (org.sample:libb-native)" that has a different version in the project than the version that "org.sample:libb (org.sample:libb-native): 1.0" was initially compiled with. Please check that the project configuration is correct and has consistent versions of all required dependencies.
|
||||
|
|
||||
|The list of "org.sample:libb (org.sample:libb-native): 1.0" dependencies that may lead to conflicts:
|
||||
|1. "org.sample:liba (org.sample:liba-native): 2.0" (was initially compiled with "org.sample:liba (org.sample:liba-native): 1.0")
|
||||
|
|
||||
|Project dependencies:
|
||||
|├─── org.sample:liba (org.sample:liba-native): 2.0
|
||||
|│ └─── stdlib: $kotlinNativeCompilerVersion
|
||||
|└─── org.sample:libb (org.sample:libb-native): 1.0
|
||||
| ^^^ This module requires symbol sample.liba/C|null[0].
|
||||
| ├─── org.sample:liba (org.sample:liba-native): 1.0 -> 2.0 (*)
|
||||
| └─── stdlib: $kotlinNativeCompilerVersion
|
||||
|
|
||||
|(*) - dependencies omitted (listed previously)
|
||||
""".trimMargin()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `declaration that is gone (KT-41378) - without cache`() {
|
||||
buildConflictingLibrariesAndApplication(
|
||||
directoryPrefix = "native-ir-linker-issues-gone-declaration",
|
||||
useCache = false
|
||||
) { kotlinNativeCompilerVersion ->
|
||||
"""
|
||||
|e: Module "org.sample:libb (org.sample:libb-native)" has a reference to symbol sample.liba/C|null[0]. Neither the module itself nor its dependencies contain such declaration.
|
||||
|
|
||||
|This could happen if the required dependency is missing in the project. Or if there is a dependency of "org.sample:libb (org.sample:libb-native)" that has a different version in the project than the version that "org.sample:libb (org.sample:libb-native): 1.0" was initially compiled with. Please check that the project configuration is correct and has consistent versions of all required dependencies.
|
||||
|
|
||||
|The list of "org.sample:libb (org.sample:libb-native): 1.0" dependencies that may lead to conflicts:
|
||||
|1. "org.sample:liba (org.sample:liba-native): 2.0" (was initially compiled with "org.sample:liba (org.sample:liba-native): 1.0")
|
||||
|
|
||||
|Project dependencies:
|
||||
|├─── org.sample:liba (org.sample:liba-native): 2.0
|
||||
|│ └─── stdlib: $kotlinNativeCompilerVersion
|
||||
|├─── org.sample:libb (org.sample:libb-native): 1.0
|
||||
|│ ^^^ This module requires symbol sample.liba/C|null[0].
|
||||
|│ ├─── org.sample:liba (org.sample:liba-native): 1.0 -> 2.0 (*)
|
||||
|│ └─── stdlib: $kotlinNativeCompilerVersion
|
||||
|└─── org.jetbrains.kotlin.native.platform.* (NNN libraries): $kotlinNativeCompilerVersion
|
||||
| └─── stdlib: $kotlinNativeCompilerVersion
|
||||
|
|
||||
|(*) - dependencies omitted (listed previously)
|
||||
""".trimMargin()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `symbol type mismatch (KT-47285) - with cache`() {
|
||||
// Don't run it on Windows. Caches are not supported there yet.
|
||||
assumeFalse(HostManager.hostIsMingw)
|
||||
|
||||
buildConflictingLibrariesAndApplication(
|
||||
directoryPrefix = "native-ir-linker-issues-symbol-mismatch",
|
||||
useCache = true
|
||||
) { kotlinNativeCompilerVersion ->
|
||||
"""
|
||||
|e: The symbol of unexpected type encountered during IR deserialization: IrClassPublicSymbolImpl, sample.liba/B|null[0]. IrTypeAliasSymbol is expected.
|
||||
|
|
||||
|This could happen if there are two libraries, where one library was compiled against the different version of the other library than the one currently used in the project. Please check that the project configuration is correct and has consistent versions of dependencies.
|
||||
|
|
||||
|The list of libraries that depend on "org.sample:liba (org.sample:liba-native)" and may lead to conflicts:
|
||||
|1. "org.sample:libb (org.sample:libb-native): 1.0" (was compiled against "org.sample:liba (org.sample:liba-native): 1.0" but "org.sample:liba (org.sample:liba-native): 2.0" is used in the project)
|
||||
|
|
||||
|Project dependencies:
|
||||
|├─── org.sample:liba (org.sample:liba-native): 2.0
|
||||
|│ ^^^ This module contains symbol sample.liba/B|null[0] that is the cause of the conflict.
|
||||
|│ └─── stdlib: $kotlinNativeCompilerVersion
|
||||
|└─── org.sample:libb (org.sample:libb-native): 1.0
|
||||
| ├─── org.sample:liba (org.sample:liba-native): 1.0 -> 2.0 (*)
|
||||
| │ ^^^ This module contains symbol sample.liba/B|null[0] that is the cause of the conflict.
|
||||
| └─── stdlib: $kotlinNativeCompilerVersion
|
||||
|
|
||||
|(*) - dependencies omitted (listed previously)
|
||||
""".trimMargin()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `symbol type mismatch (KT-47285) - without cache`() {
|
||||
buildConflictingLibrariesAndApplication(
|
||||
directoryPrefix = "native-ir-linker-issues-symbol-mismatch",
|
||||
useCache = false
|
||||
) { kotlinNativeCompilerVersion ->
|
||||
"""
|
||||
|e: The symbol of unexpected type encountered during IR deserialization: IrTypeAliasPublicSymbolImpl, sample.liba/B|null[0]. IrClassifierSymbol is expected.
|
||||
|
|
||||
|This could happen if there are two libraries, where one library was compiled against the different version of the other library than the one currently used in the project. Please check that the project configuration is correct and has consistent versions of dependencies.
|
||||
|
|
||||
|The list of libraries that depend on "org.sample:liba (org.sample:liba-native)" and may lead to conflicts:
|
||||
|1. "org.sample:libb (org.sample:libb-native): 1.0" (was compiled against "org.sample:liba (org.sample:liba-native): 1.0" but "org.sample:liba (org.sample:liba-native): 2.0" is used in the project)
|
||||
|
|
||||
|Project dependencies:
|
||||
|├─── org.sample:liba (org.sample:liba-native): 2.0
|
||||
|│ ^^^ This module contains symbol sample.liba/B|null[0] that is the cause of the conflict.
|
||||
|│ └─── stdlib: $kotlinNativeCompilerVersion
|
||||
|├─── org.sample:libb (org.sample:libb-native): 1.0
|
||||
|│ ├─── org.sample:liba (org.sample:liba-native): 1.0 -> 2.0 (*)
|
||||
|│ │ ^^^ This module contains symbol sample.liba/B|null[0] that is the cause of the conflict.
|
||||
|│ └─── stdlib: $kotlinNativeCompilerVersion
|
||||
|└─── org.jetbrains.kotlin.native.platform.* (NNN libraries): $kotlinNativeCompilerVersion
|
||||
| └─── stdlib: $kotlinNativeCompilerVersion
|
||||
|
|
||||
|(*) - dependencies omitted (listed previously)
|
||||
""".trimMargin()
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildConflictingLibrariesAndApplication(
|
||||
directoryPrefix: String,
|
||||
useCache: Boolean,
|
||||
expectedErrorMessage: (compilerVersion: String) -> String
|
||||
) {
|
||||
prepareProject(directory, projectName, localRepo) {
|
||||
val repo = setupLocalRepo()
|
||||
|
||||
buildAndPublishLibrary(directoryPrefix = directoryPrefix, projectName = "liba-v1.0", localRepo = repo)
|
||||
buildAndPublishLibrary(directoryPrefix = directoryPrefix, projectName = "liba-v2.0", localRepo = repo)
|
||||
buildAndPublishLibrary(directoryPrefix = directoryPrefix, projectName = "libb", localRepo = repo)
|
||||
|
||||
buildApplicationAndFail(
|
||||
directoryPrefix = directoryPrefix,
|
||||
projectName = "app",
|
||||
localRepo = repo,
|
||||
useCache = useCache,
|
||||
expectedErrorMessage = expectedErrorMessage
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildApplicationAndFail(
|
||||
directoryPrefix: String?,
|
||||
projectName: String,
|
||||
localRepo: File?,
|
||||
useCache: Boolean,
|
||||
expectedErrorMessage: (compilerVersion: String) -> String
|
||||
) {
|
||||
prepareProject(directoryPrefix, projectName, localRepo, useCache) {
|
||||
build("linkDebugExecutableNative") {
|
||||
assertFailed()
|
||||
|
||||
@@ -192,6 +266,16 @@ class NativeIrLinkerIssuesIT : BaseGradleIT() {
|
||||
val errorMessage = ERROR_LINE_REGEX.findAll(getOutputForTask("linkDebugExecutableNative"))
|
||||
.map { matchResult -> matchResult.groupValues[1] }
|
||||
.map { line -> line.replace(kotlinNativeTargetName, MASKED_TARGET_NAME) }
|
||||
.map { line ->
|
||||
line.replace(COMPRESSED_PLATFORM_LIBS_REGEX) { result ->
|
||||
val rangeWithPlatformLibrariesCount = result.groups[1]!!.range
|
||||
buildString {
|
||||
append(line.substring(0, rangeWithPlatformLibrariesCount.first))
|
||||
append("NNN")
|
||||
append(line.substring(rangeWithPlatformLibrariesCount.last + 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
.joinToString("\n")
|
||||
|
||||
assertEquals(expectedErrorMessage(kotlinNativeCompilerVersion), errorMessage)
|
||||
@@ -199,8 +283,8 @@ class NativeIrLinkerIssuesIT : BaseGradleIT() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildAndPublishLibrary(directory: String, projectName: String, localRepo: File) {
|
||||
prepareProject(directory, projectName, localRepo) {
|
||||
private fun buildAndPublishLibrary(directoryPrefix: String, projectName: String, localRepo: File) {
|
||||
prepareProject(directoryPrefix, projectName, localRepo, useCache = true) {
|
||||
build("publish") {
|
||||
assertSuccessful()
|
||||
}
|
||||
@@ -208,12 +292,13 @@ class NativeIrLinkerIssuesIT : BaseGradleIT() {
|
||||
}
|
||||
|
||||
private fun prepareProject(
|
||||
directory: String?,
|
||||
directoryPrefix: String?,
|
||||
projectName: String,
|
||||
localRepo: File?,
|
||||
useCache: Boolean,
|
||||
block: Project.() -> Unit
|
||||
) {
|
||||
with(transformNativeTestProjectWithPluginDsl(directoryPrefix = directory, projectName = projectName)) {
|
||||
with(transformNativeTestProjectWithPluginDsl(directoryPrefix = directoryPrefix, projectName = projectName)) {
|
||||
if (localRepo != null) {
|
||||
val localRepoUri = localRepo.absoluteFile.toURI().toString()
|
||||
gradleBuildScript().apply {
|
||||
@@ -221,12 +306,16 @@ class NativeIrLinkerIssuesIT : BaseGradleIT() {
|
||||
}
|
||||
}
|
||||
|
||||
gradleProperties().appendText("\nkotlin.native.cacheKind=${if (useCache) "static" else "none"}\n")
|
||||
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private val ERROR_LINE_REGEX = "(?m)^.*\\[ERROR] \\[\\S+] (.*)$".toRegex()
|
||||
private val COMPRESSED_PLATFORM_LIBS_REGEX =
|
||||
".*${KONAN_PLATFORM_LIBS_NAME_PREFIX.replace(".", "\\.")}\\* \\((\\d+) libraries\\).*".toRegex()
|
||||
|
||||
private fun setupLocalRepo(): File = Files.createTempDirectory("localRepo").toAbsolutePath().toFile()
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinCommonToolOptions
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.dsl.NativeCacheKind
|
||||
import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.cocoapods.asValidFrameworkName
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinNativeCompilationData
|
||||
@@ -49,6 +50,7 @@ import org.jetbrains.kotlin.library.*
|
||||
import org.jetbrains.kotlin.project.model.LanguageSettings
|
||||
import org.jetbrains.kotlin.utils.ResolvedDependency as KResolvedDependency
|
||||
import org.jetbrains.kotlin.utils.ResolvedDependencyId as KResolvedDependencyId
|
||||
import org.jetbrains.kotlin.utils.ResolvedDependencies as KResolvedDependencies
|
||||
import org.jetbrains.kotlin.utils.ResolvedDependenciesSupport as KResolvedDependenciesSupport
|
||||
import org.jetbrains.kotlin.utils.ResolvedDependencyArtifactPath as KResolvedDependencyArtifactPath
|
||||
import org.jetbrains.kotlin.utils.ResolvedDependencyVersion as KResolvedDependencyVersion
|
||||
@@ -610,7 +612,7 @@ constructor(
|
||||
override fun buildCompilerArgs(): List<String> = mutableListOf<String>().apply {
|
||||
addAll(super.buildCompilerArgs())
|
||||
|
||||
val externalDependenciesArgs = ExternalDependenciesBuilder(project, binary).buildCompilerArgs()
|
||||
val externalDependenciesArgs = ExternalDependenciesBuilder(project, compilation).buildCompilerArgs()
|
||||
addAll(externalDependenciesArgs)
|
||||
|
||||
addAll(CacheBuilder(project, binary, konanTarget, externalDependenciesArgs).buildCompilerArgs())
|
||||
@@ -689,13 +691,21 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private class ExternalDependenciesBuilder(val project: Project, val binary: NativeBinary) {
|
||||
private val compilation: KotlinNativeCompilation
|
||||
get() = binary.compilation
|
||||
private class ExternalDependenciesBuilder(
|
||||
val project: Project,
|
||||
val compilation: KotlinCompilation<*>,
|
||||
intermediateLibraryName: String?
|
||||
) {
|
||||
constructor(project: Project, compilation: KotlinNativeCompilation) : this(
|
||||
project, compilation, compilation.compileKotlinTask.moduleName
|
||||
)
|
||||
|
||||
private val compileDependencyConfiguration: Configuration
|
||||
get() = project.configurations.getByName(compilation.compileDependencyConfigurationName)
|
||||
|
||||
private val sourceCodeModuleId: KResolvedDependencyId =
|
||||
intermediateLibraryName?.let { KResolvedDependencyId(it) } ?: KResolvedDependencyId.DEFAULT_SOURCE_CODE_MODULE_ID
|
||||
|
||||
fun buildCompilerArgs(): List<String> {
|
||||
val konanVersion = Distribution(project.konanHome).compilerVersion?.let(CompilerVersion.Companion::fromString)
|
||||
?: project.konanVersion
|
||||
@@ -771,7 +781,7 @@ private class ExternalDependenciesBuilder(val project: Project, val binary: Nati
|
||||
}
|
||||
|
||||
compileDependencyConfiguration.incoming.resolutionResult.root.dependencies.forEach { dependencyResult ->
|
||||
processModule(dependencyResult, incomingDependencyId = KResolvedDependencyId.SOURCE_CODE_MODULE_ID)
|
||||
processModule(dependencyResult, incomingDependencyId = sourceCodeModuleId)
|
||||
}
|
||||
|
||||
if (moduleIdsToMerge.isEmpty())
|
||||
@@ -821,6 +831,15 @@ private class ExternalDependenciesBuilder(val project: Project, val binary: Nati
|
||||
return mergedModules.values
|
||||
}
|
||||
|
||||
private fun writeDependenciesFile(dependencies: Collection<KResolvedDependency>, deleteOnExit: Boolean): File? {
|
||||
if (dependencies.isEmpty()) return null
|
||||
|
||||
val dependenciesFile = Files.createTempFile("kotlin-native-external-dependencies", ".deps").toAbsolutePath().toFile()
|
||||
if (deleteOnExit) dependenciesFile.deleteOnExit()
|
||||
dependenciesFile.writeText(KResolvedDependenciesSupport.serialize(KResolvedDependencies(dependencies, sourceCodeModuleId)))
|
||||
return dependenciesFile
|
||||
}
|
||||
|
||||
private val ModuleComponentIdentifier.uniqueName: String
|
||||
get() = "$group:$module"
|
||||
|
||||
@@ -828,27 +847,18 @@ private class ExternalDependenciesBuilder(val project: Project, val binary: Nati
|
||||
@Suppress("unused") // Used for tests only. Accessed via reflection.
|
||||
@JvmStatic
|
||||
fun buildExternalDependenciesFileForTests(project: Project): File? {
|
||||
val executable = project.tasks.asSequence()
|
||||
val compilation = project.tasks.asSequence()
|
||||
.filterIsInstance<KotlinNativeLink>()
|
||||
.map { it.binary }
|
||||
.filterIsInstance<Executable>() // Not TestExecutable or any other kind of NativeBinary. Strictly Executable!
|
||||
.firstOrNull()
|
||||
?.compilation
|
||||
?: return null
|
||||
|
||||
val dependencies = ExternalDependenciesBuilder(project, executable)
|
||||
.buildDependencies()
|
||||
.sortedBy { it.id.toString() }
|
||||
|
||||
return writeDependenciesFile(dependencies, deleteOnExit = false)
|
||||
}
|
||||
|
||||
private fun writeDependenciesFile(dependencies: Collection<KResolvedDependency>, deleteOnExit: Boolean): File? {
|
||||
if (dependencies.isEmpty()) return null
|
||||
|
||||
val dependenciesFile = Files.createTempFile("kotlin-native-external-dependencies", ".deps").toAbsolutePath().toFile()
|
||||
if (deleteOnExit) dependenciesFile.deleteOnExit()
|
||||
dependenciesFile.writeText(KResolvedDependenciesSupport.serialize(dependencies))
|
||||
return dependenciesFile
|
||||
return with(ExternalDependenciesBuilder(project, compilation)) {
|
||||
val dependencies = buildDependencies().sortedBy { it.id.toString() }
|
||||
writeDependenciesFile(dependencies, deleteOnExit = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user