diff --git a/build.gradle.kts b/build.gradle.kts index 98c1da79a12..11fe19dfc96 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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") diff --git a/idea/idea-gradle-native/build.gradle.kts b/idea/idea-gradle-native/build.gradle.kts index ff9ce203bde..421906374b1 100644 --- a/idea/idea-gradle-native/build.gradle.kts +++ b/idea/idea-gradle-native/build.gradle.kts @@ -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() diff --git a/idea/idea-gradle-native/tests/org/jetbrains/kotlin/ide/konan/gradle/GradleNativeLibrariesInIDENamingTest.kt b/idea/idea-gradle-native/tests/org/jetbrains/kotlin/ide/konan/gradle/GradleNativeLibrariesInIDENamingTest.kt new file mode 100644 index 00000000000..9193f94477f --- /dev/null +++ b/idea/idea-gradle-native/tests/org/jetbrains/kotlin/ide/konan/gradle/GradleNativeLibrariesInIDENamingTest.kt @@ -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() + .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")) +} diff --git a/idea/idea-gradle-native/tests/org/jetbrains/kotlin/ide/konan/gradle/GradleNativeLibrariesInIDENamingTest.kt.as33 b/idea/idea-gradle-native/tests/org/jetbrains/kotlin/ide/konan/gradle/GradleNativeLibrariesInIDENamingTest.kt.as33 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-gradle-native/tests/org/jetbrains/kotlin/ide/konan/gradle/GradleNativeLibrariesInIDENamingTest.kt.as34 b/idea/idea-gradle-native/tests/org/jetbrains/kotlin/ide/konan/gradle/GradleNativeLibrariesInIDENamingTest.kt.as34 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPGradleProjectResolver.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPGradleProjectResolver.kt index 8b20d95ae2c..33c74ea3475 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPGradleProjectResolver.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPGradleProjectResolver.kt @@ -64,13 +64,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() { override fun populateModuleExtraModels(gradleModule: IdeaModule, ideModule: DataNode) { 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() 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): List { + private fun preprocessDependencies(dependencies: Collection): List { return dependencies .groupBy { it.id } .mapValues { it.value.firstOrNull { it.scope == "COMPILE" } ?: it.value.lastOrNull() } diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/classpathDataUtil.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/classpathDataUtil.kt new file mode 100644 index 00000000000..1ba7d360914 --- /dev/null +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/classpathDataUtil.kt @@ -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 + } +} diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/nativeLibrariesUtil.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/nativeLibrariesUtil.kt new file mode 100644 index 00000000000..dbb44397474 --- /dev/null +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/nativeLibrariesUtil.kt @@ -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): List { + 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>("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: ". + // KotlinNativeLibrariesNameFixer removes "Gradle: " prefix from all `LibraryData` nodes representing KLIBs. + fun applyTo(ownerNode: DataNode) { + 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: " diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/NewMultiplatformProjectImportingTest.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/NewMultiplatformProjectImportingTest.kt index 63062ab81db..a2d040a4299 100644 --- a/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/NewMultiplatformProjectImportingTest.kt +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/gradle/NewMultiplatformProjectImportingTest.kt @@ -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) diff --git a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModel.kt b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModel.kt index 9113bd1b3ea..6018c230835 100644 --- a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModel.kt +++ b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModel.kt @@ -103,4 +103,9 @@ interface KotlinMPPGradleModel : Serializable { val sourceSets: Map val targets: Collection val extraFeatures: ExtraFeatures + val kotlinNativeHome: String + + companion object { + const val NO_KOTLIN_NATIVE_HOME = "" + } } \ No newline at end of file diff --git a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt index 0db979abdac..0728e0c4d86 100644 --- a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt +++ b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt @@ -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) { diff --git a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt.181 b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt.181 index a7ff5effc27..36fd9f76ee9 100644 --- a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt.181 +++ b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt.181 @@ -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) { diff --git a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelImpl.kt b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelImpl.kt index 6b6b13df27d..7c21abb4f1f 100644 --- a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelImpl.kt +++ b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelImpl.kt @@ -88,5 +88,6 @@ class ExtraFeaturesImpl( class KotlinMPPGradleModelImpl( override val sourceSets: Map, override val targets: Collection, - override val extraFeatures: ExtraFeatures + override val extraFeatures: ExtraFeatures, + override val kotlinNativeHome: String ) : KotlinMPPGradleModel \ No newline at end of file diff --git a/idea/kotlin-gradle-tooling/src/KotlinNativeHomeEvaluator.kt b/idea/kotlin-gradle-tooling/src/KotlinNativeHomeEvaluator.kt new file mode 100644 index 00000000000..79a95e36a22 --- /dev/null +++ b/idea/kotlin-gradle-tooling/src/KotlinNativeHomeEvaluator.kt @@ -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 + } +} diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/common/stdlib/linkdata/empty.knm b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/common/stdlib/linkdata/empty.knm new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/common/stdlib/manifest b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/common/stdlib/manifest new file mode 100644 index 00000000000..184332fa5de --- /dev/null +++ b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/common/stdlib/manifest @@ -0,0 +1,2 @@ +unique_name=stdlib +compiler_version=1.0 diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/linux_x64/linux/linkdata/empty.knm b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/linux_x64/linux/linkdata/empty.knm new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/linux_x64/linux/manifest b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/linux_x64/linux/manifest new file mode 100644 index 00000000000..fda3767ce5a --- /dev/null +++ b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/linux_x64/linux/manifest @@ -0,0 +1,2 @@ +unique_name=linux +compiler_version=1.0 diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/linux_x64/posix/linkdata/empty.knm b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/linux_x64/posix/linkdata/empty.knm new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/linux_x64/posix/manifest b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/linux_x64/posix/manifest new file mode 100644 index 00000000000..59744461938 --- /dev/null +++ b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/linux_x64/posix/manifest @@ -0,0 +1,2 @@ +unique_name=posix +compiler_version=1.0 diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/linux_x64/zlib/linkdata/empty.knm b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/linux_x64/zlib/linkdata/empty.knm new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/linux_x64/zlib/manifest b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/linux_x64/zlib/manifest new file mode 100644 index 00000000000..f0a3ba72d3d --- /dev/null +++ b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/linux_x64/zlib/manifest @@ -0,0 +1,2 @@ +unique_name=zlib +compiler_version=1.0 diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/macos_x64/Foundation/linkdata/empty.knm b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/macos_x64/Foundation/linkdata/empty.knm new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/macos_x64/Foundation/manifest b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/macos_x64/Foundation/manifest new file mode 100644 index 00000000000..37261918de2 --- /dev/null +++ b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/macos_x64/Foundation/manifest @@ -0,0 +1,2 @@ +unique_name=Foundation +compiler_version=1.0 diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/macos_x64/UIKit/linkdata/empty.knm b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/macos_x64/UIKit/linkdata/empty.knm new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/macos_x64/UIKit/manifest b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/macos_x64/UIKit/manifest new file mode 100644 index 00000000000..31ac2f9694f --- /dev/null +++ b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/macos_x64/UIKit/manifest @@ -0,0 +1,2 @@ +unique_name=UIKit +compiler_version=1.0 diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/macos_x64/objc/linkdata/empty.knm b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/macos_x64/objc/linkdata/empty.knm new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/macos_x64/objc/manifest b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/macos_x64/objc/manifest new file mode 100644 index 00000000000..392508d08d1 --- /dev/null +++ b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/macos_x64/objc/manifest @@ -0,0 +1,2 @@ +unique_name=objc +compiler_version=1.0 diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/mingw_x64/iconv/linkdata/empty.knm b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/mingw_x64/iconv/linkdata/empty.knm new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/mingw_x64/iconv/manifest b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/mingw_x64/iconv/manifest new file mode 100644 index 00000000000..5999f8e5b72 --- /dev/null +++ b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/mingw_x64/iconv/manifest @@ -0,0 +1,2 @@ +unique_name=iconv +compiler_version=1.0 diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/mingw_x64/opengl32/linkdata/empty.knm b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/mingw_x64/opengl32/linkdata/empty.knm new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/mingw_x64/opengl32/manifest b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/mingw_x64/opengl32/manifest new file mode 100644 index 00000000000..4e1819a1660 --- /dev/null +++ b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/mingw_x64/opengl32/manifest @@ -0,0 +1,2 @@ +unique_name=opengl32 +compiler_version=1.0 diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/mingw_x64/windows/linkdata/empty.knm b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/mingw_x64/windows/linkdata/empty.knm new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/mingw_x64/windows/manifest b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/mingw_x64/windows/manifest new file mode 100644 index 00000000000..37d91701e08 --- /dev/null +++ b/idea/testData/gradle/nativeLibraries/kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION/klib/platform/mingw_x64/windows/manifest @@ -0,0 +1,2 @@ +unique_name=windows +compiler_version=1.0 diff --git a/idea/testData/gradle/nativeLibraries/legacyLibrariesNaming/build.gradle.kts b/idea/testData/gradle/nativeLibraries/legacyLibrariesNaming/build.gradle.kts new file mode 100644 index 00000000000..01a72715cf4 --- /dev/null +++ b/idea/testData/gradle/nativeLibraries/legacyLibrariesNaming/build.gradle.kts @@ -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")) + } + } + } +} diff --git a/idea/testData/gradle/nativeLibraries/legacyLibrariesNaming/gradle.properties b/idea/testData/gradle/nativeLibraries/legacyLibrariesNaming/gradle.properties new file mode 100644 index 00000000000..c7965da73e1 --- /dev/null +++ b/idea/testData/gradle/nativeLibraries/legacyLibrariesNaming/gradle.properties @@ -0,0 +1 @@ +org.jetbrains.kotlin.native.home=../kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION diff --git a/idea/testData/gradle/nativeLibraries/librariesNaming/build.gradle.kts b/idea/testData/gradle/nativeLibraries/librariesNaming/build.gradle.kts new file mode 100644 index 00000000000..55a69e21a0e --- /dev/null +++ b/idea/testData/gradle/nativeLibraries/librariesNaming/build.gradle.kts @@ -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")) + } + } + } +} diff --git a/idea/testData/gradle/nativeLibraries/librariesNaming/gradle.properties b/idea/testData/gradle/nativeLibraries/librariesNaming/gradle.properties new file mode 100644 index 00000000000..c7965da73e1 --- /dev/null +++ b/idea/testData/gradle/nativeLibraries/librariesNaming/gradle.properties @@ -0,0 +1 @@ +org.jetbrains.kotlin.native.home=../kotlin-native-data-dir/kotlin-native-PLATFORM-VERSION diff --git a/konan/library-reader/src/org/jetbrains/kotlin/konan/library/KonanLibraryConstants.kt b/konan/library-reader/src/org/jetbrains/kotlin/konan/library/KonanLibraryConstants.kt index b3d6bdaa77d..f387946e55c 100644 --- a/konan/library-reader/src/org/jetbrains/kotlin/konan/library/KonanLibraryConstants.kt +++ b/konan/library-reader/src/org/jetbrains/kotlin/konan/library/KonanLibraryConstants.kt @@ -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) diff --git a/konan/library-reader/src/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt b/konan/library-reader/src/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt index 35f9a8b1dff..af3bbd2b8a9 100644 --- a/konan/library-reader/src/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt +++ b/konan/library-reader/src/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt @@ -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") diff --git a/konan/library-reader/src/org/jetbrains/kotlin/konan/library/lite/LiteKonanLibrary.kt b/konan/library-reader/src/org/jetbrains/kotlin/konan/library/lite/LiteKonanLibrary.kt new file mode 100644 index 00000000000..74c0a028fec --- /dev/null +++ b/konan/library-reader/src/org/jetbrains/kotlin/konan/library/lite/LiteKonanLibrary.kt @@ -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? +) diff --git a/konan/library-reader/src/org/jetbrains/kotlin/konan/library/lite/LiteKonanLibraryInfoProvider.kt b/konan/library-reader/src/org/jetbrains/kotlin/konan/library/lite/LiteKonanLibraryInfoProvider.kt new file mode 100644 index 00000000000..627a7e1b9a1 --- /dev/null +++ b/konan/library-reader/src/org/jetbrains/kotlin/konan/library/lite/LiteKonanLibraryInfoProvider.kt @@ -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) + } +}