[Commonizer] Properly detect commonized KLIBs in IDE

This commit is contained in:
Dmitriy Dolovov
2020-02-11 13:04:30 +07:00
parent 51af1c13b1
commit a5e9e09755
61 changed files with 189 additions and 67 deletions
@@ -5,11 +5,8 @@
package org.jetbrains.kotlin.idea.configuration.klib
import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_COMMON_LIBS_DIR
import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR
import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_SOURCES_DIR
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
import org.jetbrains.kotlin.idea.configuration.klib.KlibInfoProvider.Origin.*
import org.jetbrains.kotlin.konan.library.*
import org.jetbrains.kotlin.konan.target.Distribution
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
@@ -22,10 +19,7 @@ import java.nio.file.Files.*
import java.nio.file.Path
import java.util.*
class KlibInfoProvider(
kotlinNativeHome: File,
nativeDistributionCommonizedLibsDir: File? = null
) {
class KlibInfoProvider(kotlinNativeHome: File) {
private enum class Origin {
NATIVE_DISTRIBUTION,
NATIVE_DISTRIBUTION_COMMONIZED,
@@ -34,8 +28,7 @@ class KlibInfoProvider(
private class ResultWrapper<T>(val result: T)
private val kotlinNativePath = kotlinNativeHome.toPath()
private val nativeDistributionCommonizedLibsPath = nativeDistributionCommonizedLibsDir?.toPath()
private val nativeDistributionLibrariesPath = kotlinNativeHome.resolve(KONAN_DISTRIBUTION_KLIB_DIR).toPath()
private val hostManager by lazy {
HostManager(
@@ -80,11 +73,15 @@ class KlibInfoProvider(
}
}
private fun detectOrigin(libraryPath: Path): Origin? = when {
libraryPath.startsWith(kotlinNativePath) -> NATIVE_DISTRIBUTION
nativeDistributionCommonizedLibsPath?.let { libraryPath.startsWith(it) } == true -> NATIVE_DISTRIBUTION_COMMONIZED
else -> null
}
private fun detectOrigin(libraryPath: Path): Origin? =
if (!libraryPath.startsWith(nativeDistributionLibrariesPath)) {
null
} else {
if (libraryPath.getNameOrNull(nativeDistributionLibrariesPath.nameCount) == KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR)
NATIVE_DISTRIBUTION_COMMONIZED
else
NATIVE_DISTRIBUTION
}
private fun findManifestFile(libraryPath: Path): Path? {
libraryPath.resolve(KLIB_MANIFEST_FILE_NAME).let { propertiesPath ->
@@ -131,7 +128,7 @@ class KlibInfoProvider(
if (parentDirName == KONAN_DISTRIBUTION_COMMON_LIBS_DIR)
return ResultWrapper(/* common */ null)
else {
val target = parentDirName.safeToTarget() ?: return null
val target = parentDirName.toTargetOrNull() ?: return null
val grandParentDirName = libraryPath.getName(libraryPath.nameCount - 3).toString()
return if (grandParentDirName == KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR)
ResultWrapper(target)
@@ -144,13 +141,17 @@ class KlibInfoProvider(
if (libraryPath.nameCount < 4) return null
val additionalOffset = if (ownTarget == null) /* common */ 0 else 1
val basePath = libraryPath.subpath(0, libraryPath.nameCount - 2 - additionalOffset)
val commonizedLibsDirName = libraryPath.getName(libraryPath.nameCount - 3 - additionalOffset).toString()
val props = loadProperties(basePath.resolve(COMMONIZED_PROPERTIES_FILE_NAME))
val rawTargets = props.getProperty(COMMONIZED_TARGETS)?.split(' ') ?: return null
val rawTargets = commonizedLibsDirName.split('-').dropLast(1)
val targets = rawTargets.mapNotNullTo(mutableSetOf()) { it.toTargetOrNull() }
val targets = rawTargets.mapNotNullTo(mutableSetOf()) { it.safeToTarget() }
if (targets.isEmpty() || targets.size != rawTargets.size || ownTarget !in targets) return null
if (targets.isEmpty()
|| targets.size != rawTargets.size
|| (ownTarget != null && ownTarget !in targets)
) {
return null
}
return ResultWrapper(targets)
}
@@ -166,14 +167,12 @@ class KlibInfoProvider(
return nativeDistributionLibrarySourceFiles.filter { nameFilter(it.name) }
}
private fun String.safeToTarget(): KonanTarget? = try {
private fun String.toTargetOrNull(): KonanTarget? = try {
hostManager.targetByName(this)
} catch (_: TargetSupportException) {
null
}
private companion object {
const val COMMONIZED_PROPERTIES_FILE_NAME = ".commonized"
const val COMMONIZED_TARGETS = "targets"
}
private fun Path.getNameOrNull(index: Int): String? =
if (index < 0 || index >= nameCount) null else getName(index).toString()
}
@@ -7,43 +7,71 @@ package org.jetbrains.kotlin.idea.configuration.klib
import com.intellij.testFramework.PlatformTestUtil.getTestName
import junit.framework.TestCase
import org.jetbrains.kotlin.konan.library.*
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.test.KotlinTestUtils.getHomeDirectory
import java.io.File
class KlibInfoProviderTest : TestCase() {
fun testOldStyleKlibsFromNativeDistributionRecognized() = doTest()
fun testOldStyleKlibsFromNativeDistributionRecognized() = doTest(
::generateExpectedKlibsFromDistribution
)
fun testKlibsFromNativeDistributionWithSingleComponentRecognized() = doTest()
fun testKlibsFromNativeDistributionWithSingleComponentRecognized() = doTest(
::generateExpectedKlibsFromDistribution
)
fun testKlibsFromNativeDistributionWithMultipleComponentsRecognized() = doTest()
fun testKlibsFromNativeDistributionWithMultipleComponentsRecognized() = doTest(
::generateExpectedKlibsFromDistribution
)
private fun doTest() {
fun testCommonizedKlibsFromNativeDistributionRecognized() = doTest(
::generateExpectedKlibsFromDistribution,
::generateExpectedCommonizedKlibsFromDistribution
)
private fun doTest(vararg expectedKlibsGenerators: (kotlinNativeHome: File) -> Map<File, KlibInfo>) {
val kotlinNativeHome = testDataDir.resolve(getTestName(name, true)).resolve("kotlin-native-PLATFORM-VERSION")
val sourcesDir = kotlinNativeHome.resolve("sources")
val sourcesDir = kotlinNativeHome.resolve(KONAN_DISTRIBUTION_SOURCES_DIR)
val klibProvider = KlibInfoProvider(kotlinNativeHome = kotlinNativeHome)
val potentialKlibPaths = mutableListOf<File>()
potentialKlibPaths += externalLibsDir.children()
potentialKlibPaths += kotlinNativeHome.resolve("klib", "common").children()
potentialKlibPaths += kotlinNativeHome.resolve("klib", "platform", "macos_x64").children()
val actualKlibs = potentialKlibPaths
.mapNotNull { klibProvider.getKlibInfo(it) as? NativeDistributionKlibInfo }
with(kotlinNativeHome.resolve(KONAN_DISTRIBUTION_KLIB_DIR)) {
potentialKlibPaths += resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR).children()
potentialKlibPaths += resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR, "macos_x64").children()
with(resolve(KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR, "ios_arm64-ios_x64-discriminator")) {
potentialKlibPaths += resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR).children()
potentialKlibPaths += resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR, "ios_arm64").children()
potentialKlibPaths += resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR, "ios_x64").children()
}
with(resolve(KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR, "ios_arm32-ios_arm64-ios_x64-discriminator")) {
potentialKlibPaths += resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR).children()
potentialKlibPaths += resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR, "ios_arm32").children()
potentialKlibPaths += resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR, "ios_arm64").children()
potentialKlibPaths += resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR, "ios_x64").children()
}
}
val actualKlibs = potentialKlibPaths.mapNotNull { klibProvider.getKlibInfo(it) }
.associateBy { it.path.relativeTo(kotlinNativeHome) }
val expectedKlibsFromDistribution = generateExpectedKlibsFromDistribution(
kotlinNativeHome = kotlinNativeHome,
sourcesDir = sourcesDir
)
val expectedKlibsFromDistribution = mutableMapOf<File, KlibInfo>().apply {
expectedKlibsGenerators.forEach { this += it(kotlinNativeHome) }
}
assertEquals(expectedKlibsFromDistribution.keys, actualKlibs.keys)
for (klibPath in actualKlibs.keys) {
val actualKlib = actualKlibs.getValue(klibPath)
val expectedKlib = expectedKlibsFromDistribution.getValue(klibPath)
assertEquals(expectedKlib::class.java, actualKlib::class.java)
assertEquals(expectedKlib.path, actualKlib.path)
assertEquals(expectedKlib.name, actualKlib.name)
@@ -51,48 +79,98 @@ class KlibInfoProviderTest : TestCase() {
val expectedSources = expectedKlib.sourcePaths.map { it.relativeTo(sourcesDir) }.toSet()
assertEquals(expectedSources, actualSources)
assertEquals(expectedKlib.target, actualKlib.target)
if (expectedKlib is NativeDistributionKlibInfo) {
require(actualKlib is NativeDistributionKlibInfo)
assertEquals(expectedKlib.target, actualKlib.target)
} else if (expectedKlib is NativeDistributionCommonizedKlibInfo) {
require(actualKlib is NativeDistributionCommonizedKlibInfo)
assertEquals(expectedKlib.ownTarget, actualKlib.ownTarget)
assertEquals(expectedKlib.commonizedTargets, actualKlib.commonizedTargets)
}
}
}
private fun generateExpectedKlibsFromDistribution(kotlinNativeHome: File, sourcesDir: File) = listOf(
NativeDistributionKlibInfo(
path = kotlinNativeHome.resolve("klib", "common", "stdlib"),
private fun generateExpectedKlibsFromDistribution(kotlinNativeHome: File): Map<File, NativeDistributionKlibInfo> {
val sourcesDir = kotlinNativeHome.resolve(KONAN_DISTRIBUTION_SOURCES_DIR)
val basePath = kotlinNativeHome.resolve(KONAN_DISTRIBUTION_KLIB_DIR)
val result = mutableListOf<NativeDistributionKlibInfo>()
result += NativeDistributionKlibInfo(
path = basePath.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR, KONAN_STDLIB_NAME),
sourcePaths = listOf(
sourcesDir.resolve("kotlin-stdlib-native-sources.zip"),
sourcesDir.resolve("kotlin-test-anotations-common-sources.zip")
),
name = "stdlib",
name = KONAN_STDLIB_NAME,
target = null
),
NativeDistributionKlibInfo(
path = kotlinNativeHome.resolve("klib", "common", "kotlinx-cli"),
)
result += NativeDistributionKlibInfo(
path = basePath.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR, "kotlinx-cli"),
sourcePaths = listOf(
sourcesDir.resolve("kotlinx-cli-common-sources.zip"),
sourcesDir.resolve("kotlinx-cli-native-sources.zip")
),
name = "kotlinx-cli",
target = null
),
NativeDistributionKlibInfo(
path = kotlinNativeHome.resolve("klib", "platform", "macos_x64", "foo"),
sourcePaths = emptyList(),
name = "foo",
target = KonanTarget.MACOS_X64
),
NativeDistributionKlibInfo(
path = kotlinNativeHome.resolve("klib", "platform", "macos_x64", "bar"),
sourcePaths = emptyList(),
name = "bar",
target = KonanTarget.MACOS_X64
),
NativeDistributionKlibInfo(
path = kotlinNativeHome.resolve("klib", "platform", "macos_x64", "baz"),
sourcePaths = emptyList(),
name = "baz",
target = KonanTarget.MACOS_X64
)
).associateBy { it.path.relativeTo(kotlinNativeHome) }
result += listOf("foo", "bar", "baz").map { name ->
NativeDistributionKlibInfo(
path = basePath.resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR, "macos_x64", name),
sourcePaths = emptyList(),
name = name,
target = KonanTarget.MACOS_X64
)
}
return result.associateBy { it.path.relativeTo(kotlinNativeHome) }
}
private fun generateExpectedCommonizedKlibsFromDistribution(kotlinNativeHome: File): Map<File, NativeDistributionCommonizedKlibInfo> {
val basePath = kotlinNativeHome.resolve(KONAN_DISTRIBUTION_KLIB_DIR, KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR)
fun generateCommonizedKlibsForDir(commonizedLibsDirName: String): Map<File, NativeDistributionCommonizedKlibInfo> {
val rawTargets = commonizedLibsDirName.split('-').dropLast(1)
val targets = rawTargets.map {
when (it) {
"ios_x64" -> KonanTarget.IOS_X64
"ios_arm32" -> KonanTarget.IOS_ARM32
"ios_arm64" -> KonanTarget.IOS_ARM64
else -> error("Unexpected target: $it")
}
}.toSet()
val result = mutableListOf<NativeDistributionCommonizedKlibInfo>()
with(basePath.resolve(commonizedLibsDirName)) {
val libraryDirsToTargets: Map<File, KonanTarget?> =
targets.associateBy { resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR, it.name) } +
(resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR) to null)
libraryDirsToTargets.forEach { (libraryDir, target) ->
result += listOf("foo", "bar", "baz").map { name ->
NativeDistributionCommonizedKlibInfo(
path = libraryDir.resolve(name),
sourcePaths = emptyList(),
name = name,
ownTarget = target,
commonizedTargets = targets
)
}
}
}
return result.associateBy { it.path.relativeTo(kotlinNativeHome) }
}
return generateCommonizedKlibsForDir("ios_arm64-ios_x64-discriminator") +
generateCommonizedKlibsForDir("ios_arm32-ios_arm64-ios_x64-discriminator")
}
companion object {
private val testDataDir = File(getHomeDirectory() + "/idea/testData/configuration/klib")
@@ -101,7 +179,6 @@ class KlibInfoProviderTest : TestCase() {
private val externalLibsDir = testDataDir.resolve("external-libs")
private fun File.children(): List<File> = (listFiles()?.toList() ?: emptyList())
.also { assertTrue("$this does not have children files or directories", it.isNotEmpty()) }
private fun File.resolve(relative: String, next: String, vararg others: String): File {
var temp = resolve(relative).resolve(next)