[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
+44
View File
@@ -0,0 +1,44 @@
import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
plugins {
kotlin("jvm")
id("jps-compatible")
}
kotlin {
explicitApi()
}
description = "Kotlin KLIB Library Commonizer API"
publish()
dependencies {
implementation(kotlinStdlib())
implementation(project(":native:kotlin-native-utils"))
testImplementation(project(":kotlin-test::kotlin-test-junit"))
testImplementation(commonDep("junit:junit"))
testImplementation(projectTests(":compiler:tests-common"))
testRuntimeOnly(project(":native:kotlin-klib-commonizer"))
}
sourceSets {
"main" { projectDefault() }
"test" { projectDefault() }
}
tasks.register("downloadNativeCompiler") {
doFirst {
NativeCompilerDownloader(project).downloadIfNeeded()
}
}
projectTest(parallel = false) {
dependsOn(":dist")
dependsOn("downloadNativeCompiler")
workingDir = projectDir
environment("KONAN_HOME", NativeCompilerDownloader(project).compilerDirectory.absolutePath)
}
runtimeJar()
sourcesJar()
javadocJar()
@@ -0,0 +1,61 @@
/*
* 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 java.io.File
import java.net.URLClassLoader
import kotlin.jvm.Throws
public fun CliCommonizer(classpath: Iterable<File>): CliCommonizer {
return CliCommonizer(URLClassLoader(classpath.map { it.absoluteFile.toURI().toURL() }.toTypedArray()))
}
public fun CliCommonizer(classLoader: ClassLoader): CliCommonizer {
return CliCommonizer(CommonizerClassLoaderExecutor(classLoader))
}
public class CliCommonizer(private val executor: Executor) : Commonizer {
public fun interface Executor {
public operator fun invoke(arguments: List<String>)
}
override fun commonizeLibraries(
konanHome: File,
inputLibraries: Set<File>,
dependencyLibraries: Set<File>,
outputCommonizerTarget: SharedCommonizerTarget,
outputDirectory: File
) {
val arguments = mutableListOf<String>().apply {
add("native-klib-commonize")
add("-distribution-path"); add(konanHome.absolutePath)
add("-input-libraries"); add(inputLibraries.joinToString(";") { it.absolutePath })
add("-dependency-libraries"); add(dependencyLibraries.joinToString(";") { it.absolutePath })
add("-output-commonizer-target"); add(outputCommonizerTarget.identityString)
add("-output-path"); add(outputDirectory.absolutePath)
}
executor(arguments)
}
}
private class CommonizerClassLoaderExecutor(private val commonizerClassLoader: ClassLoader) : CliCommonizer.Executor {
companion object {
private const val commonizerMainClass = "org.jetbrains.kotlin.descriptors.commonizer.cli.CommonizerCLI"
private const val commonizerMainFunction = "main"
}
@Throws(Throwable::class)
override fun invoke(arguments: List<String>) {
val commonizerMainClass = commonizerClassLoader.loadClass(commonizerMainClass)
val commonizerMainMethod = commonizerMainClass.methods.singleOrNull { it.name == commonizerMainFunction }
?: throw IllegalArgumentException(
"Missing or conflicting $commonizerMainFunction function in " +
"Class ${commonizerMainClass.name} from ClassLoader $commonizerClassLoader"
)
commonizerMainMethod.invoke(null, arguments.toTypedArray())
}
}
@@ -0,0 +1,21 @@
/*
* 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 java.io.File
import java.io.Serializable
import kotlin.jvm.Throws
public interface Commonizer : Serializable {
@Throws(Throwable::class)
public fun commonizeLibraries(
konanHome: File,
inputLibraries: Set<File>,
dependencyLibraries: Set<File>,
outputCommonizerTarget: SharedCommonizerTarget,
outputDirectory: File
)
}
@@ -0,0 +1,100 @@
/*
* 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.
*/
@file:Suppress("FunctionName")
package org.jetbrains.kotlin.descriptors.commonizer
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.Serializable
// N.B. TargetPlatform/SimplePlatform are non exhaustive enough to address both target platforms such as
// JVM, JS and concrete Kotlin/Native targets, e.g. macos_x64, ios_x64, linux_x64.
public sealed class CommonizerTarget : Serializable {
final override fun toString(): String = identityString
}
public data class LeafCommonizerTarget public constructor(val name: String) : CommonizerTarget() {
public constructor(konanTarget: KonanTarget) : this(konanTarget.name)
public val konanTargetOrNull: KonanTarget? = KonanTarget.predefinedTargets[name]
public val konanTarget: KonanTarget get() = konanTargetOrNull ?: error("Unknown KonanTarget: $name")
}
public data class SharedCommonizerTarget(val targets: Set<CommonizerTarget>) : CommonizerTarget() {
public constructor(vararg targets: CommonizerTarget) : this(targets.toSet())
public constructor(vararg targets: KonanTarget) : this(targets.toSet())
public constructor(targets: Iterable<KonanTarget>) : this(targets.map(::LeafCommonizerTarget).toSet())
init {
require(targets.isNotEmpty())
}
}
public fun CommonizerTarget(konanTargets: Iterable<KonanTarget>): CommonizerTarget {
val konanTargetsSet = konanTargets.toSet()
require(konanTargetsSet.isNotEmpty()) { "Empty set of of konanTargets" }
val leafTargets = konanTargetsSet.map(::LeafCommonizerTarget)
return leafTargets.singleOrNull() ?: SharedCommonizerTarget(leafTargets.toSet())
}
public fun CommonizerTarget(konanTarget: KonanTarget): LeafCommonizerTarget {
return LeafCommonizerTarget(konanTarget)
}
public fun CommonizerTarget(konanTarget: KonanTarget, vararg konanTargets: KonanTarget): SharedCommonizerTarget {
val targets = ArrayList<KonanTarget>(konanTargets.size + 1).apply {
add(konanTarget)
addAll(konanTargets)
}
return SharedCommonizerTarget(targets.map(::LeafCommonizerTarget).toSet())
}
public val CommonizerTarget.identityString: String
get() = when (this) {
is LeafCommonizerTarget -> name
is SharedCommonizerTarget -> identityString
}
private val SharedCommonizerTarget.identityString: String
get() {
val segments = targets.map(CommonizerTarget::identityString).sorted()
return segments.joinToString(
separator = ", ", prefix = "(", postfix = ")"
)
}
public val CommonizerTarget.prettyName: String
get() = when (this) {
is LeafCommonizerTarget -> "[$name]"
is SharedCommonizerTarget -> prettyName(null)
}
public fun SharedCommonizerTarget.prettyName(highlightedChild: CommonizerTarget?): String {
return targets
.sortedWith(compareBy<CommonizerTarget> { it.level }.thenBy { it.identityString }).joinToString(", ", "[", "]") { child ->
when (child) {
is LeafCommonizerTarget -> child.name
is SharedCommonizerTarget -> child.prettyName(highlightedChild)
} + if (child == highlightedChild) "(*)" else ""
}
}
public val CommonizerTarget.konanTargets: Set<KonanTarget>
get() {
return when (this) {
is LeafCommonizerTarget -> setOf(konanTarget)
is SharedCommonizerTarget -> targets.flatMap { it.konanTargets }.toSet()
}
}
public val CommonizerTarget.level: Int
get() {
return when (this) {
is LeafCommonizerTarget -> return 0
is SharedCommonizerTarget -> targets.maxOf { it.level } + 1
}
}
@@ -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.konan.library.KONAN_DISTRIBUTION_COMMON_LIBS_DIR
import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR
import java.io.File
public fun interface CommonizerOutputLayout {
public fun getTargetDirectory(root: File, target: CommonizerTarget): File
}
public object NativeDistributionCommonizerOutputLayout : CommonizerOutputLayout {
override fun getTargetDirectory(root: File, target: CommonizerTarget): File {
return when (target) {
is LeafCommonizerTarget -> root.resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR).resolve(target.name)
is SharedCommonizerTarget -> root.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR)
}
}
}
public object HierarchicalCommonizerOutputLayout : CommonizerOutputLayout {
override fun getTargetDirectory(root: File, target: CommonizerTarget): File {
return root.resolve(target.identityString)
}
}
@@ -0,0 +1,196 @@
/*
* 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.IdentityStringSyntaxNode.LeafTargetSyntaxNode
import org.jetbrains.kotlin.descriptors.commonizer.IdentityStringSyntaxNode.SharedTargetSyntaxNode
import org.jetbrains.kotlin.descriptors.commonizer.IdentityStringToken.*
public fun parseCommonizerTarget(identityString: String): CommonizerTarget {
try {
val tokens = tokenizeIdentityString(identityString)
val syntaxTree = parser(tokens) ?: error("Failed building syntax tree. $identityString")
check(syntaxTree.remaining.isEmpty()) { "Failed building syntax tree. Unexpected remaining tokens ${syntaxTree.remaining}" }
return buildCommonizerTarget(syntaxTree.value)
} catch (e: Throwable) {
throw IllegalArgumentException("Failed parsing CommonizerTarget from \"$identityString\"", e)
}
}
//region Tokens
private fun tokenizeIdentityString(identityString: String): List<IdentityStringToken> {
var remainingString = identityString
val tokenizer = sharedTargetStartTokenizer + sharedTargetEndTokenizer + separatorTokenizer + wordTokenizer
return mutableListOf<IdentityStringToken>().apply {
while (remainingString.isNotEmpty()) {
val generatedToken = tokenizer.nextToken(remainingString)
?: error("Unexpected token at $remainingString")
remainingString = generatedToken.remaining
add(generatedToken.token)
}
}.toList()
}
private sealed class IdentityStringToken {
data class Word(val value: String) : IdentityStringToken()
object Separator : IdentityStringToken()
object SharedTargetStart : IdentityStringToken()
object SharedTargetEnd : IdentityStringToken()
final override fun toString(): String {
return when (this) {
is Word -> value
is Separator -> ", "
is SharedTargetStart -> "["
is SharedTargetEnd -> "]"
}
}
}
private data class GeneratedToken(val token: IdentityStringToken, val remaining: String)
private interface IdentityStringTokenizer {
fun nextToken(value: String): GeneratedToken?
}
private operator fun IdentityStringTokenizer.plus(other: IdentityStringTokenizer): IdentityStringTokenizer {
return CompositeIdentityStringTokenizer(this, other)
}
private data class CompositeIdentityStringTokenizer(
val first: IdentityStringTokenizer,
val second: IdentityStringTokenizer
) : IdentityStringTokenizer {
override fun nextToken(value: String): GeneratedToken? {
return first.nextToken(value) ?: second.nextToken(value)
}
}
private data class RegexIdentityStringTokenizer(
val regex: Regex,
val token: (String) -> IdentityStringToken
) : IdentityStringTokenizer {
override fun nextToken(value: String): GeneratedToken? {
val firstMatchResult = regex.findAll(value, 0).firstOrNull() ?: return null
val range = firstMatchResult.range
if (range.first != 0) return null
return GeneratedToken(token(firstMatchResult.value), value.drop(firstMatchResult.value.length))
}
}
private val sharedTargetStartTokenizer =
RegexIdentityStringTokenizer(Regex.fromLiteral("(")) { SharedTargetStart }
private val sharedTargetEndTokenizer =
RegexIdentityStringTokenizer(Regex.fromLiteral(")")) { SharedTargetEnd }
private val separatorTokenizer =
RegexIdentityStringTokenizer(Regex("""\s*,\s*""")) { Separator }
private val wordTokenizer =
RegexIdentityStringTokenizer(Regex("\\w+"), IdentityStringToken::Word)
//endregion
//region Syntax Tree
private val parser = anyOf(SharedTargetParser, LeafTargetParser)
private data class ParserOutput<out T : Any>(val value: T, val remaining: List<IdentityStringToken>)
private interface Parser<out T : Any> {
operator fun invoke(tokens: List<IdentityStringToken>): ParserOutput<T>?
}
private fun <T : Any> anyOf(vararg parser: Parser<T>): Parser<T> {
return AnyOfParser(parser.toList())
}
private data class AnyOfParser<T : Any>(val parsers: List<Parser<T>>) : Parser<T> {
override fun invoke(tokens: List<IdentityStringToken>): ParserOutput<T>? {
return parsers.mapNotNull { parser -> parser(tokens) }.firstOrNull()
}
}
private fun <T : Any> Parser<T>.oneOrMore(): Parser<List<T>> {
return OneOrMoreParser(this)
}
private data class OneOrMoreParser<T : Any>(val parser: Parser<T>) : Parser<List<T>> {
override fun invoke(tokens: List<IdentityStringToken>): ParserOutput<List<T>>? {
val outputs = mutableListOf<T>()
var remainingTokens = tokens
while (true) {
val output = parser(remainingTokens) ?: break
if (output.remaining == remainingTokens) break
outputs.add(output.value)
remainingTokens = output.remaining
}
if (outputs.isEmpty()) {
return null
}
return ParserOutput(outputs.toList(), remainingTokens)
}
}
private fun <T : Any> Parser<T>.ignore(token: IdentityStringToken): Parser<T> {
return IgnoreTokensParser(this, token)
}
private data class IgnoreTokensParser<T : Any>(val parser: Parser<T>, val ignoredToken: IdentityStringToken) : Parser<T> {
override fun invoke(tokens: List<IdentityStringToken>): ParserOutput<T>? {
return parser(
if (tokens.firstOrNull() == ignoredToken) tokens.drop(1) else tokens
)
}
}
private object LeafTargetParser : Parser<LeafTargetSyntaxNode> {
override fun invoke(tokens: List<IdentityStringToken>): ParserOutput<LeafTargetSyntaxNode>? {
val nextToken = tokens.firstOrNull() as? Word ?: return null
return ParserOutput(LeafTargetSyntaxNode(nextToken), tokens.drop(1))
}
}
private object SharedTargetParser : Parser<SharedTargetSyntaxNode> {
override fun invoke(tokens: List<IdentityStringToken>): ParserOutput<SharedTargetSyntaxNode>? {
if (tokens.firstOrNull() !is SharedTargetStart) return null
val innerParser = anyOf(LeafTargetParser, SharedTargetParser).ignore(Separator).oneOrMore()
val innerParserOutput = innerParser(tokens.drop(1)) ?: return null
val closingToken = innerParserOutput.remaining.firstOrNull()
if (closingToken != SharedTargetEnd) {
error("Missing ']' at ${tokens.joinToString("")}")
}
return ParserOutput(SharedTargetSyntaxNode(innerParserOutput.value), innerParserOutput.remaining.drop(1))
}
}
private sealed class IdentityStringSyntaxNode {
data class LeafTargetSyntaxNode(val token: Word) : IdentityStringSyntaxNode()
data class SharedTargetSyntaxNode(val children: List<IdentityStringSyntaxNode>) : IdentityStringSyntaxNode()
}
//endregion Tree
//region Build CommonizerTarget
private fun buildCommonizerTarget(node: IdentityStringSyntaxNode): CommonizerTarget {
return when (node) {
is LeafTargetSyntaxNode -> LeafCommonizerTarget(node.token.value)
is SharedTargetSyntaxNode -> SharedCommonizerTarget(
node.children.map { child -> buildCommonizerTarget(child) }.toSet()
)
}
}
//endregion
@@ -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)
}
Binary file not shown.
Binary file not shown.