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") {
|
register("konan-tests") {
|
||||||
dependsOn("dist")
|
dependsOn("dist")
|
||||||
dependsOn(
|
dependsOn(
|
||||||
":kotlin-native:kotlin-native-library-reader:test",
|
|
||||||
":kotlin-native:commonizer: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.diagnostic.Logger
|
||||||
import com.intellij.openapi.externalSystem.model.DataNode
|
import com.intellij.openapi.externalSystem.model.DataNode
|
||||||
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
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.IdeModifiableModelsProvider
|
||||||
import com.intellij.openapi.externalSystem.service.project.IdeUIModifiableModelsProvider
|
import com.intellij.openapi.externalSystem.service.project.IdeUIModifiableModelsProvider
|
||||||
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
|
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.NoSubstitute
|
||||||
import org.jetbrains.kotlin.idea.configuration.DependencySubstitute.YesSubstitute
|
import org.jetbrains.kotlin.idea.configuration.DependencySubstitute.YesSubstitute
|
||||||
import org.jetbrains.kotlin.idea.configuration.KotlinNativeLibraryNameUtil.buildIDELibraryName
|
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.idea.inspections.gradle.findKotlinPluginVersion
|
||||||
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
|
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.ExternalDependencyId
|
||||||
import org.jetbrains.plugins.gradle.model.DefaultExternalMultiLibraryDependency
|
import org.jetbrains.plugins.gradle.model.*
|
||||||
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.data.GradleSourceSetData
|
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
|
||||||
import org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil
|
import org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil
|
||||||
import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext
|
import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext
|
||||||
@@ -110,9 +110,22 @@ internal class KotlinNativeLibrariesDependencySubstitutor(
|
|||||||
private val ProjectResolverContext.dependencySubstitutionCache
|
private val ProjectResolverContext.dependencySubstitutionCache
|
||||||
get() = getUserData(KLIB_DEPENDENCY_SUBSTITUTION_CACHE) ?: putUserDataIfAbsent(KLIB_DEPENDENCY_SUBSTITUTION_CACHE, HashMap())
|
get() = getUserData(KLIB_DEPENDENCY_SUBSTITUTION_CACHE) ?: putUserDataIfAbsent(KLIB_DEPENDENCY_SUBSTITUTION_CACHE, HashMap())
|
||||||
|
|
||||||
private val libraryProvider = LiteKonanLibraryFacade.getDistributionLibraryProvider(
|
private val klibInfoProvider: KlibInfoProvider? by lazy {
|
||||||
mppModel.kotlinNativeHome.takeIf { it != KotlinMPPGradleModel.NO_KOTLIN_NATIVE_HOME }?.let { File(it) }
|
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 {
|
private val kotlinVersion: String? by lazy {
|
||||||
// first, try to figure out Kotlin plugin version by classpath (the default approach)
|
// 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 {
|
private fun buildSubstituteIfNecessary(libraryFile: File): DependencySubstitute {
|
||||||
// need to check whether `library` points to a real KLIB,
|
// 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
|
// 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 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 {
|
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
|
name = newLibraryName
|
||||||
packaging = DEFAULT_PACKAGING
|
packaging = DEFAULT_PACKAGING
|
||||||
files += library.path
|
files += klib.path
|
||||||
sources += library.sourcePaths
|
sources += klib.sourcePaths
|
||||||
scope = DependencyScope.PROVIDED.name
|
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-io"))
|
||||||
compile(project(":kotlin-util-klib"))
|
compile(project(":kotlin-util-klib"))
|
||||||
compile(project(":kotlin-util-klib-metadata"))
|
compile(project(":kotlin-util-klib-metadata"))
|
||||||
|
|
||||||
testCompile(commonDep("junit:junit"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
"main" { projectDefault() }
|
"main" { projectDefault() }
|
||||||
"test" { projectDefault() }
|
"test" { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
standardPublicJars()
|
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