[IR] Tests for IR linking error reporting

^KT-44626
This commit is contained in:
Dmitriy Dolovov
2021-07-29 00:12:51 +03:00
parent c1fb40a436
commit cfc4e715e4
35 changed files with 1067 additions and 15 deletions
@@ -0,0 +1,134 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.util
import org.jetbrains.kotlin.utils.ResolvedDependenciesSupport
import org.junit.Assert.assertEquals
import org.junit.Assert.fail
import org.junit.Test
class ResolvedDependenciesSupportTest {
@Test
fun success1() {
val originalText = """
|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
|${'\t'}/some/path/ktor-io-cinterop-sockets.klib
|2 org.jetbrains.kotlin:kotlin-stdlib-common[1.5.0] #1[1.4.32] #3[1.5.0] #4[1.5.0]
|${'\t'}/some/path/kotlin-stdlib-common-1.5.0.jar
|3 org.jetbrains.kotlinx:kotlinx-coroutines-core,org.jetbrains.kotlinx:kotlinx-coroutines-core-macosx64[1.5.0-RC-native-mt] #1[1.4.3-native-mt] #0[1.5.0-RC-native-mt]
|${'\t'}/some/path/kotlinx-coroutines-core.klib
|4 org.jetbrains.kotlinx:atomicfu,org.jetbrains.kotlinx:atomicfu-macosx64[0.16.1] #3[0.16.1] #1[0.15.1]
|${'\t'}/some/path/atomicfu.klib
|${'\t'}/some/path/atomicfu-cinterop-interop.klib
|
""".trimMargin()
val deserializedModules = ResolvedDependenciesSupport.deserialize(originalText) { lineNo, line ->
fail("Unexpected failure at line $lineNo: $line")
}
val restoredText = ResolvedDependenciesSupport.serialize(deserializedModules)
assertEquals(originalText, restoredText)
}
@Test
fun success2() {
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[1.0] #0[1.0]
|${'\t'}/some/path/libb.klib
|
""".trimMargin()
val deserializedModules = ResolvedDependenciesSupport.deserialize(originalText) { lineNo, line ->
fail("Unexpected failure at line $lineNo: $line")
}
val restoredText = ResolvedDependenciesSupport.serialize(deserializedModules)
assertEquals(originalText, restoredText)
}
@Test
fun success3() {
val originalText = """
|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]
|${'\t'}/some/path/ktor-client-core.klib
|3 org.jetbrains.kotlinx:kotlinx-coroutines-core,org.jetbrains.kotlinx:kotlinx-coroutines-core-macosx64[1.5.0-RC-native-mt] #0[1.5.0-RC-native-mt] #2[1.4.3-native-mt] #5[1.4.3-native-mt] #6[1.4.3-native-mt] #7[1.4.3-native-mt] #8[1.4.3-native-mt]
|${'\t'}/some/path/kotlinx-coroutines-core.klib
|4 org.jetbrains.kotlinx:atomicfu,org.jetbrains.kotlinx:atomicfu-macosx64[0.16.1] #3[0.16.1] #7[0.15.1] #6[0.15.1] #5[0.15.1] #8[0.15.1] #2[0.15.1]
|${'\t'}/some/path/atomicfu.klib
|${'\t'}/some/path/atomicfu-cinterop-interop.klib
|5 io.ktor:ktor-http,io.ktor:ktor-http-macosx64[1.5.4] #2[1.5.4] #8[1.5.4]
|${'\t'}/some/path/ktor-http.klib
|6 io.ktor:ktor-utils,io.ktor:ktor-utils-macosx64[1.5.4] #5[1.5.4]
|${'\t'}/some/path/ktor-utils.klib
|${'\t'}/some/path/ktor-utils-cinterop-utils.klib
|7 io.ktor:ktor-io,io.ktor:ktor-io-macosx64[1.5.4] #6[1.5.4]
|${'\t'}/some/path/ktor-io.klib
|${'\t'}/some/path/ktor-io-cinterop-bits.klib
|${'\t'}/some/path/ktor-io-cinterop-sockets.klib
|8 io.ktor:ktor-http-cio,io.ktor:ktor-http-cio-macosx64[1.5.4] #2[1.5.4]
|${'\t'}/some/path/ktor-http-cio.klib
|
""".trimMargin()
val deserializedModules = ResolvedDependenciesSupport.deserialize(originalText) { lineNo, line ->
fail("Unexpected failure at line $lineNo: $line")
}
val restoredText = ResolvedDependenciesSupport.serialize(deserializedModules)
assertEquals(originalText, restoredText)
}
@Test(expected = NoSuchElementException::class)
fun failure1() {
// There is no record with number 42!
val originalText = """
|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]
|${'\t'}/some/path/libb.klib
|
""".trimMargin()
ResolvedDependenciesSupport.deserialize(originalText) { _, _ -> throw MyException() }
}
@Test(expected = MyException::class)
fun failure2() {
// Name 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 [1.0] #0[1.0]
|${'\t'}/some/path/libb.klib
|
""".trimMargin()
ResolvedDependenciesSupport.deserialize(originalText) { _, _ -> throw MyException() }
}
@Test(expected = MyException::class)
fun failure3() {
// Version 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]
|${'\t'}/some/path/libb.klib
|
""".trimMargin()
ResolvedDependenciesSupport.deserialize(originalText) { _, _ -> throw MyException() }
}
private class MyException : Exception()
}
@@ -0,0 +1,56 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.util
import org.jetbrains.kotlin.utils.ResolvedDependencyId
import org.jetbrains.kotlin.utils.ResolvedDependencyVersion
import org.junit.Assert.*
import org.junit.Test
class ResolvedDependencyIdTest {
@Test(expected = IllegalStateException::class)
fun failOnNoNames1() {
ResolvedDependencyId()
}
@Test(expected = IllegalStateException::class)
fun failOnNoNames2() {
ResolvedDependencyId(emptyList())
}
@Test
fun namesAreOrdered() {
val characters: List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9')
val uniqueNames: List<String> = List(10) {
List(20) { characters.random() }.joinToString("")
}
val moduleId = ResolvedDependencyId(uniqueNames)
assertEquals(uniqueNames.sorted(), moduleId.uniqueNames.toList())
}
@Test
fun contains() {
assertTrue(ResolvedDependencyId("foo", "bar") in ResolvedDependencyId("foo", "bar"))
assertTrue(ResolvedDependencyId("foo", "bar") in ResolvedDependencyId("foo", "bar", "baz"))
assertFalse(ResolvedDependencyId("foo", "bar") in ResolvedDependencyId("foo", "baz"))
}
@Test
fun withVersion() {
assertEquals("foo: 1.0.1", ResolvedDependencyId("foo").withVersion(ResolvedDependencyVersion("1.0.1")))
assertEquals("foo", ResolvedDependencyId("foo").withVersion(ResolvedDependencyVersion("")))
assertEquals("foo", ResolvedDependencyId("foo").withVersion(ResolvedDependencyVersion.EMPTY))
}
@Test
fun toStringImplementation() {
assertEquals("/", ResolvedDependencyId.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())
assertEquals("bar (baz, foo, qux)", ResolvedDependencyId("foo", "bar", "baz", "qux").toString())
}
}
@@ -0,0 +1,134 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.native
import org.jetbrains.kotlin.gradle.BaseGradleIT
import org.jetbrains.kotlin.gradle.GradleVersionRequired
import org.junit.Test
import java.io.File
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
class NativeExternalDependenciesIT : BaseGradleIT() {
override val defaultGradleVersion: GradleVersionRequired
get() = GradleVersionRequired.FOR_MPP_SUPPORT
@Test
fun `no external dependencies`() = buildProjectWithDependencies { externalDependenciesText ->
assertNull(externalDependenciesText)
}
@Test
fun `ktor 1_5_4 and coroutines 1_4_3-native-mt`() = buildProjectWithDependencies(
"io.ktor:ktor-io:1.5.4",
"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3-native-mt"
) { externalDependenciesText ->
assertNotNull(externalDependenciesText)
assertEquals(
"""
|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
|${'\t'}/some/path/ktor-io-cinterop-sockets.klib
|2 org.jetbrains.kotlinx:atomicfu,org.jetbrains.kotlinx:atomicfu-$MASKED_TARGET_NAME[0.15.1] #3[0.15.1] #1[0.15.1]
|${'\t'}/some/path/atomicfu.klib
|${'\t'}/some/path/atomicfu-cinterop-interop.klib
|3 org.jetbrains.kotlinx:kotlinx-coroutines-core,org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME[1.4.3-native-mt] #0[1.4.3-native-mt] #1[1.4.3-native-mt]
|${'\t'}/some/path/kotlinx-coroutines-core.klib
|
""".trimMargin(),
externalDependenciesText
)
}
@Test
fun `ktor 1_5_4 and coroutines 1_5_0-RC-native-mt`() = buildProjectWithDependencies(
"io.ktor:ktor-io:1.5.4",
"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC-native-mt"
) { externalDependenciesText ->
assertNotNull(externalDependenciesText)
assertEquals(
"""
|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
|${'\t'}/some/path/ktor-io-cinterop-sockets.klib
|2 org.jetbrains.kotlinx:atomicfu,org.jetbrains.kotlinx:atomicfu-$MASKED_TARGET_NAME[0.16.1] #3[0.16.1] #1[0.15.1]
|${'\t'}/some/path/atomicfu.klib
|${'\t'}/some/path/atomicfu-cinterop-interop.klib
|3 org.jetbrains.kotlinx:kotlinx-coroutines-core,org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME[1.5.0-RC-native-mt] #0[1.5.0-RC-native-mt] #1[1.4.3-native-mt]
|${'\t'}/some/path/kotlinx-coroutines-core.klib
|
""".trimMargin(),
externalDependenciesText
)
}
private fun buildProjectWithDependencies(
vararg dependencies: String,
externalDependenciesTextConsumer: (externalDependenciesText: String?) -> Unit
) {
with(transformNativeTestProjectWithPluginDsl("native-external-dependencies")) {
gradleBuildScript().appendText(
"""
|
|kotlin {
| val commonMain by sourceSets.getting {
| dependencies {${dependencies.joinToString("") { "\n| implementation(\"$it\")" }}
| }
| }
|}
""".trimMargin()
)
build(
"assemble",
options = defaultBuildOptions().copy(dryRun = true)
) {
assertSuccessful()
val kotlinNativeTargetName = findKotlinNativeTargetName(output)
assertNotNull(kotlinNativeTargetName)
val externalDependenciesFile = findParameterInOutput("for_test_external_dependencies_file", output)?.let(::File)
val externalDependenciesText = if (externalDependenciesFile?.exists() == true) {
externalDependenciesFile.readText()
.lineSequence()
.map { line ->
if (line.firstOrNull()?.isWhitespace() == true) {
// Transform artifact path.
val pureFileName = File(line.trimStart()).name
"\t/some/path/$pureFileName"
} else {
// Transform module name (mask target name).
line.replace(kotlinNativeTargetName, MASKED_TARGET_NAME)
}
}
.joinToString("\n")
} else null
externalDependenciesTextConsumer(externalDependenciesText)
}
}
}
internal companion object {
const val MASKED_TARGET_NAME = "testTargetX64"
fun findParameterInOutput(name: String, output: String): String? =
output.lineSequence().mapNotNull { line ->
val (key, value) = line.split('=', limit = 2).takeIf { it.size == 2 } ?: return@mapNotNull null
if (key.endsWith(name)) value else null
}.firstOrNull()
fun findKotlinNativeTargetName(output: String): String? = findParameterInOutput("for_test_kotlin_native_target", output)
?.takeIf(String::isNotBlank)
?.toLowerCase()
}
}
@@ -0,0 +1,231 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.native
import org.jetbrains.kotlin.gradle.BaseGradleIT
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.junit.Test
import java.io.File
import java.nio.file.Files
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
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)`() = buildApplicationAndFail(
directory = null,
projectName = "native-ir-linker-issues-ktor-and-coroutines",
localRepo = null
) { kotlinNativeCompilerVersion ->
"""
|e: The symbol of unexpected type encountered during IR deserialization: IrClassPublicSymbolImpl, kotlinx.coroutines/CancellationException|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.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME)" and may lead to conflicts:
|1. "io.ktor:ktor-client-core (io.ktor:ktor-client-core-$MASKED_TARGET_NAME): 1.5.4" (was compiled against "org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME): 1.4.3-native-mt" but "org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME): 1.5.0-RC-native-mt" is used in the project)
|2. "io.ktor:ktor-http (io.ktor:ktor-http-$MASKED_TARGET_NAME): 1.5.4" (was compiled against "org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME): 1.4.3-native-mt" but "org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME): 1.5.0-RC-native-mt" is used in the project)
|3. "io.ktor:ktor-http-cio (io.ktor:ktor-http-cio-$MASKED_TARGET_NAME): 1.5.4" (was compiled against "org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME): 1.4.3-native-mt" but "org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME): 1.5.0-RC-native-mt" is used in the project)
|4. "io.ktor:ktor-io (io.ktor:ktor-io-$MASKED_TARGET_NAME): 1.5.4" (was compiled against "org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME): 1.4.3-native-mt" but "org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME): 1.5.0-RC-native-mt" is used in the project)
|5. "io.ktor:ktor-utils (io.ktor:ktor-utils-$MASKED_TARGET_NAME): 1.5.4" (was compiled against "org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME): 1.4.3-native-mt" but "org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME): 1.5.0-RC-native-mt" is used in the project)
|
|Project dependencies:
|├─── io.ktor:ktor-client-core (io.ktor:ktor-client-core-$MASKED_TARGET_NAME): 1.5.4
|│ ├─── io.ktor:ktor-http (io.ktor:ktor-http-$MASKED_TARGET_NAME): 1.5.4
|│ │ ├─── io.ktor:ktor-utils (io.ktor:ktor-utils-$MASKED_TARGET_NAME): 1.5.4
|│ │ │ ├─── io.ktor:ktor-io (io.ktor:ktor-io-$MASKED_TARGET_NAME): 1.5.4
|│ │ │ │ ├─── io.ktor:ktor-io-cinterop-bits: 1.5.4
|│ │ │ │ │ └─── stdlib: 1.4.32 -> $kotlinNativeCompilerVersion
|│ │ │ │ ├─── io.ktor:ktor-io-cinterop-sockets: 1.5.4
|│ │ │ │ │ └─── stdlib: 1.4.32 -> $kotlinNativeCompilerVersion
|│ │ │ │ ├─── stdlib: 1.4.32 -> $kotlinNativeCompilerVersion
|│ │ │ │ ├─── org.jetbrains.kotlin.native.platform.CoreFoundation: 1.4.32 -> $kotlinNativeCompilerVersion
|│ │ │ │ │ ├─── stdlib: $kotlinNativeCompilerVersion
|│ │ │ │ │ ├─── org.jetbrains.kotlin.native.platform.darwin: $kotlinNativeCompilerVersion
|│ │ │ │ │ │ ├─── stdlib: $kotlinNativeCompilerVersion
|│ │ │ │ │ │ └─── org.jetbrains.kotlin.native.platform.posix: $kotlinNativeCompilerVersion
|│ │ │ │ │ │ └─── stdlib: $kotlinNativeCompilerVersion
|│ │ │ │ │ └─── org.jetbrains.kotlin.native.platform.posix: $kotlinNativeCompilerVersion (*)
|│ │ │ │ ├─── org.jetbrains.kotlin.native.platform.darwin: 1.4.32 -> $kotlinNativeCompilerVersion (*)
|│ │ │ │ ├─── org.jetbrains.kotlin.native.platform.iconv: 1.4.32 -> $kotlinNativeCompilerVersion
|│ │ │ │ │ ├─── stdlib: $kotlinNativeCompilerVersion
|│ │ │ │ │ └─── org.jetbrains.kotlin.native.platform.posix: $kotlinNativeCompilerVersion (*)
|│ │ │ │ ├─── org.jetbrains.kotlin.native.platform.posix: 1.4.32 -> $kotlinNativeCompilerVersion (*)
|│ │ │ │ ├─── org.jetbrains.kotlinx:atomicfu (org.jetbrains.kotlinx:atomicfu-$MASKED_TARGET_NAME): 0.15.1 -> 0.16.1
|│ │ │ │ │ ├─── stdlib: 1.5 -> $kotlinNativeCompilerVersion
|│ │ │ │ │ ├─── org.jetbrains.kotlin.native.platform.posix: 1.5 -> $kotlinNativeCompilerVersion (*)
|│ │ │ │ │ └─── org.jetbrains.kotlinx:atomicfu-cinterop-interop: 0.16.1
|│ │ │ │ │ ├─── stdlib: 1.5 -> $kotlinNativeCompilerVersion
|│ │ │ │ │ └─── org.jetbrains.kotlin.native.platform.posix: 1.5 -> $kotlinNativeCompilerVersion (*)
|│ │ │ │ ├─── org.jetbrains.kotlinx:atomicfu-cinterop-interop: 0.16.1 (*)
|│ │ │ │ └─── org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME): 1.4.3-native-mt -> 1.5.0-RC-native-mt
|│ │ │ │ ^^^ This module contains symbol kotlinx.coroutines/CancellationException|null[0] that is the cause of the conflict.
|│ │ │ │ ├─── stdlib: 1.5 -> $kotlinNativeCompilerVersion
|│ │ │ │ ├─── org.jetbrains.kotlin.native.platform.CoreFoundation: 1.5 -> $kotlinNativeCompilerVersion (*)
|│ │ │ │ ├─── org.jetbrains.kotlin.native.platform.darwin: 1.5 -> $kotlinNativeCompilerVersion (*)
|│ │ │ │ ├─── org.jetbrains.kotlin.native.platform.posix: 1.5 -> $kotlinNativeCompilerVersion (*)
|│ │ │ │ ├─── org.jetbrains.kotlinx:atomicfu (org.jetbrains.kotlinx:atomicfu-$MASKED_TARGET_NAME): 0.16.1 (*)
|│ │ │ │ └─── org.jetbrains.kotlinx:atomicfu-cinterop-interop: 0.16.1 (*)
|│ │ │ ├─── org.jetbrains.kotlinx:atomicfu (org.jetbrains.kotlinx:atomicfu-$MASKED_TARGET_NAME): 0.15.1 -> 0.16.1 (*)
|│ │ │ └─── org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME): 1.4.3-native-mt -> 1.5.0-RC-native-mt (*)
|│ │ │ ^^^ This module contains symbol kotlinx.coroutines/CancellationException|null[0] that is the cause of the conflict.
|│ │ ├─── org.jetbrains.kotlinx:atomicfu (org.jetbrains.kotlinx:atomicfu-$MASKED_TARGET_NAME): 0.15.1 -> 0.16.1 (*)
|│ │ └─── org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME): 1.4.3-native-mt -> 1.5.0-RC-native-mt (*)
|│ │ ^^^ This module contains symbol kotlinx.coroutines/CancellationException|null[0] that is the cause of the conflict.
|│ ├─── io.ktor:ktor-http-cio (io.ktor:ktor-http-cio-$MASKED_TARGET_NAME): 1.5.4
|│ │ ├─── io.ktor:ktor-http (io.ktor:ktor-http-$MASKED_TARGET_NAME): 1.5.4 (*)
|│ │ ├─── org.jetbrains.kotlinx:atomicfu (org.jetbrains.kotlinx:atomicfu-$MASKED_TARGET_NAME): 0.15.1 -> 0.16.1 (*)
|│ │ └─── org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME): 1.4.3-native-mt -> 1.5.0-RC-native-mt (*)
|│ │ ^^^ This module contains symbol kotlinx.coroutines/CancellationException|null[0] that is the cause of the conflict.
|│ ├─── org.jetbrains.kotlinx:atomicfu (org.jetbrains.kotlinx:atomicfu-$MASKED_TARGET_NAME): 0.15.1 -> 0.16.1 (*)
|│ └─── org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME): 1.4.3-native-mt -> 1.5.0-RC-native-mt (*)
|│ ^^^ This module contains symbol kotlinx.coroutines/CancellationException|null[0] that is the cause of the conflict.
|└─── org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-$MASKED_TARGET_NAME): 1.5.0-RC-native-mt (*)
| ^^^ This module contains symbol kotlinx.coroutines/CancellationException|null[0] that is the cause of the conflict.
|
|(*) - dependencies omitted (listed previously)
""".trimMargin()
}
private fun buildApplicationAndFail(
directory: String?,
projectName: String,
localRepo: File?,
expectedErrorMessage: (compilerVersion: String) -> String
) {
prepareProject(directory, projectName, localRepo) {
build("linkDebugExecutableNative") {
assertFailed()
val kotlinNativeTargetName = findKotlinNativeTargetName(output)
assertNotNull(kotlinNativeTargetName)
val kotlinNativeCompilerVersion = findKotlinNativeCompilerVersion(output)
assertNotNull(kotlinNativeCompilerVersion)
val errorMessage = ERROR_LINE_REGEX.findAll(getOutputForTask("linkDebugExecutableNative"))
.map { matchResult -> matchResult.groupValues[1] }
.map { line -> line.replace(kotlinNativeTargetName, MASKED_TARGET_NAME) }
.joinToString("\n")
assertEquals(expectedErrorMessage(kotlinNativeCompilerVersion), errorMessage)
}
}
}
private fun buildAndPublishLibrary(directory: String, projectName: String, localRepo: File) {
prepareProject(directory, projectName, localRepo) {
build("publish") {
assertSuccessful()
}
}
}
private fun prepareProject(
directory: String?,
projectName: String,
localRepo: File?,
block: Project.() -> Unit
) {
with(transformNativeTestProjectWithPluginDsl(directoryPrefix = directory, projectName = projectName)) {
if (localRepo != null) {
val localRepoPath = localRepo.absolutePath
gradleBuildScript().apply {
writeText(readText().replace("<LocalRepo>", localRepoPath))
}
}
block()
}
}
private companion object {
private val ERROR_LINE_REGEX = "(?m)^.*\\[ERROR] \\[\\S+] (.*)$".toRegex()
private fun setupLocalRepo(): File = Files.createTempDirectory("localRepo").toAbsolutePath().toFile()
fun findKotlinNativeCompilerVersion(output: String): String? = findParameterInOutput(
"for_test_kotlin_native_compiler_version",
output
)
}
}
@@ -0,0 +1,28 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
}
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
<SingleNativeTarget>("host") {
binaries {
executable {
entryPoint = "main"
}
}
}
}
afterEvaluate {
val externalDependenciesFile = Class.forName("org.jetbrains.kotlin.gradle.tasks.ExternalDependenciesBuilder")
.getDeclaredMethod("buildExternalDependenciesFileForTests", Project::class.java).apply { isAccessible = true }
.invoke(null, project)
?.toString().orEmpty()
println("for_test_kotlin_native_target=<SingleNativeTarget>")
println("for_test_external_dependencies_file=$externalDependenciesFile")
}
@@ -0,0 +1,11 @@
pluginManagement {
repositories {
mavenLocal()
mavenCentral()
gradlePluginPortal()
}
}
enableFeaturePreview('GRADLE_METADATA')
rootProject.name = "native-external-dependencies"
@@ -0,0 +1,30 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
}
repositories {
mavenCentral()
mavenLocal()
maven("<LocalRepo>")
}
kotlin {
<SingleNativeTarget>("native") {
binaries {
executable {
entryPoint = "main"
}
}
sourceSets["commonMain"].dependencies {
implementation("org.sample:libb:1.0") // libb:1.0 is compatible with liba:1.0 only!
implementation("org.sample:liba:2.0") // liba:1.0 -> liba:2.0
}
}
}
val konanHome: String? by ext.properties
val kotlinNativeCompilerVersion = konanHome?.let { org.jetbrains.kotlin.konan.target.Distribution(it).compilerVersion }
?: "<pluginMarkerVersion>"
println("for_test_kotlin_native_target=<SingleNativeTarget>")
println("for_test_kotlin_native_compiler_version=$kotlinNativeCompilerVersion")
@@ -0,0 +1,11 @@
pluginManagement {
repositories {
mavenLocal()
mavenCentral()
gradlePluginPortal()
}
}
enableFeaturePreview('GRADLE_METADATA')
rootProject.name = "app"
@@ -0,0 +1,17 @@
package sample.app
import sample.liba.A
import sample.liba.B
import sample.libb.getA
import sample.libb.getB
import sample.libb.getAll
fun main() {
val a: A = getA()
val b: B = getB()
val all = getAll()
println("a.hashCode(): ${a.hashCode()}")
println("b.hashCode(): ${b.hashCode()}")
println("all: $all")
}
@@ -0,0 +1,24 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
id("maven-publish")
}
group = "org.sample"
version = "1.0"
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
<SingleNativeTarget>("native")
}
publishing {
repositories {
maven {
url = file("<LocalRepo>").toURI()
}
}
}
@@ -0,0 +1,11 @@
pluginManagement {
repositories {
mavenLocal()
mavenCentral()
gradlePluginPortal()
}
}
enableFeaturePreview('GRADLE_METADATA')
rootProject.name = "liba"
@@ -0,0 +1,24 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
id("maven-publish")
}
group = "org.sample"
version = "2.0"
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
<SingleNativeTarget>("native")
}
publishing {
repositories {
maven {
url = file("<LocalRepo>").toURI()
}
}
}
@@ -0,0 +1,11 @@
pluginManagement {
repositories {
mavenLocal()
mavenCentral()
gradlePluginPortal()
}
}
enableFeaturePreview('GRADLE_METADATA')
rootProject.name = "liba"
@@ -0,0 +1,29 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
id("maven-publish")
}
group = "org.sample"
version = "1.0"
repositories {
mavenLocal()
mavenCentral()
maven("<LocalRepo>")
}
kotlin {
<SingleNativeTarget>("native") {
sourceSets["nativeMain"].dependencies {
implementation("org.sample:liba:1.0")
}
}
}
publishing {
repositories {
maven {
url = file("<LocalRepo>").toURI()
}
}
}
@@ -0,0 +1,11 @@
pluginManagement {
repositories {
mavenLocal()
mavenCentral()
gradlePluginPortal()
}
}
enableFeaturePreview('GRADLE_METADATA')
rootProject.name = "libb"
@@ -0,0 +1,26 @@
package sample.libb
import sample.liba.*
fun getAll(): List<Any> {
return listOf(getA(), getB(), getC())
}
fun getA(): A {
val a = A()
println("Returning a: ${a::class}, $a")
return a
}
fun getB(): B {
val b = B()
println("Returning b: ${b::class}, $b")
return b
}
fun getC(): C {
val c = C()
println("Returning c: ${c::class}, $c")
return c
}
@@ -0,0 +1,29 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
}
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
<SingleNativeTarget>("native") {
binaries {
executable {
entryPoint = "main"
}
}
sourceSets["commonMain"].dependencies {
implementation("io.ktor:ktor-client-core:1.5.4")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC-native-mt")
}
}
}
val konanHome: String? by ext.properties
val kotlinNativeCompilerVersion = konanHome?.let { org.jetbrains.kotlin.konan.target.Distribution(it).compilerVersion }
?: "<pluginMarkerVersion>"
println("for_test_kotlin_native_target=<SingleNativeTarget>")
println("for_test_kotlin_native_compiler_version=$kotlinNativeCompilerVersion")
@@ -0,0 +1,11 @@
pluginManagement {
repositories {
mavenLocal()
mavenCentral()
gradlePluginPortal()
}
}
enableFeaturePreview('GRADLE_METADATA')
rootProject.name = "native-ir-linker-issues-ktor-and-coroutines"
@@ -0,0 +1,30 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
}
repositories {
mavenCentral()
mavenLocal()
maven("<LocalRepo>")
}
kotlin {
<SingleNativeTarget>("native") {
binaries {
executable {
entryPoint = "main"
}
}
sourceSets["commonMain"].dependencies {
implementation("org.sample:libb:1.0")
implementation("org.sample:liba:2.0")
}
}
}
val konanHome: String? by ext.properties
val kotlinNativeCompilerVersion = konanHome?.let { org.jetbrains.kotlin.konan.target.Distribution(it).compilerVersion }
?: "<pluginMarkerVersion>"
println("for_test_kotlin_native_target=<SingleNativeTarget>")
println("for_test_kotlin_native_compiler_version=$kotlinNativeCompilerVersion")
@@ -0,0 +1,11 @@
pluginManagement {
repositories {
mavenLocal()
mavenCentral()
gradlePluginPortal()
}
}
enableFeaturePreview('GRADLE_METADATA')
rootProject.name = "app"
@@ -0,0 +1,14 @@
package sample.app
import sample.liba.A
import sample.liba.B
import sample.libb.getA
import sample.libb.getB
fun main() {
val a = getA()
val b = getB()
println("a.hashCode(): ${a.hashCode()}")
println("b.hashCode(): ${b.hashCode()}")
}
@@ -0,0 +1,24 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
id("maven-publish")
}
group = "org.sample"
version = "1.0"
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
<SingleNativeTarget>("native")
}
publishing {
repositories {
maven {
url = file("<LocalRepo>").toURI()
}
}
}
@@ -0,0 +1,11 @@
pluginManagement {
repositories {
mavenLocal()
mavenCentral()
gradlePluginPortal()
}
}
enableFeaturePreview('GRADLE_METADATA')
rootProject.name = "liba"
@@ -0,0 +1,24 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
id("maven-publish")
}
group = "org.sample"
version = "2.0"
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
<SingleNativeTarget>("native")
}
publishing {
repositories {
maven {
url = file("<LocalRepo>").toURI()
}
}
}
@@ -0,0 +1,11 @@
pluginManagement {
repositories {
mavenLocal()
mavenCentral()
gradlePluginPortal()
}
}
enableFeaturePreview('GRADLE_METADATA')
rootProject.name = "liba"
@@ -0,0 +1,29 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
id("maven-publish")
}
group = "org.sample"
version = "1.0"
repositories {
mavenLocal()
mavenCentral()
maven("<LocalRepo>")
}
kotlin {
<SingleNativeTarget>("native") {
sourceSets["nativeMain"].dependencies {
implementation("org.sample:liba:1.0")
}
}
}
publishing {
repositories {
maven {
url = file("<LocalRepo>").toURI()
}
}
}
@@ -0,0 +1,11 @@
pluginManagement {
repositories {
mavenLocal()
mavenCentral()
gradlePluginPortal()
}
}
enableFeaturePreview('GRADLE_METADATA')
rootProject.name = "libb"
@@ -0,0 +1,16 @@
package sample.libb
import sample.liba.*
fun getA(): A {
val a = A()
println("Returning a: ${a::class}, $a")
return a
}
fun getB(): B {
val b = B()
println("Returning b: ${b::class}, $")
return b
}
@@ -118,10 +118,9 @@ private fun FileCollection.filterOutPublishableInteropLibs(project: Project): Fi
* for it (NO-SOURCE check). So we need to take this case into account
* and skip libraries that were not compiled. See also: GH-2617 (K/N repo).
*/
private val File.isKlibPassedToCompiler: Boolean
get() = (extension == "klib" || isDirectory) && exists()
private fun Collection<File>.filterKlibsPassedToCompiler(): List<File> = filter { it.isKlibPassedToCompiler }
private fun Collection<File>.filterKlibsPassedToCompiler(): List<File> = filter {
(it.extension == "klib" || it.isDirectory) && it.exists()
}
// endregion
abstract class AbstractKotlinNativeCompile<T : KotlinCommonToolOptions, K : KotlinNativeCompilationData<*>> : AbstractCompile() {
@@ -690,8 +689,7 @@ constructor(
}
}
@Suppress("FunctionName")
internal class ExternalDependenciesBuilder(val project: Project, val binary: NativeBinary) {
private class ExternalDependenciesBuilder(val project: Project, val binary: NativeBinary) {
private val compilation: KotlinNativeCompilation
get() = binary.compilation
@@ -702,16 +700,13 @@ internal class ExternalDependenciesBuilder(val project: Project, val binary: Nat
val konanVersion = Distribution(project.konanHome).compilerVersion?.let(CompilerVersion.Companion::fromString)
?: project.konanVersion
if (!konanVersion.isAtLeast(1, 6, 0)) return emptyList()
if (konanVersion.isAtLeast(1, 6, 0)) {
val dependenciesFile = writeDependenciesFile(buildDependencies(), deleteOnExit = true)
if (dependenciesFile != null)
return listOf("-Xexternal-dependencies=${dependenciesFile.path}")
}
val modules = buildDependencies()
if (modules.isEmpty()) return emptyList()
val dependenciesFile = Files.createTempFile("kotlin-native-external-dependencies", ".deps").toAbsolutePath().toFile()
dependenciesFile.deleteOnExit()
dependenciesFile.writeText(KResolvedDependenciesSupport.serialize(modules))
return listOf("-Xexternal-dependencies=${dependenciesFile.path}")
return emptyList()
}
private fun buildDependencies(): Collection<KResolvedDependency> {
@@ -828,6 +823,34 @@ internal class ExternalDependenciesBuilder(val project: Project, val binary: Nat
private val ModuleComponentIdentifier.uniqueName: String
get() = "$group:$module"
companion object {
@Suppress("unused") // Used for tests only. Accessed via reflection.
@JvmStatic
fun buildExternalDependenciesFileForTests(project: Project): File? {
val executable = project.tasks.asSequence()
.filterIsInstance<KotlinNativeLink>()
.map { it.binary }
.filterIsInstance<Executable>() // Not TestExecutable or any other kind of NativeBinary. Strictly Executable!
.firstOrNull()
?: 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
}
}
}
internal class CacheBuilder(