Refactor "lite" API for Kotlin/Native libraries
This commit is contained in:
+12
-10
@@ -29,7 +29,7 @@ import org.jetbrains.kotlin.idea.configuration.DependencySubstitute.NoSubstitute
|
||||
import org.jetbrains.kotlin.idea.configuration.DependencySubstitute.YesSubstitute
|
||||
import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
|
||||
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
|
||||
import org.jetbrains.kotlin.konan.library.lite.LiteKonanLibraryInfoProvider
|
||||
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
|
||||
@@ -86,7 +86,7 @@ class KotlinNativeLibraryDataService : AbstractProjectDataService<LibraryData, L
|
||||
|
||||
// KT-29613, KT-29783
|
||||
internal class KotlinNativeLibrariesDependencySubstitutor(
|
||||
private val mppModel: KotlinMPPGradleModel,
|
||||
mppModel: KotlinMPPGradleModel,
|
||||
private val gradleModule: IdeaModule,
|
||||
private val resolverCtx: ProjectResolverContext
|
||||
) {
|
||||
@@ -112,7 +112,9 @@ internal class KotlinNativeLibrariesDependencySubstitutor(
|
||||
private val ProjectResolverContext.dependencySubstitutionCache
|
||||
get() = getUserData(KLIB_DEPENDENCY_SUBSTITUTION_CACHE) ?: putUserDataIfAbsent(KLIB_DEPENDENCY_SUBSTITUTION_CACHE, HashMap())
|
||||
|
||||
private val libraryInfoProvider by lazy { LiteKonanLibraryInfoProvider(mppModel.kotlinNativeHome) }
|
||||
private val libraryProvider = LiteKonanLibraryFacade.getDistributionLibraryProvider(
|
||||
mppModel.kotlinNativeHome.takeIf { it != KotlinMPPGradleModel.NO_KOTLIN_NATIVE_HOME }?.let { File(it) }
|
||||
)
|
||||
|
||||
private val kotlinVersion: String? by lazy {
|
||||
val classpathData = buildClasspathData(gradleModule, resolverCtx)
|
||||
@@ -150,20 +152,20 @@ internal class KotlinNativeLibrariesDependencySubstitutor(
|
||||
}
|
||||
|
||||
private fun buildSubstituteIfNecessary(libraryFile: File): DependencySubstitute {
|
||||
// need to check whether `libraryFile` 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
|
||||
val libraryInfo = libraryInfoProvider.getDistributionLibraryInfo(libraryFile) ?: return NoSubstitute
|
||||
val library = libraryProvider.getLibrary(libraryFile) ?: return NoSubstitute
|
||||
val nonNullKotlinVersion = kotlinVersion ?: return NoSubstitute
|
||||
|
||||
val platformNamePart = libraryInfo.platform?.let { " [$it]" }.orEmpty()
|
||||
val newLibraryName = "$KOTLIN_NATIVE_LIBRARY_PREFIX_PLUS_SPACE$nonNullKotlinVersion - ${libraryInfo.name}$platformNamePart"
|
||||
val platformNamePart = library.platform?.let { " [$it]" }.orEmpty()
|
||||
val newLibraryName = "$KOTLIN_NATIVE_LIBRARY_PREFIX_PLUS_SPACE$nonNullKotlinVersion - ${library.name}$platformNamePart"
|
||||
|
||||
val substitute = DefaultExternalMultiLibraryDependency().apply {
|
||||
classpathOrder = if (libraryInfo.name == KONAN_STDLIB_NAME) -1 else 0 // keep stdlib upper
|
||||
classpathOrder = if (library.name == KONAN_STDLIB_NAME) -1 else 0 // keep stdlib upper
|
||||
name = newLibraryName
|
||||
packaging = DEFAULT_PACKAGING
|
||||
files += libraryInfo.path
|
||||
sources += libraryInfo.sourcePaths
|
||||
files += library.path
|
||||
sources += library.sourcePaths
|
||||
scope = DependencyScope.PROVIDED.name
|
||||
}
|
||||
|
||||
|
||||
+7
-4
@@ -5,8 +5,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.konan.library.lite
|
||||
|
||||
data class LiteKonanDistribution(
|
||||
val distributionHome: String,
|
||||
val kotlinNativeVersion: KotlinVersion,
|
||||
val kotlinNativeVersionString: String
|
||||
import org.jetbrains.kotlin.konan.KonanVersion
|
||||
import java.io.File
|
||||
|
||||
class LiteKonanDistribution(
|
||||
val distributionHome: File,
|
||||
val konanVersion: KonanVersion,
|
||||
val konanVersionString: String
|
||||
)
|
||||
|
||||
-37
@@ -1,37 +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_STDLIB_NAME
|
||||
import org.jetbrains.kotlin.konan.library.konanCommonLibraryPath
|
||||
import java.nio.file.Paths
|
||||
|
||||
class LiteKonanDistributionInfoProvider(private val konanHomeDir: String) {
|
||||
|
||||
fun getDistributionInfo(): LiteKonanDistribution? {
|
||||
val stdlibInfo = LiteKonanLibraryInfoProvider(konanHomeDir).getDistributionLibraryInfo(
|
||||
Paths.get(konanHomeDir).resolve(konanCommonLibraryPath(KONAN_STDLIB_NAME)).toFile()
|
||||
) ?: return null
|
||||
|
||||
val versionString = stdlibInfo.compilerVersion
|
||||
val version = versionString.substringBefore('-')
|
||||
.split('.')
|
||||
.let {
|
||||
when (it.size) {
|
||||
2 -> listOf(it[0], it[1], "0")
|
||||
3 -> it
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
?.mapNotNull { it.toIntOrNull() }
|
||||
?.takeIf { it.size == 3 }
|
||||
?.let {
|
||||
KotlinVersion(it[0], it[1], it[2])
|
||||
} ?: return null
|
||||
|
||||
return LiteKonanDistribution(konanHomeDir, version, versionString)
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.KonanVersion
|
||||
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
|
||||
import org.jetbrains.kotlin.konan.library.konanCommonLibraryPath
|
||||
import java.io.File
|
||||
|
||||
object LiteKonanDistributionProvider {
|
||||
fun getDistribution(konanHomeDir: File): LiteKonanDistribution? {
|
||||
val stdlib = LiteKonanLibraryFacade.getDistributionLibraryProvider(konanHomeDir)
|
||||
.getLibrary(konanHomeDir.resolve(konanCommonLibraryPath(KONAN_STDLIB_NAME).toFile())) as? LiteKonanLibraryImpl ?: return null
|
||||
|
||||
return LiteKonanDistribution(
|
||||
konanHomeDir,
|
||||
KonanVersion.fromString(stdlib.compilerVersion),
|
||||
stdlib.compilerVersion
|
||||
)
|
||||
}
|
||||
}
|
||||
+6
-7
@@ -7,10 +7,9 @@ package org.jetbrains.kotlin.konan.library.lite
|
||||
|
||||
import java.io.File
|
||||
|
||||
data class LiteKonanLibrary(
|
||||
val path: File,
|
||||
val sourcePaths: Collection<File>,
|
||||
val name: String,
|
||||
val platform: String?,
|
||||
internal val compilerVersion: String
|
||||
)
|
||||
interface LiteKonanLibrary {
|
||||
val path: File
|
||||
val sourcePaths: Collection<File>
|
||||
val name: String
|
||||
val platform: String?
|
||||
}
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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 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
|
||||
val compilerVersion = manifest.getProperty(KLIB_PROPERTY_COMPILER_VERSION) ?: return null
|
||||
|
||||
return LiteKonanLibraryImpl(
|
||||
path = libraryPath,
|
||||
name = name,
|
||||
compilerVersion = compilerVersion
|
||||
)
|
||||
}
|
||||
|
||||
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(KLIB_DIR_NAME).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_COMMON_LIBS_DIR_NAME -> null to getDataDir(parentDir)
|
||||
else -> {
|
||||
val grandParentDir = parentDir.parentFile ?: return null
|
||||
when {
|
||||
grandParentDir.name == KONAN_PLATFORM_LIBS_DIR_NAME -> parentDirName to getDataDir(grandParentDir)
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStdlibSources(dataDir: File): Collection<File> {
|
||||
val sourcesDir = dataDir.resolve(KONAN_SOURCES_DIR_NAME).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,
|
||||
internal val compilerVersion: String
|
||||
) : LiteKonanLibrary {
|
||||
override var platform: String? = null
|
||||
override var sourcePaths: Collection<File> = emptyList()
|
||||
}
|
||||
-104
@@ -1,104 +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.KLIB_DIR_NAME
|
||||
import org.jetbrains.kotlin.konan.library.KLIB_MANIFEST_FILE_NAME
|
||||
import org.jetbrains.kotlin.konan.library.KLIB_PROPERTY_COMPILER_VERSION
|
||||
import org.jetbrains.kotlin.konan.library.KLIB_PROPERTY_UNIQUE_NAME
|
||||
import org.jetbrains.kotlin.konan.library.KONAN_COMMON_LIBS_DIR_NAME
|
||||
import org.jetbrains.kotlin.konan.library.KONAN_PLATFORM_LIBS_DIR_NAME
|
||||
import org.jetbrains.kotlin.konan.library.KONAN_SOURCES_DIR_NAME
|
||||
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
|
||||
class LiteKonanLibraryInfoProvider(customKonanHomeDir: String? = null) {
|
||||
|
||||
private val konanHomeDir by lazy {
|
||||
customKonanHomeDir
|
||||
?.takeIf { it.isNotEmpty() }
|
||||
?.let { Paths.get(it) }
|
||||
?.takeIf {
|
||||
// small sanity check to ensure that it's a valid Kotlin/Native home directory
|
||||
Files.isDirectory(it.resolve(KLIB_DIR_NAME))
|
||||
}
|
||||
}
|
||||
|
||||
private val konanDataDir by lazy {
|
||||
val path = System.getenv("KONAN_DATA_DIR")?.let { Paths.get(it) }
|
||||
?: Paths.get(System.getProperty("user.home"), ".konan")
|
||||
path.toAbsolutePath()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns either [LiteKonanLibrary], or null if there is no such library in
|
||||
* Kotlin/Native distribution.
|
||||
*/
|
||||
fun getDistributionLibraryInfo(libraryPath: File): LiteKonanLibrary? {
|
||||
// check whether it under Kotlin/Native root
|
||||
if (!isUnderKonanRoot(libraryPath))
|
||||
return null
|
||||
|
||||
val manifestFile = libraryPath.resolve(KLIB_MANIFEST_FILE_NAME).takeIf { it.isFile } ?: return null
|
||||
val (platform: String?, dataDir: File) = getPlatformAndDataDir(libraryPath) ?: return null
|
||||
|
||||
val manifestProperties = Properties().apply {
|
||||
try {
|
||||
manifestFile.inputStream().use { load(it) }
|
||||
} catch (e: IOException) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
val name = manifestProperties[KLIB_PROPERTY_UNIQUE_NAME]?.toString() ?: return null
|
||||
val compilerVersion = manifestProperties[KLIB_PROPERTY_COMPILER_VERSION]?.toString() ?: return null
|
||||
|
||||
val sourcePaths = if (name == KONAN_STDLIB_NAME) getStdlibSources(dataDir) else emptyList()
|
||||
|
||||
return LiteKonanLibrary(libraryPath, sourcePaths, name, platform, compilerVersion)
|
||||
}
|
||||
|
||||
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_COMMON_LIBS_DIR_NAME -> null to getDataDir(parentDir)
|
||||
else -> {
|
||||
val grandParentDir = parentDir.parentFile ?: return null
|
||||
when {
|
||||
grandParentDir.name == KONAN_PLATFORM_LIBS_DIR_NAME -> parentDirName to getDataDir(grandParentDir)
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStdlibSources(dataDir: File): Collection<File> {
|
||||
val sourcesDir = dataDir.resolve(KONAN_SOURCES_DIR_NAME).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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user