diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/KotlinIrLinkerIssues.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/KotlinIrLinkerIssues.kt index 4cfe783b27e..490ea93f7e4 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/KotlinIrLinkerIssues.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/KotlinIrLinkerIssues.kt @@ -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, problemModuleIds: Set, problemCause: String, + sourceCodeModuleId: ResolvedDependencyId, moduleIdComparator: Comparator ) { 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 = - incomingDependencyIdToDependencies.getValue(ResolvedDependencyId.SOURCE_CODE_MODULE_ID) + val firstLevelDependencies: Collection = incomingDependencyIdToDependencies.getValue(sourceCodeModuleId) renderModules(firstLevelDependencies, parentData = null) @@ -375,7 +377,8 @@ private fun findPotentiallyConflictingOutgoingDependencies( */ private fun findPotentiallyConflictingIncomingDependencies( problemModuleId: ResolvedDependencyId, - allModules: Map + allModules: Map, + sourceCodeModuleId: ResolvedDependencyId ): Map { val dependencyStatesMap: MutableMap> = 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 -> { diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/UserVisibleIrModulesSupport.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/UserVisibleIrModulesSupport.kt index 4eae3aa3c1d..c28b2da4c69 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/UserVisibleIrModulesSupport.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/linkerissues/UserVisibleIrModulesSupport.kt @@ -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 + fun load(): ResolvedDependencies companion object { val EMPTY = object : ExternalDependenciesLoader { - override fun load(): Collection = emptyList() + override fun load(): ResolvedDependencies = ResolvedDependencies.EMPTY } fun from(externalDependenciesFile: File?, onMalformedExternalDependencies: (String) -> Unit): ExternalDependenciesLoader = if (externalDependenciesFile != null) object : ExternalDependenciesLoader { - override fun load(): Collection { + 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 + 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): Map { - val modules: Map = deserializers.associate { deserializer -> + protected open fun modulesFromDeserializers( + deserializers: Collection, + excludedModuleIds: Set + ): Map { + val modules: Map = 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): MutableMap { - // First, load external dependencies. - val externalDependencyModules: Collection = externalDependenciesLoader.load() - val externalDependencyModulesByNames: Map = mutableMapOf().apply { externalDependencyModules.forEach { externalDependency -> @@ -118,7 +132,10 @@ open class UserVisibleIrModulesSupport(protected val externalDependenciesLoader: val providedModules = mutableListOf() // 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 } } diff --git a/compiler/tests/org/jetbrains/kotlin/util/ResolvedDependenciesSupportTest.kt b/compiler/tests/org/jetbrains/kotlin/util/ResolvedDependenciesSupportTest.kt index 377a9c43fe6..cb534b183c6 100644 --- a/compiler/tests/org/jetbrains/kotlin/util/ResolvedDependenciesSupportTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/util/ResolvedDependenciesSupportTest.kt @@ -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() diff --git a/compiler/tests/org/jetbrains/kotlin/util/ResolvedDependencyIdTest.kt b/compiler/tests/org/jetbrains/kotlin/util/ResolvedDependencyIdTest.kt index 95f05712ffd..32eec784c54 100644 --- a/compiler/tests/org/jetbrains/kotlin/util/ResolvedDependencyIdTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/util/ResolvedDependencyIdTest.kt @@ -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()) diff --git a/compiler/util/src/org/jetbrains/kotlin/utils/ResolvedDependencies.kt b/compiler/util/src/org/jetbrains/kotlin/utils/ResolvedDependencies.kt index a71a0d8b236..f7a4bb1bb5f 100644 --- a/compiler/util/src/org/jetbrains/kotlin/utils/ResolvedDependencies.kt +++ b/compiler/util/src/org/jetbrains/kotlin/utils/ResolvedDependencies.kt @@ -45,7 +45,7 @@ class ResolvedDependencyId private constructor(val uniqueNames: Set) { } 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) { * - [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, + val sourceCodeModuleId: ResolvedDependencyId +) { + companion object { + val EMPTY = ResolvedDependencies(emptyList(), ResolvedDependencyId.DEFAULT_SOURCE_CODE_MODULE_ID) + } +} + object ResolvedDependenciesSupport { - fun serialize(modules: Collection): 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 { - val moduleIndexToId: MutableMap = mutableMapOf(0 to ResolvedDependencyId.SOURCE_CODE_MODULE_ID) + fun deserialize(source: String, onMalformedLine: (lineNo: Int, line: String) -> Unit): ResolvedDependencies { + val moduleIndexToId: MutableMap = mutableMapOf() val requestedVersionsByIncomingDependenciesIndices: MutableMap> = mutableMapOf() + var sourceCodeModuleId: ResolvedDependencyId? = null val modules = mutableListOf() source.lines().forEachIndexed { lineNo, line -> - fun malformedLine(): Collection { + 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+)\\[(.*)]$") } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanUserVisibleIrModulesSupport.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanUserVisibleIrModulesSupport.kt index 8bca23e3470..ca0919cec46 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanUserVisibleIrModulesSupport.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanUserVisibleIrModulesSupport.kt @@ -26,7 +26,10 @@ class KonanUserVisibleIrModulesSupport( return compressedModules(deserializers) } - override fun modulesFromDeserializers(deserializers: Collection): Map { + override fun modulesFromDeserializers( + deserializers: Collection, + excludedModuleIds: Set + ): Map { val moduleToCompilerVersion: MutableMap = mutableMapOf() val kotlinNativeBundledLibraries: MutableSet = mutableSetOf() @@ -34,6 +37,9 @@ class KonanUserVisibleIrModulesSupport( val modules: Map = 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() ) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeExternalDependenciesIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeExternalDependenciesIT.kt index 1146a7b5016..a9044ffe8ec 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeExternalDependenciesIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeExternalDependenciesIT.kt @@ -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 diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeIrLinkerIssuesIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeIrLinkerIssuesIT.kt index 2731a934ab1..e695791770e 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeIrLinkerIssuesIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeIrLinkerIssuesIT.kt @@ -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() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt index c0d6b47d5e4..aee68c3ecb4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt @@ -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 = mutableListOf().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 { 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, 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() .map { it.binary } .filterIsInstance() // 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, 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) + } } } }