User friendly presentation of KLIBs in IDEA

Adds friendly names for KLIBs imported as file dependencies from Gradle project.

Issue #KT-29613 Fixed
Issue #KT-29783 Fixed
This commit is contained in:
Dmitriy Dolovov
2019-01-31 02:21:25 +07:00
parent 06b5a42d0d
commit 75250cf1a0
42 changed files with 657 additions and 21 deletions
+1
View File
@@ -534,6 +534,7 @@ tasks {
create("idea-plugin-additional-tests") {
dependsOn("dist")
dependsOn(":idea:idea-gradle:test",
":idea:idea-gradle-native:test",
":idea:idea-maven:test",
":j2k:test",
":eval4j:test")
+43 -3
View File
@@ -4,6 +4,8 @@ plugins {
}
dependencies {
testRuntime(intellijDep())
compile(project(":kotlin-native:kotlin-native-library-reader"))
compileOnly(project(":idea:idea-gradle"))
@@ -19,15 +21,53 @@ dependencies {
compile(project(":js:js.frontend"))
compileOnly(intellijDep())
testCompile(projectTests(":idea"))
testCompile(projectTests(":idea:idea-test-framework"))
testCompile(projectTests(":idea:idea-gradle"))
compileOnly(intellijPluginDep("gradle"))
compileOnly(intellijPluginDep("Groovy"))
compileOnly(intellijPluginDep("junit"))
compileOnly(intellijDep())
testCompile(intellijPluginDep("gradle"))
testCompileOnly(intellijPluginDep("Groovy"))
testCompileOnly(intellijDep())
testRuntime(project(":kotlin-reflect"))
testRuntime(project(":idea:idea-jvm"))
testRuntime(project(":idea:idea-android"))
testRuntime(project(":plugins:kapt3-idea"))
testRuntime(project(":plugins:android-extensions-ide"))
testRuntime(project(":plugins:lint"))
testRuntime(project(":sam-with-receiver-ide-plugin"))
testRuntime(project(":allopen-ide-plugin"))
testRuntime(project(":noarg-ide-plugin"))
testRuntime(project(":kotlin-scripting-idea"))
testRuntime(project(":kotlinx-serialization-ide-plugin"))
// TODO: the order of the plugins matters here, consider avoiding order-dependency
testRuntime(intellijPluginDep("junit"))
testRuntime(intellijPluginDep("testng"))
testRuntime(intellijPluginDep("properties"))
testRuntime(intellijPluginDep("gradle"))
testRuntime(intellijPluginDep("Groovy"))
testRuntime(intellijPluginDep("coverage"))
if (Ide.IJ()) {
testRuntime(intellijPluginDep("maven"))
}
testRuntime(intellijPluginDep("android"))
testRuntime(intellijPluginDep("smali"))
}
sourceSets {
"main" { projectDefault() }
"test" { none() }
"test" { projectDefault() }
}
testsJar()
projectTest {
workingDir = rootDir
useAndroidSdk()
}
configureFormInstrumentation()
@@ -0,0 +1,169 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.ide.konan.gradle
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.LibraryOrderEntry
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.vfs.VfsUtilCore.urlToPath
import com.intellij.util.io.readText
import org.jetbrains.kotlin.ide.konan.NativeLibraryKind
import org.jetbrains.kotlin.idea.codeInsight.gradle.GradleImportingTestCase
import org.jetbrains.kotlin.idea.configuration.externalCompilerVersion
import org.jetbrains.kotlin.idea.framework.detectLibraryKind
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.idea.util.projectStructure.allModules
import org.jetbrains.kotlin.konan.library.konanCommonLibraryPath
import org.jetbrains.kotlin.konan.library.konanPlatformLibraryPath
import org.jetbrains.kotlin.platform.impl.isKotlinNative
import org.jetbrains.plugins.gradle.util.GradleConstants
import org.junit.Assert.*
import org.junit.Test
import org.junit.runners.Parameterized
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
class GradleNativeLibrariesInIDENamingTest : GradleImportingTestCase() {
// Test naming of Kotlin/Native libraries in projects with Gradle plugin 1.3.21+
@Test
fun testLibrariesNaming() {
configureProject()
importProject()
val projectRoot = Paths.get(projectPath)
myProject.allModules().forEach { assertValidModule(it, projectRoot) }
}
// Test naming of Kotlin/Native libraries in projects with Gradle plugin 1.3.20 or earlier
@Test
fun testLegacyLibrariesNaming() {
configureProject()
importProject()
val projectRoot = Paths.get(projectPath)
myProject.allModules().forEach { assertValidModule(it, projectRoot) }
}
override fun getExternalSystemConfigFileName() = GradleConstants.KOTLIN_DSL_SCRIPT_NAME
override fun testDataDirName() = "nativeLibraries"
private fun configureProject() {
configureByFiles()
// include data dir with fake Kotlin/Native libraries
val testSuiteDataDir = testDataDirectory().toPath().parent
val kotlinNativeHome = testSuiteDataDir.resolve(FAKE_KOTLIN_NATIVE_HOME_RELATIVE_PATH)
Files.walk(kotlinNativeHome)
.filter { Files.isRegularFile(it) }
.forEach { pathInTestSuite ->
// need to put copied file one directory upper than the project root, so adding ".." to the beginning of relative path
// reason: distribution KLIBs should not be appear in IDEA indexes, so they should be located outside of the project root
val relativePathInProject = DOUBLE_DOT_PATH.resolve(pathInTestSuite.toRelativePath(testSuiteDataDir))
createProjectSubFile(relativePathInProject.toString(), pathInTestSuite.readText())
}
}
companion object {
@Parameterized.Parameters(name = "{index}: with Gradle-{0}")
@Throws(Throwable::class)
@JvmStatic
fun data() = listOf(arrayOf("4.10.2"))
}
}
private val FAKE_KOTLIN_NATIVE_HOME_RELATIVE_PATH = Paths.get("kotlin-native-data-dir", "kotlin-native-PLATFORM-VERSION")
private val NATIVE_LIBRARY_NAME_REGEX = Regex("^Kotlin/Native ([\\d\\w\\.-]+) - ([\\w\\d]+)( \\[([\\w\\d_]+)\\])?$")
private val NATIVE_LINUX_LIBRARIES = listOf(
"Kotlin/Native {version} - stdlib",
"Kotlin/Native {version} - linux [linux_x64]",
"Kotlin/Native {version} - posix [linux_x64]",
"Kotlin/Native {version} - zlib [linux_x64]"
)
private val NATIVE_MACOS_LIBRARIES = listOf(
"Kotlin/Native {version} - stdlib",
"Kotlin/Native {version} - Foundation [macos_x64]",
"Kotlin/Native {version} - UIKit [macos_x64]",
"Kotlin/Native {version} - objc [macos_x64]"
)
private val NATIVE_MINGW_LIBRARIES = listOf(
"Kotlin/Native {version} - stdlib",
"Kotlin/Native {version} - iconv [mingw_x64]",
"Kotlin/Native {version} - opengl32 [mingw_x64]",
"Kotlin/Native {version} - windows [mingw_x64]"
)
private val DOUBLE_DOT_PATH = Paths.get("..")
private fun Path.toRelativePath(basePath: Path) = basePath.relativize(this)
private val Module.libraries
get() = ModuleRootManager.getInstance(this).orderEntries
.asSequence()
.filterIsInstance<LibraryOrderEntry>()
.mapNotNull { it.library }
private fun assertValidModule(module: Module, projectRoot: Path) {
val (nativeLibraries, otherLibraries) = module.libraries.partition { library ->
detectLibraryKind(library.getFiles(OrderRootType.CLASSES)) == NativeLibraryKind
}
if (module.platform.isKotlinNative) {
assertFalse("No Kotlin/Native libraries in $module", nativeLibraries.isEmpty())
nativeLibraries.forEach { assertValidNativeLibrary(it, projectRoot) }
val kotlinVersion = requireNotNull(module.externalCompilerVersion) {
"External compiler version shoul not be null"
}
val expectedNativeLibraryNames = when {
module.name.contains("linux", ignoreCase = true) -> NATIVE_LINUX_LIBRARIES
module.name.contains("macos", ignoreCase = true) -> NATIVE_MACOS_LIBRARIES
module.name.contains("mingw", ignoreCase = true) -> NATIVE_MINGW_LIBRARIES
else -> emptyList()
}.map { it.replace("{version}", kotlinVersion) }.sorted()
val actualNativeLibraryNames = nativeLibraries.map { it.name.orEmpty() }.sorted()
assertEquals("Different set of Kotlin/Native libraries in $module", expectedNativeLibraryNames, actualNativeLibraryNames)
} else {
assertTrue("Unexpected Kotlin/Native libraries in $module: $nativeLibraries", nativeLibraries.isEmpty())
}
otherLibraries.forEach(::assertValidNonNativeLibrary)
}
private fun assertValidNativeLibrary(library: Library, projectRoot: Path) {
val fullName = library.name.orEmpty()
val result = NATIVE_LIBRARY_NAME_REGEX.matchEntire(fullName)
assertTrue("Invalid Kotlin/Native library name: $fullName", result?.groups?.size == 5)
val (_, name, _, platform) = result!!.destructured
val libraryUrls = library.getUrls(OrderRootType.CLASSES).toList()
assertTrue("Kotlin/Native library $fullName has multiple roots (${libraryUrls.size}): $libraryUrls", libraryUrls.size == 1)
val actualShortPath = Paths.get(urlToPath(libraryUrls.single()))
.toRelativePath(projectRoot.resolve(DOUBLE_DOT_PATH).normalize())
.toRelativePath(FAKE_KOTLIN_NATIVE_HOME_RELATIVE_PATH)
val expectedShortPath = if (platform.isEmpty()) konanCommonLibraryPath(name) else konanPlatformLibraryPath(name, platform)
assertEquals("The short path of $fullName does not match its location", expectedShortPath, actualShortPath)
}
private fun assertValidNonNativeLibrary(library: Library) {
val name = library.name.orEmpty()
assertFalse("Invalid non-native library name: $name", name.contains("Kotlin/Native"))
}
@@ -64,13 +64,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
override fun populateModuleExtraModels(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>) {
if (ExternalSystemApiUtil.find(ideModule, BuildScriptClasspathData.KEY) == null) {
val buildScriptClasspathModel = resolverCtx.getExtraProject(gradleModule, BuildScriptClasspathModel::class.java)
val classpathEntries = buildScriptClasspathModel?.classpath?.map {
BuildScriptClasspathData.ClasspathEntry(it.classes, it.sources, it.javadoc)
} ?: emptyList()
val buildScriptClasspathData = BuildScriptClasspathData(GradleConstants.SYSTEM_ID, classpathEntries).also {
it.gradleHomeDir = buildScriptClasspathModel?.gradleHomeDir
}
val buildScriptClasspathData = buildClasspathData(gradleModule, resolverCtx)
ideModule.createChild(BuildScriptClasspathData.KEY, buildScriptClasspathData)
}
@@ -347,17 +341,20 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
val mppModel = resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java) ?: return
val sourceSetMap = ideProject.getUserData(GradleProjectResolver.RESOLVED_SOURCE_SETS) ?: return
val artifactsMap = ideProject.getUserData(CONFIGURATION_ARTIFACTS) ?: return
val substitutor = KotlinNativeLibrariesDependencySubstitutor(mppModel, gradleModule, resolverCtx)
val processedModuleIds = HashSet<String>()
processCompilations(gradleModule, mppModel, ideModule, resolverCtx) { dataNode, compilation ->
if (processedModuleIds.add(getKotlinModuleId(gradleModule, compilation, resolverCtx))) {
val substitutedDependencies = substitutor.substituteDependencies(compilation.dependencies)
buildDependencies(
resolverCtx,
sourceSetMap,
artifactsMap,
dataNode,
preprocessDependencies(compilation.dependencies),
preprocessDependencies(substitutedDependencies),
ideProject
)
KotlinNativeLibrariesNameFixer.applyTo(dataNode)
for (sourceSet in compilation.sourceSets) {
if (sourceSet.fullName() == compilation.fullName()) continue
val targetDataNode = getSiblingKotlinModuleData(sourceSet, gradleModule, ideModule, resolverCtx) ?: continue
@@ -446,7 +443,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
else -> null
}
private fun preprocessDependencies(dependencies: Set<KotlinDependency>): List<ExternalDependency> {
private fun preprocessDependencies(dependencies: Collection<KotlinDependency>): List<ExternalDependency> {
return dependencies
.groupBy { it.id }
.mapValues { it.value.firstOrNull { it.scope == "COMPILE" } ?: it.value.lastOrNull() }
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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
import org.gradle.tooling.model.idea.IdeaModule
import org.jetbrains.plugins.gradle.model.BuildScriptClasspathModel
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData
import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext
import org.jetbrains.plugins.gradle.util.GradleConstants
fun buildClasspathData(
gradleModule: IdeaModule,
resolverCtx: ProjectResolverContext
): BuildScriptClasspathData {
val classpathModel = resolverCtx.getExtraProject(gradleModule, BuildScriptClasspathModel::class.java)
val classpathEntries = classpathModel?.classpath?.map {
BuildScriptClasspathData.ClasspathEntry(it.classes, it.sources, it.javadoc)
} ?: emptyList()
return BuildScriptClasspathData(GradleConstants.SYSTEM_ID, classpathEntries).also {
it.gradleHomeDir = classpathModel?.gradleHomeDir
}
}
@@ -0,0 +1,142 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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
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.util.ExternalSystemApiUtil
import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.util.Key
import org.gradle.tooling.model.idea.IdeaModule
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.inspections.gradle.findKotlinPluginVersion
import org.jetbrains.kotlin.konan.library.lite.LiteKonanLibraryInfoProvider
import org.jetbrains.plugins.gradle.ExternalDependencyId
import org.jetbrains.plugins.gradle.model.DefaultExternalLibraryDependency
import org.jetbrains.plugins.gradle.model.ExternalDependency
import org.jetbrains.plugins.gradle.model.ExternalLibraryDependency
import org.jetbrains.plugins.gradle.model.FileCollectionDependency
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext
import java.io.File
// KT-29613, KT-29783
internal class KotlinNativeLibrariesDependencySubstitutor(
private val mppModel: KotlinMPPGradleModel,
private val gradleModule: IdeaModule,
private val resolverCtx: ProjectResolverContext
) {
// Substitutes `ExternalDependency` entries that represent KLIBs with new dependency entries with proper type and name:
// - every `FileCollectionDependency` is checked whether it points to an existing KLIB, and substituted if it is
// - similarly for every `ExternalLibraryDependency` with `groupId == "Kotlin/Native"` (legacy KLIB provided by Gradle plugin <= 1.3.20)
fun substituteDependencies(dependencies: Collection<ExternalDependency>): List<ExternalDependency> {
val result = ArrayList(dependencies)
for (i in 0 until result.size) {
val dependency = result[i]
val dependencySubstitute = when (dependency) {
is FileCollectionDependency -> getFileCollectionDependencySubstitute(dependency)
is ExternalLibraryDependency -> getExternalLibraryDependencySubstitute(dependency)
else -> NoSubstitute
}
val newDependency = (dependencySubstitute as? YesSubstitute)?.substitute ?: continue
result[i] = newDependency
}
return result
}
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 kotlinVersion: String? by lazy {
val classpathData = buildClasspathData(gradleModule, resolverCtx)
val result = findKotlinPluginVersion(classpathData)
if (result == null)
LOG.error(
"""
Unexpectedly can't obtain Kotlin Gradle plugin version for ${gradleModule.name} module.
Build classpath is ${classpathData.classpathEntries.flatMap { it.classesFile }}.
${KotlinNativeLibrariesDependencySubstitutor::class.java.simpleName} will run in idle mode. No dependencies will be substituted.
""".trimIndent()
)
result
}
private fun getFileCollectionDependencySubstitute(dependency: FileCollectionDependency): DependencySubstitute =
resolverCtx.dependencySubstitutionCache.getOrPut(dependency.id) {
val libraryFile = dependency.files.firstOrNull() ?: return@getOrPut NoSubstitute
buildSubstituteIfNecessary(libraryFile)
}
private fun getExternalLibraryDependencySubstitute(dependency: ExternalLibraryDependency): DependencySubstitute =
resolverCtx.dependencySubstitutionCache.getOrPut(dependency.id) {
if (KOTLIN_NATIVE_LEGACY_GROUP_ID != dependency.group) return@getOrPut NoSubstitute
val libraryFile = dependency.file ?: return@getOrPut NoSubstitute
buildSubstituteIfNecessary(libraryFile)
}
private fun buildSubstituteIfNecessary(libraryFile: File): DependencySubstitute {
// need to check whether `libraryFile` 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.toPath()) ?: return NoSubstitute
val nonNullKotlinVersion = kotlinVersion ?: return NoSubstitute
val platformNamePart = libraryInfo.platform?.let { " [$it]" }.orEmpty()
val newLibraryName = "$KOTLIN_NATIVE_LIBRARY_PREFIX $nonNullKotlinVersion - ${libraryInfo.name}$platformNamePart"
val substitute = DefaultExternalLibraryDependency().apply {
name = newLibraryName
packaging = DEFAULT_PACKAGING
file = libraryFile
scope = DependencyScope.PROVIDED.name
}
return YesSubstitute(substitute)
}
companion object {
private const val DEFAULT_PACKAGING = "jar"
private val LOG = Logger.getInstance(KotlinNativeLibrariesDependencySubstitutor::class.java)
private val KLIB_DEPENDENCY_SUBSTITUTION_CACHE =
Key.create<MutableMap<ExternalDependencyId, DependencySubstitute>>("KLIB_DEPENDENCY_SUBSTITUTION_CACHE")
}
}
internal object KotlinNativeLibrariesNameFixer {
// Gradle IDE plugin creates `LibraryData` nodes with internal name consisting of two parts:
// - mandatory "Gradle: " prefix
// - and library name
// Then internal name is propagated to IDE `Library` object, and is displayed in IDE as "Gradle: <LIBRARY_NAME>".
// KotlinNativeLibrariesNameFixer removes "Gradle: " prefix from all `LibraryData` nodes representing KLIBs.
fun applyTo(ownerNode: DataNode<GradleSourceSetData>) {
for (libraryDependency in ExternalSystemApiUtil.findAll(ownerNode, ProjectKeys.LIBRARY_DEPENDENCY)) {
val libraryData = libraryDependency.data.target
if (libraryData.internalName.startsWith("$GRADLE_LIBRARY_PREFIX$KOTLIN_NATIVE_LIBRARY_PREFIX")) {
libraryData.internalName = libraryData.internalName.substringAfter(GRADLE_LIBRARY_PREFIX)
}
}
}
}
private sealed class DependencySubstitute {
object NoSubstitute : DependencySubstitute()
class YesSubstitute(val substitute: ExternalLibraryDependency) : DependencySubstitute()
}
private const val KOTLIN_NATIVE_LIBRARY_PREFIX = "Kotlin/Native"
private const val KOTLIN_NATIVE_LEGACY_GROUP_ID = KOTLIN_NATIVE_LIBRARY_PREFIX
private const val GRADLE_LIBRARY_PREFIX = "Gradle: "
@@ -330,14 +330,14 @@ class NewMultiplatformProjectImportingTest : GradleImportingTestCase() {
sourceFolder("shared/src/androidTest/resources", JavaResourceRootType.TEST_RESOURCE)
}
module("shared_iOSMain") {
libraryDependency("Gradle: Kotlin/Native:stdlib:0.9.3", DependencyScope.COMPILE)
libraryDependency("Kotlin/Native 0.9.3 - stdlib", DependencyScope.PROVIDED)
libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.0-rc-146", DependencyScope.COMPILE)
moduleDependency("shared_commonMain", DependencyScope.COMPILE)
sourceFolder("shared/src/iOSMain/kotlin", KotlinSourceRootType.Source)
sourceFolder("shared/src/iOSMain/resources", KotlinResourceRootType.Resource)
}
module("shared_iOSTest") {
libraryDependency("Gradle: Kotlin/Native:stdlib:0.9.3", DependencyScope.TEST)
libraryDependency("Kotlin/Native 0.9.3 - stdlib", DependencyScope.PROVIDED)
libraryDependency("Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.0-rc-146", DependencyScope.TEST)
moduleDependency("shared_iOSMain", DependencyScope.TEST)
moduleDependency("shared_commonMain", DependencyScope.TEST)
@@ -103,4 +103,9 @@ interface KotlinMPPGradleModel : Serializable {
val sourceSets: Map<String, KotlinSourceSet>
val targets: Collection<KotlinTarget>
val extraFeatures: ExtraFeatures
val kotlinNativeHome: String
companion object {
const val NO_KOTLIN_NATIVE_HOME = ""
}
}
@@ -16,6 +16,7 @@ import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.logging.Logging
import org.gradle.api.provider.Property
import org.jetbrains.kotlin.gradle.KotlinMPPGradleModel.Companion.NO_KOTLIN_NATIVE_HOME
import org.jetbrains.plugins.gradle.model.*
import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder
import org.jetbrains.plugins.gradle.tooling.ModelBuilderService
@@ -50,7 +51,8 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
computeSourceSetsDeferredInfo(sourceSets, targets)
val coroutinesState = getCoroutinesState(project)
reportUnresolvedDependencies(targets)
return KotlinMPPGradleModelImpl(sourceSetMap, targets, ExtraFeaturesImpl(coroutinesState))
val kotlinNativeHome = KotlinNativeHomeEvaluator.getKotlinNativeHome(project) ?: NO_KOTLIN_NATIVE_HOME
return KotlinMPPGradleModelImpl(sourceSetMap, targets, ExtraFeaturesImpl(coroutinesState), kotlinNativeHome)
}
private fun reportUnresolvedDependencies(targets: Collection<KotlinTarget>) {
@@ -24,6 +24,7 @@ import org.jetbrains.plugins.gradle.tooling.util.DependencyResolverImpl
import org.jetbrains.plugins.gradle.tooling.util.SourceSetCachedFinder
import java.io.File
import java.lang.reflect.Method
import org.jetbrains.kotlin.gradle.KotlinMPPGradleModel.Companion.NO_KOTLIN_NATIVE_HOME
class KotlinMPPGradleModelBuilder : ModelBuilderService {
override fun getErrorMessageBuilder(project: Project, e: Exception): ErrorMessageBuilder {
@@ -50,7 +51,7 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
computeSourceSetsDeferredInfo(sourceSets, targets)
val coroutinesState = getCoroutinesState(project)
reportUnresolvedDependencies(targets)
return KotlinMPPGradleModelImpl(sourceSetMap, targets, ExtraFeaturesImpl(coroutinesState))
return KotlinMPPGradleModelImpl(sourceSetMap, targets, ExtraFeaturesImpl(coroutinesState), NO_KOTLIN_NATIVE_HOME)
}
private fun reportUnresolvedDependencies(targets: Collection<KotlinTarget>) {
@@ -88,5 +88,6 @@ class ExtraFeaturesImpl(
class KotlinMPPGradleModelImpl(
override val sourceSets: Map<String, KotlinSourceSet>,
override val targets: Collection<KotlinTarget>,
override val extraFeatures: ExtraFeatures
override val extraFeatures: ExtraFeatures,
override val kotlinNativeHome: String
) : KotlinMPPGradleModel
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.gradle
import org.gradle.api.Project
// KT-29613, KT-29783
internal object KotlinNativeHomeEvaluator {
private const val KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY = "konanHome"
private const val FALLBACK_ACCESSOR_CLASS = "org.jetbrains.kotlin.compilerRunner.KotlinNativeToolRunnerKt"
private const val FALLBACK_ACCESSOR_METHOD = "getKonanHome"
internal fun getKotlinNativeHome(project: Project): String? =
getKotlinNativeHomePrimary(project) ?: getKotlinNativeHomeFallback(project)
// Read Kotlin/Native home from the predefined property in Gradle plugin.
// Should work for Gradle plugin with version >= 1.3.20.
private fun getKotlinNativeHomePrimary(project: Project) = project.findProperty(KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY) as String?
// Evaluate Kotlin/Native home using reflection by internal val declared in Gradle plugin.
// This should work for Gradle plugin with version < 1.3.20.
private fun getKotlinNativeHomeFallback(project: Project): String? {
val kotlinExtensionClassLoader = project.extensions.findByName("kotlin")?.javaClass?.classLoader ?: return null
val accessorClass = try {
Class.forName(FALLBACK_ACCESSOR_CLASS, true, kotlinExtensionClassLoader)
} catch (e: ClassNotFoundException) {
return null
}
val accessorMethod = accessorClass.getMethodOrNull(FALLBACK_ACCESSOR_METHOD, Project::class.java) ?: return null
return accessorMethod.invoke(null, project) as String
}
}
@@ -0,0 +1,2 @@
unique_name=stdlib
compiler_version=1.0
@@ -0,0 +1,2 @@
unique_name=linux
compiler_version=1.0
@@ -0,0 +1,2 @@
unique_name=posix
compiler_version=1.0
@@ -0,0 +1,2 @@
unique_name=zlib
compiler_version=1.0
@@ -0,0 +1,2 @@
unique_name=Foundation
compiler_version=1.0
@@ -0,0 +1,2 @@
unique_name=UIKit
compiler_version=1.0
@@ -0,0 +1,2 @@
unique_name=objc
compiler_version=1.0
@@ -0,0 +1,2 @@
unique_name=iconv
compiler_version=1.0
@@ -0,0 +1,2 @@
unique_name=opengl32
compiler_version=1.0
@@ -0,0 +1,2 @@
unique_name=windows
compiler_version=1.0
@@ -0,0 +1,48 @@
plugins {
kotlin("multiplatform") version "1.3.20"
}
repositories {
mavenCentral()
}
kotlin {
jvm()
js()
macosX64()
linuxX64()
mingwX64()
sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
jvm().compilations["main"].defaultSourceSet {
dependencies {
implementation(kotlin("stdlib-jdk8"))
}
}
jvm().compilations["test"].defaultSourceSet {
dependencies {
implementation(kotlin("test-junit"))
}
}
js().compilations["main"].defaultSourceSet {
dependencies {
implementation(kotlin("stdlib-js"))
}
}
}
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.native.home=../kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION
@@ -0,0 +1,48 @@
plugins {
kotlin("multiplatform") version "1.3.21"
}
repositories {
mavenCentral()
}
kotlin {
jvm()
js()
macosX64()
linuxX64()
mingwX64()
sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
jvm().compilations["main"].defaultSourceSet {
dependencies {
implementation(kotlin("stdlib-jdk8"))
}
}
jvm().compilations["test"].defaultSourceSet {
dependencies {
implementation(kotlin("test-junit"))
}
}
js().compilations["main"].defaultSourceSet {
dependencies {
implementation(kotlin("stdlib-js"))
}
}
}
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.native.home=../kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION
@@ -18,16 +18,20 @@ const val KDEFINITIONS_FILE_EXTENSION = "def"
const val KLIB_MODULE_METADATA_FILE_NAME = "module"
const val KLIB_MANIFEST_FILE_NAME = "manifest"
const val KONAN_STDLIB_NAME = "stdlib"
const val KLIB_DIR_NAME = "klib"
val KONAN_COMMON_LIBS_PATH: Path
get() = Paths.get("klib", "common")
get() = Paths.get(KLIB_DIR_NAME, "common")
val KONAN_ALL_PLATFORM_LIBS_PATH: Path
get() = Paths.get("klib", "platform")
get() = Paths.get(KLIB_DIR_NAME, "platform")
fun konanCommonLibraryPath(libraryName: String): Path = KONAN_COMMON_LIBS_PATH.resolve(libraryName)
fun konanSpecificPlatformLibrariesPath(platform: String): Path = KONAN_ALL_PLATFORM_LIBS_PATH.resolve(platform)
fun konanPlatformLibraryPath(platform: String, libraryName: String): Path = konanSpecificPlatformLibrariesPath(platform).resolve(libraryName)
fun konanPlatformLibraryPath(libraryName: String, platform: String): Path = konanSpecificPlatformLibrariesPath(platform).resolve(libraryName)
@@ -21,7 +21,7 @@ interface KonanLibraryLayout {
// This is a default implementation. Can't make it an assignment.
val target: KonanTarget? get() = null
val manifestFile get() = File(libDir, "manifest")
val manifestFile get() = File(libDir, KLIB_MANIFEST_FILE_NAME)
val resourcesDir get() = File(libDir, "resources")
val targetsDir get() = File(libDir, "targets")
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.nio.file.Path
data class LiteKonanLibrary(
val path: Path,
val name: String,
val platform: String?
)
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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_UNIQUE_NAME
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Path
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: Path): LiteKonanLibrary? {
// check whether it under Kotlin/Native root
if (!isUnderKonanRoot(libraryPath))
return null
val manifestFile = libraryPath.resolve(KLIB_MANIFEST_FILE_NAME)
if (!Files.isRegularFile(manifestFile))
return null
val parentPath = libraryPath.parent ?: return null
val parentName = parentPath.toFile().name
val platform = when (parentName) {
"common" -> null
else -> {
val grandParentName = parentPath.parent?.toFile()?.name ?: return null
when (grandParentName) {
"platform" -> parentName
else -> return null
}
}
}
val manifestProperties = Properties().apply {
try {
Files.newInputStream(manifestFile).use { load(it) }
} catch (e: IOException) {
return null
}
}
val name = manifestProperties[KLIB_PROPERTY_UNIQUE_NAME]?.toString() ?: return null
return LiteKonanLibrary(libraryPath, name, platform)
}
private fun isUnderKonanRoot(libraryPath: Path): Boolean {
if (konanHomeDir != null && libraryPath.startsWith(konanHomeDir))
return true
return libraryPath.startsWith(konanDataDir)
}
}