[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 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.idea.configuration.klib.KlibInfoProvider.Origin.*
import org.jetbrains.kotlin.konan.library.*
import org.jetbrains.kotlin.konan.target.Distribution import org.jetbrains.kotlin.konan.target.Distribution
import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
@@ -22,10 +19,7 @@ import java.nio.file.Files.*
import java.nio.file.Path import java.nio.file.Path
import java.util.* import java.util.*
class KlibInfoProvider( class KlibInfoProvider(kotlinNativeHome: File) {
kotlinNativeHome: File,
nativeDistributionCommonizedLibsDir: File? = null
) {
private enum class Origin { private enum class Origin {
NATIVE_DISTRIBUTION, NATIVE_DISTRIBUTION,
NATIVE_DISTRIBUTION_COMMONIZED, NATIVE_DISTRIBUTION_COMMONIZED,
@@ -34,8 +28,7 @@ class KlibInfoProvider(
private class ResultWrapper<T>(val result: T) private class ResultWrapper<T>(val result: T)
private val kotlinNativePath = kotlinNativeHome.toPath() private val nativeDistributionLibrariesPath = kotlinNativeHome.resolve(KONAN_DISTRIBUTION_KLIB_DIR).toPath()
private val nativeDistributionCommonizedLibsPath = nativeDistributionCommonizedLibsDir?.toPath()
private val hostManager by lazy { private val hostManager by lazy {
HostManager( HostManager(
@@ -80,11 +73,15 @@ class KlibInfoProvider(
} }
} }
private fun detectOrigin(libraryPath: Path): Origin? = when { private fun detectOrigin(libraryPath: Path): Origin? =
libraryPath.startsWith(kotlinNativePath) -> NATIVE_DISTRIBUTION if (!libraryPath.startsWith(nativeDistributionLibrariesPath)) {
nativeDistributionCommonizedLibsPath?.let { libraryPath.startsWith(it) } == true -> NATIVE_DISTRIBUTION_COMMONIZED null
else -> null } else {
} if (libraryPath.getNameOrNull(nativeDistributionLibrariesPath.nameCount) == KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR)
NATIVE_DISTRIBUTION_COMMONIZED
else
NATIVE_DISTRIBUTION
}
private fun findManifestFile(libraryPath: Path): Path? { private fun findManifestFile(libraryPath: Path): Path? {
libraryPath.resolve(KLIB_MANIFEST_FILE_NAME).let { propertiesPath -> libraryPath.resolve(KLIB_MANIFEST_FILE_NAME).let { propertiesPath ->
@@ -131,7 +128,7 @@ class KlibInfoProvider(
if (parentDirName == KONAN_DISTRIBUTION_COMMON_LIBS_DIR) if (parentDirName == KONAN_DISTRIBUTION_COMMON_LIBS_DIR)
return ResultWrapper(/* common */ null) return ResultWrapper(/* common */ null)
else { else {
val target = parentDirName.safeToTarget() ?: return null val target = parentDirName.toTargetOrNull() ?: return null
val grandParentDirName = libraryPath.getName(libraryPath.nameCount - 3).toString() val grandParentDirName = libraryPath.getName(libraryPath.nameCount - 3).toString()
return if (grandParentDirName == KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR) return if (grandParentDirName == KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR)
ResultWrapper(target) ResultWrapper(target)
@@ -144,13 +141,17 @@ class KlibInfoProvider(
if (libraryPath.nameCount < 4) return null if (libraryPath.nameCount < 4) return null
val additionalOffset = if (ownTarget == null) /* common */ 0 else 1 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 = commonizedLibsDirName.split('-').dropLast(1)
val rawTargets = props.getProperty(COMMONIZED_TARGETS)?.split(' ') ?: return null val targets = rawTargets.mapNotNullTo(mutableSetOf()) { it.toTargetOrNull() }
val targets = rawTargets.mapNotNullTo(mutableSetOf()) { it.safeToTarget() } if (targets.isEmpty()
if (targets.isEmpty() || targets.size != rawTargets.size || ownTarget !in targets) return null || targets.size != rawTargets.size
|| (ownTarget != null && ownTarget !in targets)
) {
return null
}
return ResultWrapper(targets) return ResultWrapper(targets)
} }
@@ -166,14 +167,12 @@ class KlibInfoProvider(
return nativeDistributionLibrarySourceFiles.filter { nameFilter(it.name) } return nativeDistributionLibrarySourceFiles.filter { nameFilter(it.name) }
} }
private fun String.safeToTarget(): KonanTarget? = try { private fun String.toTargetOrNull(): KonanTarget? = try {
hostManager.targetByName(this) hostManager.targetByName(this)
} catch (_: TargetSupportException) { } catch (_: TargetSupportException) {
null null
} }
private companion object { private fun Path.getNameOrNull(index: Int): String? =
const val COMMONIZED_PROPERTIES_FILE_NAME = ".commonized" if (index < 0 || index >= nameCount) null else getName(index).toString()
const val COMMONIZED_TARGETS = "targets"
}
} }
@@ -7,43 +7,71 @@ package org.jetbrains.kotlin.idea.configuration.klib
import com.intellij.testFramework.PlatformTestUtil.getTestName import com.intellij.testFramework.PlatformTestUtil.getTestName
import junit.framework.TestCase import junit.framework.TestCase
import org.jetbrains.kotlin.konan.library.*
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.test.KotlinTestUtils.getHomeDirectory import org.jetbrains.kotlin.test.KotlinTestUtils.getHomeDirectory
import java.io.File import java.io.File
class KlibInfoProviderTest : TestCase() { 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 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 klibProvider = KlibInfoProvider(kotlinNativeHome = kotlinNativeHome)
val potentialKlibPaths = mutableListOf<File>() val potentialKlibPaths = mutableListOf<File>()
potentialKlibPaths += externalLibsDir.children() potentialKlibPaths += externalLibsDir.children()
potentialKlibPaths += kotlinNativeHome.resolve("klib", "common").children()
potentialKlibPaths += kotlinNativeHome.resolve("klib", "platform", "macos_x64").children()
val actualKlibs = potentialKlibPaths with(kotlinNativeHome.resolve(KONAN_DISTRIBUTION_KLIB_DIR)) {
.mapNotNull { klibProvider.getKlibInfo(it) as? NativeDistributionKlibInfo } 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) } .associateBy { it.path.relativeTo(kotlinNativeHome) }
val expectedKlibsFromDistribution = generateExpectedKlibsFromDistribution( val expectedKlibsFromDistribution = mutableMapOf<File, KlibInfo>().apply {
kotlinNativeHome = kotlinNativeHome, expectedKlibsGenerators.forEach { this += it(kotlinNativeHome) }
sourcesDir = sourcesDir }
)
assertEquals(expectedKlibsFromDistribution.keys, actualKlibs.keys) assertEquals(expectedKlibsFromDistribution.keys, actualKlibs.keys)
for (klibPath in actualKlibs.keys) { for (klibPath in actualKlibs.keys) {
val actualKlib = actualKlibs.getValue(klibPath) val actualKlib = actualKlibs.getValue(klibPath)
val expectedKlib = expectedKlibsFromDistribution.getValue(klibPath) val expectedKlib = expectedKlibsFromDistribution.getValue(klibPath)
assertEquals(expectedKlib::class.java, actualKlib::class.java)
assertEquals(expectedKlib.path, actualKlib.path) assertEquals(expectedKlib.path, actualKlib.path)
assertEquals(expectedKlib.name, actualKlib.name) assertEquals(expectedKlib.name, actualKlib.name)
@@ -51,48 +79,98 @@ class KlibInfoProviderTest : TestCase() {
val expectedSources = expectedKlib.sourcePaths.map { it.relativeTo(sourcesDir) }.toSet() val expectedSources = expectedKlib.sourcePaths.map { it.relativeTo(sourcesDir) }.toSet()
assertEquals(expectedSources, actualSources) 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( private fun generateExpectedKlibsFromDistribution(kotlinNativeHome: File): Map<File, NativeDistributionKlibInfo> {
NativeDistributionKlibInfo( val sourcesDir = kotlinNativeHome.resolve(KONAN_DISTRIBUTION_SOURCES_DIR)
path = kotlinNativeHome.resolve("klib", "common", "stdlib"), 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( sourcePaths = listOf(
sourcesDir.resolve("kotlin-stdlib-native-sources.zip"), sourcesDir.resolve("kotlin-stdlib-native-sources.zip"),
sourcesDir.resolve("kotlin-test-anotations-common-sources.zip") sourcesDir.resolve("kotlin-test-anotations-common-sources.zip")
), ),
name = "stdlib", name = KONAN_STDLIB_NAME,
target = null target = null
), )
NativeDistributionKlibInfo(
path = kotlinNativeHome.resolve("klib", "common", "kotlinx-cli"), result += NativeDistributionKlibInfo(
path = basePath.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR, "kotlinx-cli"),
sourcePaths = listOf( sourcePaths = listOf(
sourcesDir.resolve("kotlinx-cli-common-sources.zip"), sourcesDir.resolve("kotlinx-cli-common-sources.zip"),
sourcesDir.resolve("kotlinx-cli-native-sources.zip") sourcesDir.resolve("kotlinx-cli-native-sources.zip")
), ),
name = "kotlinx-cli", name = "kotlinx-cli",
target = null 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 { companion object {
private val testDataDir = File(getHomeDirectory() + "/idea/testData/configuration/klib") private val testDataDir = File(getHomeDirectory() + "/idea/testData/configuration/klib")
@@ -101,7 +179,6 @@ class KlibInfoProviderTest : TestCase() {
private val externalLibsDir = testDataDir.resolve("external-libs") private val externalLibsDir = testDataDir.resolve("external-libs")
private fun File.children(): List<File> = (listFiles()?.toList() ?: emptyList()) 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 { private fun File.resolve(relative: String, next: String, vararg others: String): File {
var temp = resolve(relative).resolve(next) var temp = resolve(relative).resolve(next)