[Commonizer] Implement :native:kotlin-klib-commonizer:api with support for library commonization

- Implement new Gradle module ':native:kotlin-klib-commonizer'
- Implement new NativeKlibCommonize task
- Implement CommonizerTarget.identityString
This commit is contained in:
sebastian.sellmair
2021-02-17 09:33:03 +01:00
committed by Space
parent 6d019d9544
commit 4500b6ce74
59 changed files with 1646 additions and 565 deletions
@@ -0,0 +1,30 @@
/*
* 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.descriptors.commonizer
import org.jetbrains.kotlin.descriptors.commonizer.utils.konanHome
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import kotlin.test.Test
class CliCommonizerTest {
@get:Rule
val temporaryOutputDirectory = TemporaryFolder()
@Test
fun invokeCliWithEmptyArguments() {
val commonizer = CliCommonizer(this::class.java.classLoader)
commonizer.commonizeLibraries(
konanHome = konanHome,
inputLibraries = emptySet(),
dependencyLibraries = emptySet(),
outputCommonizerTarget = CommonizerTarget(KonanTarget.LINUX_X64, KonanTarget.MACOS_X64),
outputDirectory = temporaryOutputDirectory.root
)
}
}
@@ -0,0 +1,80 @@
/*
* 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.descriptors.commonizer
import org.jetbrains.kotlin.descriptors.commonizer.utils.konanHome
import org.jetbrains.kotlin.konan.target.KonanTarget.LINUX_ARM64
import org.jetbrains.kotlin.konan.target.KonanTarget.LINUX_X64
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import java.io.File
import kotlin.test.Test
import kotlin.test.assertTrue
import kotlin.test.fail
class CommonizeLibcurlTest {
@get:Rule
val temporaryOutputDirectory = TemporaryFolder()
@Test
fun commonizeSuccessfully() {
val libraries = File("testData/libcurl").walkTopDown().filter { it.isFile && it.extension == "klib" }.toSet()
val commonizer = CliCommonizer(this::class.java.classLoader)
commonizer.commonizeLibraries(
konanHome = konanHome,
inputLibraries = libraries,
dependencyLibraries = emptySet(),
outputCommonizerTarget = CommonizerTarget(LINUX_ARM64, LINUX_X64),
outputDirectory = temporaryOutputDirectory.root
)
val x64OutputDirectory = temporaryOutputDirectory.root.resolve(CommonizerTarget(LINUX_X64).identityString)
val arm64OutputDirectory = temporaryOutputDirectory.root.resolve(CommonizerTarget(LINUX_ARM64).identityString)
val commonOutputDirectory = temporaryOutputDirectory.root.resolve(CommonizerTarget(LINUX_X64, LINUX_ARM64).identityString)
assertTrue(
x64OutputDirectory.exists(),
"Missing output directory for x64 target"
)
assertTrue(
arm64OutputDirectory.exists(),
"Missing output directory for arm64 target"
)
assertTrue(
commonOutputDirectory.exists(),
"Missing output directory for commonized x64&arm64 target"
)
fun assertContainsKnmFiles(file: File) {
assertTrue(
file.walkTopDown().any { it.extension == "knm" },
"Expected directory ${file.name} to contain at least one knm file"
)
}
assertContainsKnmFiles(x64OutputDirectory)
assertContainsKnmFiles(arm64OutputDirectory)
assertContainsKnmFiles(commonOutputDirectory)
fun assertContainsManifestWithContent(directory: File, content: String) {
val manifest = directory.walkTopDown().firstOrNull { it.name == "manifest" }
?: fail("${directory.name} does not contain any manifest")
assertTrue(
content in manifest.readText(),
"Expected manifest in ${directory.name} to contain $content\n${manifest.readText()}"
)
}
assertContainsManifestWithContent(x64OutputDirectory, "native_targets=linux_x64")
assertContainsManifestWithContent(arm64OutputDirectory, "native_targets=linux_arm64")
assertContainsManifestWithContent(commonOutputDirectory, "native_targets=linux_x64 linux_arm64")
}
}
@@ -0,0 +1,137 @@
/*
* 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.descriptors.commonizer
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.KonanTarget.*
import kotlin.test.Test
import kotlin.test.assertEquals
class CommonizerTargetIdentityStringTest {
@Test
fun leafTargets() {
KonanTarget.predefinedTargets.values.forEach { konanTarget ->
assertEquals(konanTarget.name, CommonizerTarget(konanTarget).identityString)
assertEquals(CommonizerTarget(konanTarget), parseCommonizerTarget(CommonizerTarget(konanTarget).identityString))
}
}
@Test
fun `simple shared targets are invariant under konanTarget order`() {
val macosFirst = CommonizerTarget(MACOS_X64, LINUX_X64)
val linuxFirst = CommonizerTarget(LINUX_X64, MACOS_X64)
assertEquals(macosFirst, linuxFirst)
assertEquals(macosFirst.identityString, linuxFirst.identityString)
assertEquals(linuxFirst, parseCommonizerTarget(linuxFirst.identityString))
assertEquals(macosFirst, parseCommonizerTarget(macosFirst.identityString))
assertEquals(macosFirst, parseCommonizerTarget(linuxFirst.identityString))
assertEquals(linuxFirst, parseCommonizerTarget(macosFirst.identityString))
}
@Test
fun `hierarchical commonizer targets`() {
val hierarchy = SharedCommonizerTarget(
CommonizerTarget(LINUX_X64, MACOS_X64),
CommonizerTarget(IOS_ARM64, IOS_X64)
)
assertEquals(setOf(LINUX_X64, MACOS_X64, IOS_ARM64, IOS_X64), hierarchy.konanTargets)
assertEquals(hierarchy, parseCommonizerTarget(hierarchy.identityString))
}
@Test
fun `multilevel hierarchical commonizer targets`() {
val hierarchy = SharedCommonizerTarget(
SharedCommonizerTarget(
SharedCommonizerTarget(
SharedCommonizerTarget(
CommonizerTarget(LINUX_X64, MACOS_X64),
CommonizerTarget(IOS_X64, IOS_ARM64)
),
CommonizerTarget(LINUX_ARM32_HFP)
),
CommonizerTarget(LINUX_MIPSEL32)
),
CommonizerTarget(WATCHOS_X86, WATCHOS_ARM64)
)
assertEquals(hierarchy, parseCommonizerTarget(hierarchy.identityString))
}
@Test
fun `parsing CommonizerTarget`() {
val target = parseCommonizerTarget("(x, (x, y, (a, b), (b, c)))")
assertEquals(
SharedCommonizerTarget(
LeafCommonizerTarget("x"),
SharedCommonizerTarget(
LeafCommonizerTarget("x"),
LeafCommonizerTarget("y"),
SharedCommonizerTarget(
LeafCommonizerTarget("a"),
LeafCommonizerTarget("b"),
),
SharedCommonizerTarget(
LeafCommonizerTarget("b"),
LeafCommonizerTarget("c")
)
)
),
target
)
}
@Test(expected = IllegalArgumentException::class)
fun `fail parsing CommonizerTarget 1`() {
parseCommonizerTarget("xxx,")
}
@Test(expected = IllegalArgumentException::class)
fun `fail parsing CommonizerTarget 2`() {
parseCommonizerTarget("")
}
@Test(expected = IllegalArgumentException::class)
fun `fail parsing CommonizerTarget 3`() {
parseCommonizerTarget("()")
}
@Test(expected = IllegalArgumentException::class)
fun `fail parsing CommonizerTarget 4`() {
parseCommonizerTarget("(xxx")
}
@Test(expected = IllegalArgumentException::class)
fun `fail parsing CommonizerTarget 5`() {
parseCommonizerTarget("xxx)")
}
@Test(expected = IllegalArgumentException::class)
fun `fail parsing CommonizerTarget 6`() {
parseCommonizerTarget("(xxx")
}
@Test(expected = IllegalArgumentException::class)
fun `fail parsing CommonizerTarget 7`() {
parseCommonizerTarget("(xxx yyy)")
}
@Test(expected = IllegalArgumentException::class)
fun `fail parsing CommonizerTarget 8`() {
parseCommonizerTarget(" ")
}
@Test(expected = IllegalArgumentException::class)
fun `fail parsing CommonizerTarget 9`() {
parseCommonizerTarget("xxx?")
}
@Test(expected = IllegalArgumentException::class)
fun `fail parsing CommonizerTarget 10`() {
parseCommonizerTarget("(x, (x, y)")
}
}
@@ -0,0 +1,88 @@
/*
* 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.descriptors.commonizer
import org.junit.Test
import kotlin.test.assertEquals
class CommonizerTargetPrettyNameTest {
@Test
fun leafTargetNames() {
listOf(
Triple("foo", "[foo]", FOO),
Triple("bar", "[bar]", BAR),
Triple("baz_123", "[baz_123]", BAZ),
).forEach { (name, prettyName, target: LeafCommonizerTarget) ->
assertEquals(name, target.name)
assertEquals(prettyName, target.prettyName)
}
}
@Test
fun sharedTargetNames() {
listOf(
"[foo]" to SharedTarget(FOO),
"[bar, foo]" to SharedTarget(FOO, BAR),
"[bar, baz_123, foo]" to SharedTarget(FOO, BAR, BAZ),
"[bar, baz_123, foo, [bar, foo]]" to SharedTarget(FOO, BAR, BAZ, SharedTarget(FOO, BAR))
).forEach { (prettyName, target: SharedCommonizerTarget) ->
assertEquals(prettyName, target.prettyName)
}
}
@Test
fun prettyCommonizedName() {
val sharedTarget = SharedTarget(FOO, BAR, BAZ)
listOf(
"[bar, baz_123, foo(*)]" to FOO,
"[bar(*), baz_123, foo]" to BAR,
"[bar, baz_123(*), foo]" to BAZ,
"[bar, baz_123, foo]" to sharedTarget,
).forEach { (prettyCommonizerName, target: CommonizerTarget) ->
assertEquals(prettyCommonizerName, sharedTarget.prettyName(target))
}
}
@Test
fun prettyNestedName() {
val target = parseCommonizerTarget("(a, b, (c, (d, e)))") as SharedCommonizerTarget
assertEquals(
"[a, b, [c, [d, e]]]", target.prettyName
)
assertEquals(
"[a, b, [c, [d, e(*)]]]", target.prettyName(LeafCommonizerTarget("e"))
)
assertEquals(
"[a, b, [c, [d, e](*)]]", target.prettyName(parseCommonizerTarget("(d, e)"))
)
assertEquals(
"[a, b, [c, [d, e]](*)]", target.prettyName(parseCommonizerTarget("(c, (d, e))"))
)
assertEquals(
"[a, b(*), [c, [d, e]]]", target.prettyName(LeafCommonizerTarget("b"))
)
}
@Test(expected = IllegalArgumentException::class)
fun sharedTargetNoInnerTargets() {
SharedCommonizerTarget(emptySet<CommonizerTarget>())
}
private companion object {
val FOO = LeafCommonizerTarget("foo")
val BAR = LeafCommonizerTarget("bar")
val BAZ = LeafCommonizerTarget("baz_123")
@Suppress("TestFunctionName")
fun SharedTarget(vararg targets: CommonizerTarget) = SharedCommonizerTarget(linkedSetOf(*targets))
}
}
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2020 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.descriptors.commonizer.utils
import java.io.File
internal val konanHome: File
get() {
val konanHomePath = System.getenv("KONAN_HOME")?.toString() ?: error("Missing KONAN_HOME environment variable")
return File(konanHomePath)
}