K/N: Get rid of remaining parts of "lite" KLIB API
Rewrite it in clean way and move to 'idea-gradle' module.
This commit is contained in:
@@ -610,7 +610,6 @@ tasks {
|
||||
register("konan-tests") {
|
||||
dependsOn("dist")
|
||||
dependsOn(
|
||||
":kotlin-native:kotlin-native-library-reader:test",
|
||||
":kotlin-native:commonizer:test"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.idea.configuration.klib
|
||||
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import java.io.File
|
||||
|
||||
sealed class KlibInfo(
|
||||
val path: File,
|
||||
val sourcePaths: Collection<File>,
|
||||
val name: String
|
||||
)
|
||||
|
||||
class NativeDistributionKlibInfo(
|
||||
path: File,
|
||||
sourcePaths: Collection<File>,
|
||||
name: String,
|
||||
val target: KonanTarget? // null means "common"
|
||||
) : KlibInfo(path, sourcePaths, name)
|
||||
|
||||
class NativeDistributionCommonizedKlibInfo(
|
||||
path: File,
|
||||
sourcePaths: Collection<File>,
|
||||
name: String,
|
||||
val ownTarget: KonanTarget?, // null means "common"
|
||||
val commonizedTargets: Set<KonanTarget>
|
||||
) : KlibInfo(path, sourcePaths, name)
|
||||
|
||||
// TODO: add other subclasses as necessary
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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.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.target.Distribution
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.konan.target.TargetSupportException
|
||||
import org.jetbrains.kotlin.library.KLIB_MANIFEST_FILE_NAME
|
||||
import org.jetbrains.kotlin.library.KLIB_PROPERTY_UNIQUE_NAME
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files.*
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
|
||||
class KlibInfoProvider(
|
||||
kotlinNativeHome: File,
|
||||
nativeDistributionCommonizedLibsDir: File? = null
|
||||
) {
|
||||
private enum class Origin {
|
||||
NATIVE_DISTRIBUTION,
|
||||
NATIVE_DISTRIBUTION_COMMONIZED,
|
||||
// TODO: add other origins as necessary
|
||||
}
|
||||
|
||||
private class ResultWrapper<T>(val result: T)
|
||||
|
||||
private val kotlinNativePath = kotlinNativeHome.toPath()
|
||||
private val nativeDistributionCommonizedLibsPath = nativeDistributionCommonizedLibsDir?.toPath()
|
||||
|
||||
private val hostManager by lazy {
|
||||
HostManager(
|
||||
distribution = Distribution(konanHomeOverride = kotlinNativeHome.path),
|
||||
experimental = true
|
||||
)
|
||||
}
|
||||
|
||||
private val nativeDistributionLibrarySourceFiles by lazy {
|
||||
kotlinNativeHome.resolve(KONAN_DISTRIBUTION_SOURCES_DIR)
|
||||
.takeIf { it.isDirectory }
|
||||
?.walkTopDown()
|
||||
?.maxDepth(1)
|
||||
?.filter { it.isFile && it.name.endsWith(".zip") }
|
||||
?.toList()
|
||||
?: emptyList()
|
||||
}
|
||||
|
||||
private val cachedProperties = mutableMapOf<Path, Properties>()
|
||||
|
||||
fun getKlibInfo(libraryFile: File): KlibInfo? {
|
||||
val libraryPath = libraryFile.toPath()
|
||||
|
||||
val origin = detectOrigin(libraryPath) ?: return null
|
||||
val manifest = loadProperties(libraryPath.resolve(KLIB_MANIFEST_FILE_NAME))
|
||||
|
||||
val name = manifest.getProperty(KLIB_PROPERTY_UNIQUE_NAME) ?: return null
|
||||
val target = (detectTarget(libraryPath) ?: return null).result
|
||||
|
||||
return when (origin) {
|
||||
NATIVE_DISTRIBUTION -> {
|
||||
val sourcePaths = if (target == null) getLibrarySourcesFromDistribution(name) else emptyList()
|
||||
NativeDistributionKlibInfo(libraryFile, sourcePaths, name, target)
|
||||
}
|
||||
|
||||
NATIVE_DISTRIBUTION_COMMONIZED -> {
|
||||
val commonizedTargets = (detectCommonizedTargets(libraryPath, target) ?: return null).result
|
||||
NativeDistributionCommonizedKlibInfo(libraryFile, emptyList(), name, target, commonizedTargets)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 loadProperties(propertiesPath: Path): Properties = cachedProperties.computeIfAbsent(propertiesPath) {
|
||||
val properties = Properties()
|
||||
|
||||
if (isRegularFile(propertiesPath)) {
|
||||
try {
|
||||
newInputStream(propertiesPath).use { properties.load(it) }
|
||||
} catch (_: IOException) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
properties
|
||||
}
|
||||
|
||||
private fun detectTarget(libraryPath: Path): ResultWrapper<KonanTarget?>? {
|
||||
if (libraryPath.nameCount < 3) return null
|
||||
|
||||
val parentDirName = libraryPath.getName(libraryPath.nameCount - 2).toString()
|
||||
if (parentDirName == KONAN_DISTRIBUTION_COMMON_LIBS_DIR)
|
||||
return ResultWrapper(/* common */ null)
|
||||
else {
|
||||
val target = parentDirName.safeToTarget() ?: return null
|
||||
val grandParentDirName = libraryPath.getName(libraryPath.nameCount - 3).toString()
|
||||
return if (grandParentDirName == KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR)
|
||||
ResultWrapper(target)
|
||||
else
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun detectCommonizedTargets(libraryPath: Path, ownTarget: KonanTarget?): ResultWrapper<Set<KonanTarget>>? {
|
||||
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 props = loadProperties(basePath.resolve(COMMONIZED_PROPERTIES_FILE_NAME))
|
||||
val rawTargets = props.getProperty(COMMONIZED_TARGETS)?.split(' ') ?: return null
|
||||
|
||||
val targets = rawTargets.mapNotNullTo(mutableSetOf()) { it.safeToTarget() }
|
||||
if (targets.isEmpty() || targets.size != rawTargets.size || ownTarget !in targets) return null
|
||||
|
||||
return ResultWrapper(targets)
|
||||
}
|
||||
|
||||
private fun getLibrarySourcesFromDistribution(libraryName: String): Collection<File> {
|
||||
val nameFilter: (String) -> Boolean = if (libraryName == KONAN_STDLIB_NAME) {
|
||||
// stdlib is a special case
|
||||
{ it.startsWith("kotlin-stdlib") || it.startsWith("kotlin-test") }
|
||||
} else {
|
||||
{ it.startsWith(libraryName) }
|
||||
}
|
||||
|
||||
return nativeDistributionLibrarySourceFiles.filter { nameFilter(it.name) }
|
||||
}
|
||||
|
||||
private fun String.safeToTarget(): KonanTarget? = try {
|
||||
hostManager.targetByName(this)
|
||||
} catch (_: TargetSupportException) {
|
||||
null
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val COMMONIZED_PROPERTIES_FILE_NAME = ".commonized"
|
||||
const val COMMONIZED_TARGETS = "targets"
|
||||
}
|
||||
}
|
||||
+28
-15
@@ -8,7 +8,10 @@ package org.jetbrains.kotlin.idea.configuration
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.externalSystem.model.DataNode
|
||||
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
||||
import com.intellij.openapi.externalSystem.model.project.*
|
||||
import com.intellij.openapi.externalSystem.model.project.LibraryData
|
||||
import com.intellij.openapi.externalSystem.model.project.LibraryDependencyData
|
||||
import com.intellij.openapi.externalSystem.model.project.LibraryLevel
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectData
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeUIModifiableModelsProvider
|
||||
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
|
||||
@@ -25,15 +28,12 @@ import org.jetbrains.kotlin.gradle.KotlinMPPGradleModel
|
||||
import org.jetbrains.kotlin.idea.configuration.DependencySubstitute.NoSubstitute
|
||||
import org.jetbrains.kotlin.idea.configuration.DependencySubstitute.YesSubstitute
|
||||
import org.jetbrains.kotlin.idea.configuration.KotlinNativeLibraryNameUtil.buildIDELibraryName
|
||||
import org.jetbrains.kotlin.idea.configuration.klib.KlibInfoProvider
|
||||
import org.jetbrains.kotlin.idea.configuration.klib.NativeDistributionKlibInfo
|
||||
import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
|
||||
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
|
||||
import org.jetbrains.kotlin.konan.library.lite.LiteKonanLibraryFacade
|
||||
import org.jetbrains.plugins.gradle.ExternalDependencyId
|
||||
import org.jetbrains.plugins.gradle.model.DefaultExternalMultiLibraryDependency
|
||||
import org.jetbrains.plugins.gradle.model.ExternalDependency
|
||||
import org.jetbrains.plugins.gradle.model.ExternalLibraryDependency
|
||||
import org.jetbrains.plugins.gradle.model.ExternalMultiLibraryDependency
|
||||
import org.jetbrains.plugins.gradle.model.FileCollectionDependency
|
||||
import org.jetbrains.plugins.gradle.model.*
|
||||
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
|
||||
import org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil
|
||||
import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext
|
||||
@@ -110,9 +110,22 @@ internal class KotlinNativeLibrariesDependencySubstitutor(
|
||||
private val ProjectResolverContext.dependencySubstitutionCache
|
||||
get() = getUserData(KLIB_DEPENDENCY_SUBSTITUTION_CACHE) ?: putUserDataIfAbsent(KLIB_DEPENDENCY_SUBSTITUTION_CACHE, HashMap())
|
||||
|
||||
private val libraryProvider = LiteKonanLibraryFacade.getDistributionLibraryProvider(
|
||||
mppModel.kotlinNativeHome.takeIf { it != KotlinMPPGradleModel.NO_KOTLIN_NATIVE_HOME }?.let { File(it) }
|
||||
)
|
||||
private val klibInfoProvider: KlibInfoProvider? by lazy {
|
||||
val kotlinNativeHome = mppModel.kotlinNativeHome.takeIf { it != KotlinMPPGradleModel.NO_KOTLIN_NATIVE_HOME }?.let(::File)
|
||||
|
||||
if (kotlinNativeHome == null) {
|
||||
LOG.warn(
|
||||
"""
|
||||
Can't obtain Kotlin/Native home path in Kotlin Gradle plugin.
|
||||
${KotlinMPPGradleModel::class.java.simpleName} is $mppModel.
|
||||
${KotlinNativeLibrariesDependencySubstitutor::class.java.simpleName} will run in idle mode. No dependencies will be substituted.
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
null
|
||||
} else
|
||||
KlibInfoProvider(kotlinNativeHome = kotlinNativeHome)
|
||||
}
|
||||
|
||||
private val kotlinVersion: String? by lazy {
|
||||
// first, try to figure out Kotlin plugin version by classpath (the default approach)
|
||||
@@ -159,17 +172,17 @@ internal class KotlinNativeLibrariesDependencySubstitutor(
|
||||
private fun buildSubstituteIfNecessary(libraryFile: File): DependencySubstitute {
|
||||
// need to check whether `library` points to a real KLIB,
|
||||
// and if answer is yes then build a new dependency that will substitute original one
|
||||
val library = libraryProvider.getLibrary(libraryFile) ?: return NoSubstitute
|
||||
val klib = klibInfoProvider?.getKlibInfo(libraryFile) as? NativeDistributionKlibInfo ?: return NoSubstitute
|
||||
val nonNullKotlinVersion = kotlinVersion ?: return NoSubstitute
|
||||
|
||||
val newLibraryName = buildIDELibraryName(nonNullKotlinVersion, library.name, library.platform)
|
||||
val newLibraryName = buildIDELibraryName(nonNullKotlinVersion, klib.name, klib.target?.name)
|
||||
|
||||
val substitute = DefaultExternalMultiLibraryDependency().apply {
|
||||
classpathOrder = if (library.name == KONAN_STDLIB_NAME) -1 else 0 // keep stdlib upper
|
||||
classpathOrder = if (klib.name == KONAN_STDLIB_NAME) -1 else 0 // keep stdlib upper
|
||||
name = newLibraryName
|
||||
packaging = DEFAULT_PACKAGING
|
||||
files += library.path
|
||||
sources += library.sourcePaths
|
||||
files += klib.path
|
||||
sources += klib.sourcePaths
|
||||
scope = DependencyScope.PROVIDED.name
|
||||
}
|
||||
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.idea.configuration.klib
|
||||
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils.getHomeDirectory
|
||||
import java.io.File
|
||||
|
||||
class KlibInfoProviderTest : TestCase() {
|
||||
|
||||
fun testKlibsFromNativeDistributionRecognized() {
|
||||
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 }
|
||||
.associateBy { it.path.relativeTo(testDataDir) }
|
||||
|
||||
assertEquals(expectedKlibsFromDistribution.keys, actualKlibs.keys)
|
||||
for (klibPath in actualKlibs.keys) {
|
||||
val actualKlib = actualKlibs.getValue(klibPath)
|
||||
val expectedKlib = expectedKlibsFromDistribution.getValue(klibPath)
|
||||
|
||||
assertEquals(expectedKlib.path, actualKlib.path)
|
||||
assertEquals(expectedKlib.name, actualKlib.name)
|
||||
|
||||
val actualSources = actualKlib.sourcePaths.map { it.relativeTo(sourcesDir) }.toSet()
|
||||
val expectedSources = expectedKlib.sourcePaths.map { it.relativeTo(sourcesDir) }.toSet()
|
||||
|
||||
assertEquals(expectedSources, actualSources)
|
||||
assertEquals(expectedKlib.target, actualKlib.target)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val testDataDir = File(getHomeDirectory() + "/idea/testData/configuration/klib")
|
||||
.also { assertTrue("Test data directory does not exist: $it", it.isDirectory) }
|
||||
|
||||
private val externalLibsDir = testDataDir.resolve("external-libs")
|
||||
private val kotlinNativeHome = testDataDir.resolve("kotlin-native-PLATFORM-VERSION")
|
||||
private val sourcesDir = kotlinNativeHome.resolve("sources")
|
||||
|
||||
private val expectedKlibsFromDistribution = listOf(
|
||||
NativeDistributionKlibInfo(
|
||||
path = kotlinNativeHome.resolve("klib", "common", "stdlib"),
|
||||
sourcePaths = listOf(
|
||||
sourcesDir.resolve("kotlin-stdlib-native-sources.zip"),
|
||||
sourcesDir.resolve("kotlin-test-anotations-common-sources.zip")
|
||||
),
|
||||
name = "stdlib",
|
||||
target = null
|
||||
),
|
||||
NativeDistributionKlibInfo(
|
||||
path = kotlinNativeHome.resolve("klib", "common", "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(testDataDir) }
|
||||
|
||||
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)
|
||||
for (other in others)
|
||||
temp = temp.resolve(other)
|
||||
|
||||
return temp
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
unique_name=kotlinx-cli
|
||||
BIN
Binary file not shown.
+1
@@ -0,0 +1 @@
|
||||
// this file is empty
|
||||
+1
@@ -0,0 +1 @@
|
||||
// this file is empty
|
||||
BIN
Binary file not shown.
+1
@@ -0,0 +1 @@
|
||||
// this file is empty
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -13,13 +13,11 @@ dependencies {
|
||||
compile(project(":kotlin-util-io"))
|
||||
compile(project(":kotlin-util-klib"))
|
||||
compile(project(":kotlin-util-klib-metadata"))
|
||||
|
||||
testCompile(commonDep("junit:junit"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { projectDefault() }
|
||||
"test" { none() }
|
||||
}
|
||||
|
||||
standardPublicJars()
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.konan.library.lite
|
||||
|
||||
import java.io.File
|
||||
|
||||
interface LiteKonanLibrary {
|
||||
val path: File
|
||||
val sourcePaths: Collection<File>
|
||||
val name: String
|
||||
val platform: String?
|
||||
}
|
||||
-134
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.konan.library.lite
|
||||
|
||||
import org.jetbrains.kotlin.konan.library.*
|
||||
import org.jetbrains.kotlin.library.*
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
|
||||
// TODO: agree how to distinguish between Native and non-Native (ex: JS) KLIBs
|
||||
object LiteKonanLibraryFacade {
|
||||
/**
|
||||
* Use this [LiteKonanLibraryProvider] to get information about any Kotlin/Native library.
|
||||
*/
|
||||
fun getLibraryProvider(): LiteKonanLibraryProvider = DefaultLiteKonanLibraryProvider
|
||||
|
||||
/**
|
||||
* Use this [LiteKonanLibraryProvider] to get information about libraries inside of Kotlin/Native distribution.
|
||||
*/
|
||||
fun getDistributionLibraryProvider(customKonanHomeDir: File?): LiteKonanLibraryProvider =
|
||||
FromDistributionLiteKonanLibraryProvider(customKonanHomeDir)
|
||||
}
|
||||
|
||||
interface LiteKonanLibraryProvider {
|
||||
/**
|
||||
* Returns either [LiteKonanLibrary], or null if path does not point to a valid Kotlin/Native library
|
||||
* or the current [LiteKonanLibraryProvider] does not accept the library due to internal restrictions.
|
||||
*/
|
||||
fun getLibrary(libraryPath: File): LiteKonanLibrary?
|
||||
}
|
||||
|
||||
private object DefaultLiteKonanLibraryProvider : LiteKonanLibraryProvider {
|
||||
override fun getLibrary(libraryPath: File): LiteKonanLibraryImpl? {
|
||||
val manifest = loadManifest(libraryPath) ?: return null
|
||||
|
||||
val name = manifest.getProperty(KLIB_PROPERTY_UNIQUE_NAME) ?: return null
|
||||
|
||||
return LiteKonanLibraryImpl(
|
||||
path = libraryPath,
|
||||
name = name
|
||||
)
|
||||
}
|
||||
|
||||
private fun loadManifest(libraryPath: File): Properties? {
|
||||
val manifestFile = libraryPath.resolve(KLIB_MANIFEST_FILE_NAME).takeIf { it.isFile } ?: return null
|
||||
|
||||
return try {
|
||||
manifestFile.inputStream().use { Properties().apply { load(it) } }
|
||||
} catch (_: IOException) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FromDistributionLiteKonanLibraryProvider(customKonanHomeDir: File?) : LiteKonanLibraryProvider {
|
||||
private val konanHomeDir: Path? by lazy {
|
||||
customKonanHomeDir?.takeIf {
|
||||
// small sanity check to ensure that it's a valid Kotlin/Native home directory
|
||||
customKonanHomeDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR).isDirectory
|
||||
}?.toPath()
|
||||
}
|
||||
|
||||
private val konanDataDir: Path by lazy {
|
||||
val path = System.getenv("KONAN_DATA_DIR")?.let { Paths.get(it) }
|
||||
?: Paths.get(System.getProperty("user.home"), ".konan")
|
||||
path.toAbsolutePath()
|
||||
}
|
||||
|
||||
override fun getLibrary(libraryPath: File): LiteKonanLibrary? {
|
||||
// check whether it under Kotlin/Native root
|
||||
if (!isUnderKonanRoot(libraryPath)) return null
|
||||
|
||||
val library = DefaultLiteKonanLibraryProvider.getLibrary(libraryPath) ?: return null
|
||||
|
||||
val (platform: String?, dataDir: File) = getPlatformAndDataDir(libraryPath) ?: return null
|
||||
library.platform = platform
|
||||
|
||||
if (library.name == KONAN_STDLIB_NAME)
|
||||
library.sourcePaths = getStdlibSources(dataDir)
|
||||
|
||||
return library
|
||||
}
|
||||
|
||||
private fun isUnderKonanRoot(libraryPath: File): Boolean {
|
||||
with(libraryPath.toPath()) {
|
||||
if (konanHomeDir != null && startsWith(konanHomeDir))
|
||||
return true
|
||||
|
||||
return startsWith(konanDataDir)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPlatformAndDataDir(libraryPath: File): Pair<String?, File>? {
|
||||
val parentDir = libraryPath.parentFile ?: return null
|
||||
val parentDirName = parentDir.name
|
||||
|
||||
fun getDataDir(platformDir: File): File = platformDir.parentFile.parentFile
|
||||
|
||||
return when (parentDirName) {
|
||||
KONAN_DISTRIBUTION_COMMON_LIBS_DIR -> null to getDataDir(parentDir)
|
||||
else -> {
|
||||
val grandParentDir = parentDir.parentFile ?: return null
|
||||
when {
|
||||
grandParentDir.name == KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR -> parentDirName to getDataDir(grandParentDir)
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStdlibSources(dataDir: File): Collection<File> {
|
||||
val sourcesDir = dataDir.resolve(KONAN_DISTRIBUTION_SOURCES_DIR).takeIf { it.isDirectory } ?: return emptyList()
|
||||
|
||||
return sourcesDir.walkTopDown().maxDepth(1)
|
||||
.filter { it.isFile }
|
||||
.filter {
|
||||
with(it.name) { endsWith(".zip") && (startsWith("kotlin-stdlib") || startsWith("kotlin-test")) }
|
||||
}.toList()
|
||||
}
|
||||
}
|
||||
|
||||
internal class LiteKonanLibraryImpl(
|
||||
override val path: File,
|
||||
override val name: String
|
||||
) : LiteKonanLibrary {
|
||||
override var platform: String? = null
|
||||
override var sourcePaths: Collection<File> = emptyList()
|
||||
}
|
||||
-123
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.konan.library.lite
|
||||
|
||||
import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_KLIB_DIR
|
||||
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_STDLIB_NAME
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.io.FileFilter
|
||||
import java.nio.file.Files
|
||||
import java.util.zip.ZipException
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
class LiteKonanLibraryFacadeTests {
|
||||
|
||||
@Test
|
||||
fun allLibrariesRecognized() {
|
||||
val libraryProvider = LiteKonanLibraryFacade.getLibraryProvider()
|
||||
|
||||
val actualLibraries = collectLibrariesFromLocalFS(libraryProvider)
|
||||
val expectedLibraries = librariesExpectedInDistribution() + librariesExpectedInExternalDir()
|
||||
|
||||
compareLibraries(
|
||||
expectedLibraries,
|
||||
actualLibraries,
|
||||
/* `LiteKonanLibraryFacade.getLibraryProvider()` is currently platform-agnostic */ true
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onlyFromDistributionLibrariesRecognized() {
|
||||
val libraryProvider = LiteKonanLibraryFacade.getDistributionLibraryProvider(konanHomeDir)
|
||||
|
||||
val actualLibraries = collectLibrariesFromLocalFS(libraryProvider)
|
||||
val expectedLibraries = librariesExpectedInDistribution()
|
||||
|
||||
compareLibraries(expectedLibraries, actualLibraries, false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sourcesAttachedToStdlib() {
|
||||
val libraryProvider = LiteKonanLibraryFacade.getDistributionLibraryProvider(konanHomeDir)
|
||||
|
||||
val stdlib = collectLibrariesFromLocalFS(libraryProvider).values.firstOrNull { it.name == KONAN_STDLIB_NAME }
|
||||
?: error("Could not load Kotlin/Native $KONAN_STDLIB_NAME from $konanHomeDir")
|
||||
|
||||
assertTrue("Kotlin/Native $KONAN_STDLIB_NAME sources must not be empty", stdlib.sourcePaths.isNotEmpty())
|
||||
|
||||
assertTrue(
|
||||
"Each path in $KONAN_STDLIB_NAME.sourcePaths must be either non-empty directory or ZIP (JAR) file",
|
||||
stdlib.sourcePaths.all { sourcePath ->
|
||||
when {
|
||||
sourcePath.isDirectory -> Files.newDirectoryStream(sourcePath.toPath()).use { it.any { true } }
|
||||
sourcePath.isFile -> try {
|
||||
ZipFile(sourcePath).use { it.name != null }
|
||||
} catch (e: ZipException) {
|
||||
false
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
private fun getPotentialLibraryPathsOnLocalFS(): List<File> {
|
||||
val roots = mutableListOf(externalLibsDir)
|
||||
|
||||
roots += klibDir.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR)
|
||||
roots += klibDir.resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR).listFiles(FileFilter { it.isDirectory })?.toList() ?: emptyList()
|
||||
|
||||
return roots.flatMap { it.listFiles()?.toList() ?: emptyList() }
|
||||
}
|
||||
|
||||
private fun collectLibrariesFromLocalFS(libraryProvider: LiteKonanLibraryProvider): Map<File, LiteKonanLibrary> =
|
||||
getPotentialLibraryPathsOnLocalFS().mapNotNull { libraryProvider.getLibrary(it) }.toLibraryMap()
|
||||
|
||||
private fun librariesExpectedInDistribution(): Map<File, FakeLibraryForTest> = listOf(
|
||||
FakeLibraryForTest(klibDir.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR, KONAN_STDLIB_NAME)),
|
||||
FakeLibraryForTest(klibDir.resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR, "macos_x64", "foo"), platform = "macos_x64"),
|
||||
FakeLibraryForTest(klibDir.resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR, "macos_x64", "bar"), platform = "macos_x64"),
|
||||
FakeLibraryForTest(klibDir.resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR, "macos_x64", "baz"), platform = "macos_x64")
|
||||
).toLibraryMap()
|
||||
|
||||
private fun librariesExpectedInExternalDir(): Map<File, FakeLibraryForTest> = listOf(
|
||||
FakeLibraryForTest(externalLibsDir.resolve("correct"))
|
||||
).toLibraryMap()
|
||||
|
||||
private fun compareLibraries(expected: Map<File, FakeLibraryForTest>, actual: Map<File, LiteKonanLibrary>, ignorePlatform: Boolean) {
|
||||
assertEquals("Libraries paths mismatch", expected.keys, actual.keys)
|
||||
|
||||
for (key in expected.keys) {
|
||||
val expectedLibrary = expected.getValue(key)
|
||||
val actualLibrary = actual.getValue(key)
|
||||
|
||||
assertEquals("Library path mismatch", expectedLibrary.path, actualLibrary.path)
|
||||
|
||||
if (!ignorePlatform) {
|
||||
assertEquals("Library platform mismatch, path=${expectedLibrary.path}", expectedLibrary.platform, actualLibrary.platform)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val klibDir = konanHomeDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR)
|
||||
val externalLibsDir = testDataDir.resolve("external-libs")
|
||||
|
||||
fun <T : LiteKonanLibrary> List<T>.toLibraryMap() = map { it.path to it }.toMap()
|
||||
}
|
||||
}
|
||||
|
||||
private class FakeLibraryForTest(
|
||||
override val path: File,
|
||||
override val platform: String? = null
|
||||
) : LiteKonanLibrary {
|
||||
override val sourcePaths: Collection<File> = emptyList()
|
||||
override val name: String = path.name
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.konan.library.lite
|
||||
|
||||
import java.io.File
|
||||
|
||||
internal val testDataDir = File(System.getProperty("user.dir") ?: ".").canonicalFile.resolve("testData")
|
||||
|
||||
internal val konanHomeDir = testDataDir.resolve("kotlin-native-data-dir", "kotlin-native-PLATFORM-VERSION")
|
||||
|
||||
internal fun File.resolve(relative: String, next: String) = resolve(relative).resolve(next)
|
||||
|
||||
internal fun File.resolve(relative: String, next: String, another: String) = resolve(relative, next).resolve(another)
|
||||
Reference in New Issue
Block a user