[K/N lib] IDEA plugin for K/N + missed items in konan-serializer module
This commit is contained in:
committed by
Mikhail Glukhikh
parent
51bb408c56
commit
eda29a48a4
@@ -0,0 +1,35 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile(project(":konan:konan-serializer"))
|
||||
|
||||
// compile("org.jetbrains.kotlin:kotlin-native-gradle-plugin")
|
||||
|
||||
compileOnly(project(":idea:idea-gradle"))
|
||||
compileOnly(project(":idea:idea-native"))
|
||||
|
||||
compileOnly(project(":idea")) { isTransitive = false }
|
||||
compileOnly(project(":idea:idea-jvm"))
|
||||
compile(project(":idea:kotlin-gradle-tooling"))
|
||||
|
||||
compile(project(":compiler:frontend"))
|
||||
compile(project(":compiler:frontend.java"))
|
||||
compile(project(":compiler:frontend.script"))
|
||||
|
||||
compile(project(":js:js.frontend"))
|
||||
|
||||
compileOnly(intellijDep())
|
||||
compileOnly(intellijPluginDep("gradle"))
|
||||
compileOnly(intellijPluginDep("Groovy"))
|
||||
compileOnly(intellijPluginDep("junit"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { none() }
|
||||
}
|
||||
|
||||
configureInstrumentation()
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.gradle
|
||||
|
||||
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
||||
import com.intellij.openapi.externalSystem.service.project.ProjectDataManager
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.find
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.findAll
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.util.BooleanFunction
|
||||
import com.sun.media.jfxmediaimpl.platform.PlatformManager
|
||||
import org.jetbrains.konan.settings.KonanArtifact
|
||||
import org.jetbrains.konan.settings.KonanModelProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.model.KonanModel
|
||||
import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifact
|
||||
import org.jetbrains.kotlin.konan.target.PlatformManager
|
||||
import org.jetbrains.kotlin.konan.target.customerDistribution
|
||||
import org.jetbrains.plugins.gradle.settings.GradleSettings
|
||||
import org.jetbrains.plugins.gradle.util.GradleConstants
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
|
||||
class GradleKonanModelProvider : KonanModelProvider {
|
||||
override fun reloadLibraries(project: Project, libraryPaths: Collection<Path>): Boolean =
|
||||
GradleSettings.getInstance(project).linkedProjectsSettings.isNotEmpty()
|
||||
|
||||
override fun getKonanHome(project: Project): Path? {
|
||||
val projectNode = ProjectDataManager.getInstance().getExternalProjectsData(project, GradleConstants.SYSTEM_ID)
|
||||
.mapNotNull { it.externalProjectStructure }
|
||||
.firstOrNull() ?: return null
|
||||
projectNode.getUserData(KONAN_HOME)?.let { return it }
|
||||
|
||||
var konanHomePath: Path? = null
|
||||
find(projectNode, ProjectKeys.MODULE, BooleanFunction { moduleNode ->
|
||||
find(moduleNode, KonanProjectResolver.KONAN_MODEL_KEY, BooleanFunction { konanModelNode ->
|
||||
konanHomePath = konanModelNode.data.konanHome.toPath()
|
||||
projectNode.putUserData(KONAN_HOME, konanHomePath)
|
||||
konanHomePath != null
|
||||
}) != null
|
||||
})
|
||||
return konanHomePath
|
||||
}
|
||||
|
||||
override fun getArtifacts(project: Project): Collection<KonanArtifact> {
|
||||
val artifacts = mutableListOf<KonanArtifact>()
|
||||
ProjectDataManager.getInstance().getExternalProjectsData(project, GradleConstants.SYSTEM_ID)
|
||||
.mapNotNull { it.externalProjectStructure }
|
||||
.forEach { projectStructure ->
|
||||
findAll(projectStructure, ProjectKeys.MODULE)
|
||||
.map { Pair(it.data.externalName, find<KonanModel>(it, KonanProjectResolver.KONAN_MODEL_KEY)) }
|
||||
.filter { it.second != null }
|
||||
.forEach { (moduleName, konanProjectNode) ->
|
||||
konanProjectNode!!.data.artifacts.forEach { konanArtifact ->
|
||||
val sources = konanArtifact.srcFiles.map { it.toPath() }
|
||||
|
||||
artifacts.add(
|
||||
KonanArtifact(
|
||||
konanArtifact.name,
|
||||
moduleName,
|
||||
konanArtifact.type,
|
||||
konanTarget(konanProjectNode.data.konanHome, konanArtifact),
|
||||
mutableListOf(), sources, konanArtifact.file.toPath()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return artifacts
|
||||
}
|
||||
|
||||
private fun konanTarget(konanHome: File, konanArtifactEx: KonanModelArtifact) =
|
||||
PlatformManager(customerDistribution(konanHome.absolutePath)).targetValues.find { it.name == konanArtifactEx.targetPlatform }
|
||||
|
||||
companion object {
|
||||
val KONAN_HOME = Key.create<Path>("KONAN_HOME")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.gradle
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.konan.settings.KonanProjectComponent
|
||||
import org.jetbrains.plugins.gradle.settings.GradleSettings
|
||||
|
||||
class GradleKonanProjectComponent(project: Project) : KonanProjectComponent(project) {
|
||||
override fun looksLikeKotlinNativeProject(): Boolean {
|
||||
//TODO not just any gradle project
|
||||
return GradleSettings.getInstance(project).linkedProjectsSettings.isNotEmpty()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.gradle
|
||||
|
||||
import com.intellij.execution.RunManager
|
||||
import com.intellij.execution.RunnerAndConfigurationSettings
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.externalSystem.model.DataNode
|
||||
import com.intellij.openapi.externalSystem.model.Key
|
||||
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
||||
import com.intellij.openapi.externalSystem.model.project.ModuleData
|
||||
import com.intellij.openapi.externalSystem.model.project.ProjectData
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeModelsProvider
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
|
||||
import com.intellij.openapi.externalSystem.service.project.ProjectDataManager
|
||||
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import org.jetbrains.konan.settings.KonanArtifact
|
||||
import org.jetbrains.konan.settings.KonanModelProvider
|
||||
import org.jetbrains.konan.settings.isExecutable
|
||||
import org.jetbrains.kotlin.gradle.plugin.model.KonanModel
|
||||
import org.jetbrains.plugins.gradle.util.GradleConstants
|
||||
|
||||
class KonanProjectDataService : AbstractProjectDataService<KonanModel, Module>() {
|
||||
|
||||
override fun getTargetDataKey(): Key<KonanModel> = KonanProjectResolver.KONAN_MODEL_KEY
|
||||
|
||||
override fun postProcess(
|
||||
toImport: Collection<DataNode<KonanModel>>,
|
||||
projectData: ProjectData?,
|
||||
project: Project,
|
||||
modelsProvider: IdeModifiableModelsProvider
|
||||
) {
|
||||
}
|
||||
|
||||
override fun onSuccessImport(
|
||||
imported: Collection<DataNode<KonanModel>>,
|
||||
projectData: ProjectData?,
|
||||
project: Project,
|
||||
modelsProvider: IdeModelsProvider
|
||||
) {
|
||||
if (projectData?.owner != GradleConstants.SYSTEM_ID) return
|
||||
project.messageBus.syncPublisher(KonanModelProvider.RELOAD_TOPIC).run()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun forEachKonanProject(
|
||||
project: Project,
|
||||
consumer: (konanProject: KonanModel, moduleData: ModuleData, rootProjectPath: String) -> Unit
|
||||
) {
|
||||
for (projectInfo in ProjectDataManager.getInstance().getExternalProjectsData(project, GradleConstants.SYSTEM_ID)) {
|
||||
val projectStructure = projectInfo.externalProjectStructure ?: continue
|
||||
val projectData = projectStructure.data
|
||||
val rootProjectPath = projectData.linkedExternalProjectPath
|
||||
val modulesNodes = ExternalSystemApiUtil.findAll(projectStructure, ProjectKeys.MODULE)
|
||||
for (moduleNode in modulesNodes) {
|
||||
val projectNode = ExternalSystemApiUtil.find<KonanModel>(moduleNode, KonanProjectResolver.KONAN_MODEL_KEY)
|
||||
if (projectNode != null) {
|
||||
val konanProject = projectNode.data
|
||||
val moduleData = moduleNode.data
|
||||
consumer(konanProject, moduleData, rootProjectPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.gradle
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.externalSystem.model.DataNode
|
||||
import com.intellij.openapi.externalSystem.model.Key
|
||||
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
||||
import com.intellij.openapi.externalSystem.model.project.*
|
||||
import com.intellij.util.containers.ContainerUtil.set
|
||||
import com.intellij.util.io.isDirectory
|
||||
import org.gradle.tooling.model.idea.IdeaModule
|
||||
import org.jetbrains.kotlin.gradle.plugin.model.KonanModel
|
||||
import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifact
|
||||
import org.jetbrains.kotlin.konan.KonanVersion
|
||||
import org.jetbrains.kotlin.konan.MetaVersion
|
||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension
|
||||
import org.jetbrains.plugins.gradle.util.GradleConstants
|
||||
import java.io.File
|
||||
import java.nio.file.Files.isDirectory
|
||||
import java.nio.file.Files.walk
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* [KonanProjectResolver] creates IDE project model in terms of External System API
|
||||
*/
|
||||
class KonanProjectResolver : AbstractProjectResolverExtension() {
|
||||
|
||||
// to ask gradle for the model
|
||||
override fun getExtraProjectModelClasses(): Set<Class<*>> {
|
||||
return set<Class<*>>(KonanModel::class.java)
|
||||
}
|
||||
|
||||
override fun populateModuleExtraModels(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>) {
|
||||
resolverCtx.getExtraProject(gradleModule, KonanModel::class.java)?.let {
|
||||
// store a local process copy of the object to get rid of proxy types for further serialization
|
||||
ideModule.createChild(KONAN_MODEL_KEY, MyKonanModel(it))
|
||||
}
|
||||
|
||||
nextResolver.populateModuleExtraModels(gradleModule, ideModule)
|
||||
}
|
||||
|
||||
override fun populateModuleContentRoots(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>) {
|
||||
resolverCtx.getExtraProject(gradleModule, KonanModel::class.java)?.let {
|
||||
val added = mutableSetOf<String>()
|
||||
for (artifact in it.artifacts) {
|
||||
for (srcDir in artifact.srcDirs) {
|
||||
val rootPath = srcDir.absolutePath
|
||||
if (!added.add(rootPath)) continue
|
||||
|
||||
val ideContentRoot = ContentRootData(GradleConstants.SYSTEM_ID, rootPath)
|
||||
ideContentRoot.storePath(ExternalSystemSourceType.SOURCE, rootPath)
|
||||
ideModule.createChild(ProjectKeys.CONTENT_ROOT, ideContentRoot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextResolver.populateModuleContentRoots(gradleModule, ideModule)
|
||||
}
|
||||
|
||||
// based on KonanCMakeProjectComponent.reloadLibraries but with the multi-module projects support
|
||||
override fun populateModuleDependencies(
|
||||
gradleModule: IdeaModule,
|
||||
ideModule: DataNode<ModuleData>,
|
||||
ideProject: DataNode<ProjectData>
|
||||
) {
|
||||
val konanModelEx = resolverCtx.getExtraProject(gradleModule, KonanModel::class.java)
|
||||
if (konanModelEx != null) {
|
||||
val libraryPaths = LinkedHashSet<Path>()
|
||||
var konanHome: Path? = null
|
||||
var targetPlatform: String? = null
|
||||
for (konanArtifact in konanModelEx.artifacts) {
|
||||
if (konanHome == null) {
|
||||
konanHome = konanModelEx.konanHome.toPath()
|
||||
targetPlatform = konanArtifact.targetPlatform
|
||||
}
|
||||
konanArtifact.libraries.forEach { libraryPaths.add(it.toPath()) }
|
||||
}
|
||||
|
||||
if (konanHome != null) {
|
||||
// add konanStdlib copied from KonanPaths.konanStdlib
|
||||
libraryPaths.add(konanHome.resolve("klib/common/stdlib"))
|
||||
|
||||
// add konanPlatformLibraries, copied from KonanPaths.konanPlatformLibraries
|
||||
if (targetPlatform != null) {
|
||||
try {
|
||||
val resolvedTargetName = HostManager.resolveAlias(targetPlatform)
|
||||
val klibPath = konanHome.resolve("klib/platform/${resolvedTargetName}")
|
||||
walk(klibPath, 1)
|
||||
.filter { it.isDirectory() && it.fileName.toString() != "stdlib" && it != klibPath }
|
||||
.forEach { libraryPaths.add(it) }
|
||||
} catch (e: Exception) {
|
||||
LOG.warn("Unable to collect konan platform libraries paths for '$targetPlatform'", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val moduleData = ideModule.data
|
||||
for (path in libraryPaths) {
|
||||
val library = LibraryData(moduleData.owner, path.fileName.toString())
|
||||
library.addPath(LibraryPathType.BINARY, if (isDirectory(path)) path.toAbsolutePath().toString() else path.toString())
|
||||
|
||||
val data = LibraryDependencyData(moduleData, library, LibraryLevel.MODULE)
|
||||
ideModule.createChild(ProjectKeys.LIBRARY_DEPENDENCY, data)
|
||||
}
|
||||
}
|
||||
|
||||
nextResolver.populateModuleDependencies(gradleModule, ideModule, ideProject)
|
||||
}
|
||||
|
||||
private class MyKonanModel(konanModel: KonanModel) : KonanModel {
|
||||
override val artifacts: List<KonanModelArtifact> = konanModel.artifacts.map { MyKonanArtifactEx(it) }
|
||||
override val konanHome: File = konanModel.konanHome
|
||||
override val konanVersion: KonanVersion = MyKonanVersionEx(konanModel.konanVersion)
|
||||
override val apiVersion: String? = konanModel.apiVersion
|
||||
override val languageVersion: String? = konanModel.languageVersion
|
||||
|
||||
private class MyKonanVersionEx(version: KonanVersion) : KonanVersion {
|
||||
override val build: Int = version.build
|
||||
override val maintenance: Int = version.maintenance
|
||||
override val major: Int = version.major
|
||||
override val meta: MetaVersion = version.meta
|
||||
override val minor: Int = version.minor
|
||||
|
||||
override fun toString(showMeta: Boolean, showBuild: Boolean): String {
|
||||
val sb = StringBuilder("$major.$minor.$maintenance")
|
||||
if (showMeta) sb.append('-').append(meta)
|
||||
if (showBuild) sb.append('-').append(build)
|
||||
return sb.toString()
|
||||
}
|
||||
}
|
||||
|
||||
private class MyKonanArtifactEx(artifact: KonanModelArtifact) : KonanModelArtifact {
|
||||
override val searchPaths: List<File> = artifact.searchPaths
|
||||
override val name: String = artifact.name
|
||||
override val type: CompilerOutputKind = artifact.type
|
||||
override val targetPlatform: String = artifact.targetPlatform
|
||||
override val file: File = artifact.file
|
||||
override val buildTaskName: String = artifact.buildTaskName
|
||||
|
||||
override val srcDirs: List<File> = artifact.srcDirs.toList()
|
||||
override val srcFiles: List<File> = artifact.srcFiles.toList()
|
||||
override val libraries: List<File> = artifact.libraries.toList()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val KONAN_MODEL_KEY = Key.create(KonanModel::class.java, ProjectKeys.MODULE.processingWeight + 1)
|
||||
private val LOG = Logger.getInstance(KonanProjectResolver::class.java)
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.gradle.internal;
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionEP;
|
||||
import com.intellij.openapi.components.ApplicationComponent;
|
||||
import com.intellij.openapi.extensions.ExtensionPoint;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
|
||||
public class KotlinNativeIdeInitializer implements ApplicationComponent {
|
||||
|
||||
@Override
|
||||
public void initComponent() {
|
||||
unregisterGroovyInspections();
|
||||
}
|
||||
|
||||
// There are groovy local inspections which should not be loaded w/o groovy plugin enabled.
|
||||
// Those plugin definitions should become optional and dependant on groovy plugin.
|
||||
// This is a temp workaround before it happens.
|
||||
private static void unregisterGroovyInspections() {
|
||||
ExtensionPoint<LocalInspectionEP> extensionPoint =
|
||||
Extensions.getRootArea().getExtensionPoint(LocalInspectionEP.LOCAL_INSPECTION);
|
||||
|
||||
for (LocalInspectionEP ep : extensionPoint.getExtensions()) {
|
||||
if ("Kotlin".equals(ep.groupDisplayName) && "Groovy".equals(ep.language)) {
|
||||
extensionPoint.unregisterExtension(ep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile(project(":idea"))
|
||||
compile(project(":idea:idea-core"))
|
||||
compile(project(":compiler:frontend"))
|
||||
compileOnly(intellijDep())
|
||||
compile(project(":konan:konan-serializer"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" {
|
||||
projectDefault()
|
||||
// java.srcDirs("$rootDir/core/runtime.jvm/src")
|
||||
}
|
||||
"test" { none() }
|
||||
}
|
||||
|
||||
configureInstrumentation()
|
||||
|
||||
runtimeJar {
|
||||
archiveName = "native-ide.jar"
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan
|
||||
|
||||
import com.intellij.ide.highlighter.ArchiveFileType
|
||||
import com.intellij.ide.util.TipAndTrickBean
|
||||
import com.intellij.openapi.components.ApplicationComponent
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager
|
||||
|
||||
class KonanApplicationComponent : ApplicationComponent {
|
||||
override fun getComponentName(): String = "KonanApplicationComponent"
|
||||
|
||||
override fun initComponent() {
|
||||
FileTypeManager.getInstance().associateExtension(ArchiveFileType.INSTANCE, "klib")
|
||||
|
||||
val extensionPoint = Extensions.getRootArea().getExtensionPoint(TipAndTrickBean.EP_NAME)
|
||||
for (name in arrayOf("Kotlin.html", "Kotlin_project.html", "Kotlin_mix.html", "Kotlin_Java_convert.html")) {
|
||||
TipAndTrickBean.findByFileName(name)?.let {
|
||||
extensionPoint.unregisterExtension(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.decompiler.KotlinDecompiledFileViewProvider
|
||||
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText
|
||||
|
||||
class KonanDecompiledFile(provider: KotlinDecompiledFileViewProvider, text: (VirtualFile) -> DecompiledText) :
|
||||
KtDecompiledFile(provider, text)
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.konan.settings.KonanPaths
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.konan.library.*
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
|
||||
class KonanPluginSearchPathResolver(val project: Project) : SearchPathResolverWithTarget {
|
||||
|
||||
private val paths by lazy { KonanPaths(project) }
|
||||
|
||||
override val searchRoots: List<File> by lazy {
|
||||
paths.konanDist()?.let { distPath ->
|
||||
listOf(KONAN_COMMON_LIBS_PATH, konanSpecificPlatformLibrariesPath(target.toString())).mapNotNull { relativePath ->
|
||||
distPath.resolve(relativePath).File().takeIf { it.exists }
|
||||
}
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
override fun resolve(givenPath: String): File {
|
||||
|
||||
val given = File(givenPath)
|
||||
|
||||
if (given.isAbsolute) {
|
||||
found(given)?.apply { return this }
|
||||
} else {
|
||||
searchRoots.forEach {
|
||||
found(File(it, givenPath))?.apply { return this }
|
||||
}
|
||||
}
|
||||
|
||||
error("Could not find \"$givenPath\" in ${searchRoots.map { it.absolutePath }}.")
|
||||
}
|
||||
|
||||
override fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List<File> {
|
||||
|
||||
val result = mutableListOf<File>()
|
||||
|
||||
if (!noStdLib) {
|
||||
result.add(resolve(KONAN_STDLIB_NAME))
|
||||
}
|
||||
|
||||
if (!noDefaultLibs) {
|
||||
val defaultLibs = searchRoots.flatMap { it.listFiles }
|
||||
.filterNot { it.name.removeSuffix(KLIB_FILE_EXTENSION_WITH_DOT) == KONAN_STDLIB_NAME }
|
||||
.map { File(it.absolutePath) }
|
||||
result.addAll(defaultLibs)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
override val target: KonanTarget
|
||||
get() = paths.target()
|
||||
|
||||
private fun found(candidate: File): File? {
|
||||
|
||||
fun check(file: File): Boolean =
|
||||
file.exists && (file.isFile || File(file, "manifest").exists)
|
||||
|
||||
val noSuffix = File(candidate.path.removeSuffix(KLIB_FILE_EXTENSION_WITH_DOT))
|
||||
val withSuffix = File(candidate.path.suffixIfNot(KLIB_FILE_EXTENSION_WITH_DOT))
|
||||
|
||||
return when {
|
||||
check(withSuffix) -> withSuffix
|
||||
check(noSuffix) -> noSuffix
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.suffixIfNot(suffix: String) = if (this.endsWith(suffix)) this else "$this$suffix"
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.psi.PsiFileFactory
|
||||
import com.intellij.psi.SingleRootFileViewProvider
|
||||
import com.intellij.psi.impl.PsiFileFactoryImpl
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.stubs.PsiFileStub
|
||||
import com.intellij.testFramework.LightVirtualFile
|
||||
import org.jetbrains.kotlin.analyzer.ModuleContent
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.context.ModuleContext
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.idea.caches.project.LibraryInfo
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.LoggingErrorReporter
|
||||
import org.jetbrains.kotlin.konan.library.KonanLibrary
|
||||
import org.jetbrains.kotlin.konan.library.libraryResolver
|
||||
import org.jetbrains.kotlin.konan.utils.KonanFactories
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
|
||||
import org.jetbrains.kotlin.serialization.konan.KonanResolvedModuleDescriptors
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import java.io.File.pathSeparatorChar
|
||||
|
||||
const val KONAN_CURRENT_ABI_VERSION = 1
|
||||
|
||||
fun createFileStub(project: Project, text: String): PsiFileStub<*> {
|
||||
val virtualFile = LightVirtualFile("dummy.kt", KotlinFileType.INSTANCE, text)
|
||||
virtualFile.language = KotlinLanguage.INSTANCE
|
||||
SingleRootFileViewProvider.doNotCheckFileSizeLimit(virtualFile)
|
||||
|
||||
val psiFileFactory = PsiFileFactory.getInstance(project) as PsiFileFactoryImpl
|
||||
val file = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, false, false)!!
|
||||
return KtStubElementTypes.FILE.builder.buildStubTree(file) as PsiFileStub<*>
|
||||
}
|
||||
|
||||
fun createLoggingErrorReporter(log: Logger) = LoggingErrorReporter(log)
|
||||
|
||||
fun <M : ModuleInfo> destructModuleContent(moduleContent: ModuleContent<M>) =
|
||||
moduleContent.syntheticFiles to moduleContent.moduleContentScope
|
||||
|
||||
fun <M : ModuleInfo> createDeclarationProviderFactory(
|
||||
project: Project,
|
||||
moduleContext: ModuleContext,
|
||||
syntheticFiles: Collection<KtFile>,
|
||||
moduleInfo: M,
|
||||
globalSearchScope: GlobalSearchScope?
|
||||
) = DeclarationProviderFactoryService.createDeclarationProviderFactory(
|
||||
project,
|
||||
moduleContext.storageManager,
|
||||
syntheticFiles,
|
||||
globalSearchScope!!,
|
||||
moduleInfo
|
||||
)
|
||||
|
||||
fun Module.createResolvedModuleDescriptors(
|
||||
storageManager: StorageManager,
|
||||
builtIns: KotlinBuiltIns,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
): KonanResolvedModuleDescriptors {
|
||||
|
||||
val libraryMap = mutableMapOf<String, LibraryInfo>()
|
||||
ModuleRootManager.getInstance(this).orderEntries().forEachLibrary { intellijLibrary ->
|
||||
intellijLibrary.name?.let { name -> libraryMap[name] = LibraryInfo(project, intellijLibrary) }
|
||||
true
|
||||
}
|
||||
|
||||
val resolvedLibraries =
|
||||
KonanPluginSearchPathResolver(project).libraryResolver(KONAN_CURRENT_ABI_VERSION).resolveWithDependencies(libraryMap.keys.toList())
|
||||
|
||||
return KonanFactories.DefaultResolvedDescriptorsFactory.createResolved(
|
||||
resolvedLibraries,
|
||||
storageManager,
|
||||
builtIns,
|
||||
languageVersionSettings,
|
||||
null,
|
||||
// Preserve capabilities from the original IntelliJ library:
|
||||
{ konanLibrary -> libraryMap[konanLibrary.pureName]?.capabilities ?: emptyMap() }
|
||||
)
|
||||
}
|
||||
|
||||
private val KonanLibrary.pureName
|
||||
get() = libraryName.substringAfterLast(pathSeparatorChar)
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.progress.Task
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.SystemProperties.getUserHome
|
||||
import com.intellij.util.io.exists
|
||||
import com.intellij.util.net.IOExceptionDialog
|
||||
import org.jetbrains.konan.settings.KonanProjectComponent
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
import org.jetbrains.kotlin.konan.util.DependencyDownloaderHTTPResponseException
|
||||
import org.jetbrains.kotlin.konan.util.DependencyProcessor
|
||||
import org.jetbrains.kotlin.konan.util.DependencySource
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
class KotlinNativeToolchain(
|
||||
val version: String,
|
||||
val repoUrl: String
|
||||
) {
|
||||
private val artifactName = "kotlin-native-$KONAN_OS-$version"
|
||||
val baseDir: Path get() = Paths.get("${getUserHome()}/.konan/$artifactName")
|
||||
val konanc: Path get() = baseDir.resolve("bin/konanc")
|
||||
val cinterop: Path get() = baseDir.resolve("bin/cinterop")
|
||||
|
||||
|
||||
fun ensureExists(project: Project) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread()
|
||||
if (baseDir.exists()) return
|
||||
|
||||
for (attempt in 0..3) {
|
||||
var exception: Throwable? = null
|
||||
object : Task.Modal(project, "Downloading Kotlin/Native $version", false) {
|
||||
override fun run(progress: ProgressIndicator) {
|
||||
DependencyProcessor(
|
||||
baseDir.parent.toFile(),
|
||||
repoUrl,
|
||||
mapOf(artifactName to listOf(DependencySource.Remote.Public)),
|
||||
customProgressCallback = { _, downloaded, total ->
|
||||
progress.fraction = downloaded / maxOf(total, downloaded, 1).toDouble()
|
||||
}
|
||||
).run()
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
project.getComponent(KonanProjectComponent::class.java).reloadLibraries()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onThrowable(error: Throwable) {
|
||||
exception = error
|
||||
}
|
||||
}.queue()
|
||||
|
||||
val ex = exception
|
||||
val tryAgain = if (ex == null) {
|
||||
false
|
||||
} else {
|
||||
val details = if (ex is DependencyDownloaderHTTPResponseException) {
|
||||
"Server returned ${ex.responseCode} when trying to download ${ex.url}."
|
||||
} else {
|
||||
LOG.error(ex)
|
||||
"Unknown error occurred."
|
||||
}
|
||||
IOExceptionDialog.showErrorDialog(
|
||||
"Failed to download Kotlin/Native",
|
||||
details
|
||||
)
|
||||
}
|
||||
if (!tryAgain) break
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun looksLikeBundledToolchain(path: String): Boolean =
|
||||
Paths.get(path).startsWith(Paths.get("${getUserHome()}/.konan/"))
|
||||
|
||||
private val KONAN_OS = HostManager.simpleOsName()
|
||||
|
||||
//todo: fixme
|
||||
private val BUNDLED_VERSION = "0.8" //bundledFile("kotlin-native-version").readText().trim()
|
||||
private val BUILD_DIR = "releases"
|
||||
|
||||
val BUNDLED = KotlinNativeToolchain(
|
||||
version = BUNDLED_VERSION,
|
||||
repoUrl = "https://download.jetbrains.com/kotlin/native/builds/$BUILD_DIR/$BUNDLED_VERSION/$KONAN_OS"
|
||||
)
|
||||
|
||||
private val LOG = Logger.getInstance(KotlinNativeToolchain::class.java)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.startup.StartupActivity
|
||||
import org.jetbrains.konan.settings.KonanProjectComponent
|
||||
|
||||
class SetupKotlinNativeStartupActivity : StartupActivity {
|
||||
override fun runActivity(project: Project) {
|
||||
//todo: looks like without default project component (like disabled Gradle plugin) we will get exception here (that's bad)
|
||||
if (!KonanProjectComponent.getInstance(project).looksLikeKotlinNativeProject()) return
|
||||
ensureKotlinNativeExists(project)
|
||||
}
|
||||
|
||||
private fun ensureKotlinNativeExists(project: Project) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread()
|
||||
KotlinNativeToolchain.BUNDLED.ensureExists(project)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.analyser
|
||||
|
||||
import org.jetbrains.konan.createDeclarationProviderFactory
|
||||
import org.jetbrains.konan.createResolvedModuleDescriptors
|
||||
import org.jetbrains.konan.destructModuleContent
|
||||
import org.jetbrains.kotlin.analyzer.*
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.TargetPlatformVersion
|
||||
import org.jetbrains.kotlin.container.get
|
||||
import org.jetbrains.kotlin.context.ModuleContext
|
||||
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.frontend.di.createContainerForLazyResolve
|
||||
import org.jetbrains.kotlin.idea.caches.project.ModuleProductionSourceInfo
|
||||
import org.jetbrains.kotlin.resolve.BindingTraceContext
|
||||
import org.jetbrains.kotlin.resolve.TargetEnvironment
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.resolve.konan.platform.KonanPlatform
|
||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
|
||||
|
||||
/**
|
||||
* @author Alefas
|
||||
*/
|
||||
class KonanAnalyzerFacade : ResolverForModuleFactory() {
|
||||
override val targetPlatform: TargetPlatform
|
||||
get() = KonanPlatform
|
||||
|
||||
override fun <M : ModuleInfo> createResolverForModule(
|
||||
moduleDescriptor: ModuleDescriptorImpl,
|
||||
moduleContext: ModuleContext,
|
||||
moduleContent: ModuleContent<M>,
|
||||
platformParameters: PlatformAnalysisParameters,
|
||||
targetEnvironment: TargetEnvironment,
|
||||
resolverForProject: ResolverForProject<M>,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
targetPlatformVersion: TargetPlatformVersion
|
||||
): ResolverForModule {
|
||||
|
||||
val (syntheticFiles, moduleContentScope) = destructModuleContent(moduleContent)
|
||||
val project = moduleContext.project
|
||||
|
||||
val declarationProviderFactory = createDeclarationProviderFactory(
|
||||
project,
|
||||
moduleContext,
|
||||
syntheticFiles,
|
||||
moduleContent.moduleInfo,
|
||||
moduleContentScope
|
||||
)
|
||||
|
||||
val container = createContainerForLazyResolve(
|
||||
moduleContext,
|
||||
declarationProviderFactory,
|
||||
BindingTraceContext(),
|
||||
targetPlatform,
|
||||
TargetPlatformVersion.NoVersion,
|
||||
targetEnvironment,
|
||||
languageVersionSettings
|
||||
)
|
||||
|
||||
val packageFragmentProvider = container.get<ResolveSession>().packageFragmentProvider
|
||||
val module = (moduleContent.moduleInfo as? ModuleProductionSourceInfo)?.module
|
||||
|
||||
val fragmentProviders = mutableListOf(packageFragmentProvider)
|
||||
|
||||
if (module != null) {
|
||||
|
||||
val moduleDescriptors = module.createResolvedModuleDescriptors(
|
||||
moduleContext.storageManager,
|
||||
moduleContext.module.builtIns, // FIXME(ddol): investigate: reuse existing builtIns (from KonanPlatformSupport) or create new one
|
||||
languageVersionSettings
|
||||
)
|
||||
|
||||
moduleDescriptors.resolvedDescriptors.mapTo(fragmentProviders) { it.packageFragmentProvider }
|
||||
fragmentProviders.add(moduleDescriptors.forwardDeclarationsModule.packageFragmentProvider)
|
||||
}
|
||||
|
||||
return ResolverForModule(CompositePackageFragmentProvider(fragmentProviders), container)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.analyser
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import org.jetbrains.konan.KONAN_CURRENT_ABI_VERSION
|
||||
import org.jetbrains.konan.settings.KonanPaths
|
||||
import org.jetbrains.kotlin.analyzer.ResolverForModuleFactory
|
||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.caches.resolve.IdePlatformSupport
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
||||
import org.jetbrains.kotlin.context.GlobalContextImpl
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.PlatformAnalysisSettings
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.konan.library.createKonanLibrary
|
||||
import org.jetbrains.kotlin.konan.utils.KonanFactories.DefaultDeserializedDescriptorFactory
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.resolve.konan.platform.KonanPlatform
|
||||
|
||||
/**
|
||||
* @author Alefas
|
||||
*/
|
||||
class KonanPlatformSupport : IdePlatformSupport() {
|
||||
|
||||
override val resolverForModuleFactory: ResolverForModuleFactory
|
||||
get() = KonanAnalyzerFacade()
|
||||
|
||||
override val libraryKind: PersistentLibraryKind<*>? = null
|
||||
|
||||
override val platform: TargetPlatform
|
||||
get() = KonanPlatform
|
||||
|
||||
override fun createBuiltIns(settings: PlatformAnalysisSettings, sdkContext: GlobalContextImpl) = createKonanBuiltIns(sdkContext)
|
||||
|
||||
override fun isModuleForPlatform(module: Module) = true
|
||||
}
|
||||
|
||||
private fun createKonanBuiltIns(sdkContext: GlobalContextImpl): KotlinBuiltIns {
|
||||
|
||||
// TODO: it depends on a random project's stdlib, propagate the actual project here
|
||||
val stdlibLocation = ProjectManager.getInstance().openProjects.asSequence().mapNotNull {
|
||||
val stdlibPath = KonanPaths.getInstance(it).konanStdlib() ?: return@mapNotNull null
|
||||
val stdlibVirtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(stdlibPath.toFile()) ?: return@mapNotNull null
|
||||
stdlibPath to stdlibVirtualFile
|
||||
}.firstOrNull()
|
||||
|
||||
if (stdlibLocation != null) {
|
||||
val library = createKonanLibrary(stdlibLocation.first.File(), KONAN_CURRENT_ABI_VERSION)
|
||||
|
||||
val builtInsModule = DefaultDeserializedDescriptorFactory.createDescriptorAndNewBuiltIns(
|
||||
library,
|
||||
LanguageVersionSettingsImpl.DEFAULT,
|
||||
sdkContext.storageManager
|
||||
)
|
||||
|
||||
return builtInsModule.builtIns
|
||||
}
|
||||
|
||||
return DefaultBuiltIns.Instance
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.analyser.index
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.ApplicationComponent
|
||||
import com.intellij.openapi.vfs.*
|
||||
import com.intellij.util.containers.ContainerUtil.createConcurrentWeakValueMap
|
||||
import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf
|
||||
import org.jetbrains.kotlin.serialization.konan.parsePackageFragment
|
||||
|
||||
class KonanDescriptorManager : ApplicationComponent {
|
||||
|
||||
companion object {
|
||||
|
||||
private const val currentAbiVersion = 1
|
||||
|
||||
@JvmStatic
|
||||
fun getInstance(): KonanDescriptorManager = ApplicationManager.getApplication().getComponent(KonanDescriptorManager::class.java)
|
||||
}
|
||||
|
||||
private val protoCache = createConcurrentWeakValueMap<VirtualFile, KonanProtoBuf.LinkDataPackageFragment>()
|
||||
|
||||
fun getCachedPackageFragment(virtualFile: VirtualFile): KonanProtoBuf.LinkDataPackageFragment {
|
||||
return protoCache.computeIfAbsent(virtualFile) {
|
||||
val bytes = virtualFile.contentsToByteArray(false)
|
||||
parsePackageFragment(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initComponent() {
|
||||
VirtualFileManager.getInstance().addVirtualFileListener(object : VirtualFileListener {
|
||||
override fun fileCreated(event: VirtualFileEvent) = invalidateCaches(event.file)
|
||||
override fun fileDeleted(event: VirtualFileEvent) = invalidateCaches(event.file)
|
||||
override fun fileMoved(event: VirtualFileMoveEvent) = invalidateCaches(event.file)
|
||||
override fun contentsChanged(event: VirtualFileEvent) = invalidateCaches(event.file)
|
||||
override fun propertyChanged(event: VirtualFilePropertyEvent) = invalidateCaches(event.file)
|
||||
})
|
||||
}
|
||||
|
||||
private fun invalidateCaches(virtualFile: VirtualFile) {
|
||||
protoCache.remove(virtualFile)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.analyser.index;
|
||||
|
||||
import org.jetbrains.kotlin.idea.util.KotlinBinaryExtension;
|
||||
|
||||
public class KonanMetaBinary extends KotlinBinaryExtension {
|
||||
public KonanMetaBinary() {
|
||||
super(KonanMetaFileType.INSTANCE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.analyser.index
|
||||
|
||||
import com.intellij.util.indexing.FileBasedIndex
|
||||
import org.jetbrains.kotlin.idea.vfilefinder.KotlinFileIndexBase
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
class KonanMetaFileIndex
|
||||
: KotlinFileIndexBase<KonanMetaFileIndex>(KonanMetaFileIndex::class.java) {
|
||||
|
||||
companion object {
|
||||
private const val VERSION = 4
|
||||
}
|
||||
|
||||
/*todo: check version?!*/
|
||||
private val dataIndexer = indexer { fileContent ->
|
||||
val fragment = KonanDescriptorManager.getInstance().getCachedPackageFragment(fileContent.file)
|
||||
FqName(fragment.fqName)
|
||||
}
|
||||
|
||||
// this is to express intention to index all Kotlin/Native metadata files irrespectively to file size
|
||||
override fun getFileTypesWithSizeLimitNotApplicable() = listOf(KonanMetaFileType)
|
||||
|
||||
override fun getInputFilter() = FileBasedIndex.InputFilter { it.fileType === KonanMetaFileType }
|
||||
|
||||
override fun getIndexer() = dataIndexer
|
||||
|
||||
override fun getVersion() = VERSION
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.analyser.index
|
||||
|
||||
import com.intellij.openapi.fileTypes.FileType
|
||||
import com.intellij.openapi.fileTypes.FileTypeConsumer
|
||||
import com.intellij.openapi.fileTypes.FileTypeFactory
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
|
||||
import org.jetbrains.kotlin.resolve.konan.platform.KonanPlatform
|
||||
import org.jetbrains.kotlin.serialization.konan.KonanSerializerProtocol
|
||||
import org.jetbrains.kotlin.serialization.konan.NullFlexibleTypeDeserializer
|
||||
|
||||
class KonanMetadataDecompiler : KonanMetadataDecompilerBase<KonanMetadataVersion>(
|
||||
KonanMetaFileType, KonanPlatform, KonanSerializerProtocol, NullFlexibleTypeDeserializer,
|
||||
KonanMetadataVersion.DEFAULT_INSTANCE, KonanMetadataVersion.INVALID_VERSION, KonanMetaFileType.STUB_VERSION
|
||||
) {
|
||||
|
||||
override fun doReadFile(file: VirtualFile): FileWithMetadata? {
|
||||
val proto = KonanDescriptorManager.getInstance().getCachedPackageFragment(file)
|
||||
return FileWithMetadata.Compatible(proto, KonanSerializerProtocol) //todo: check version compatibility
|
||||
}
|
||||
}
|
||||
|
||||
class KonanMetadataVersion(vararg numbers: Int) : BinaryVersion(*numbers) {
|
||||
override fun isCompatible(): Boolean = true //todo: ?
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val DEFAULT_INSTANCE = KonanMetadataVersion(1, 1, 0)
|
||||
|
||||
@JvmField
|
||||
val INVALID_VERSION = KonanMetadataVersion()
|
||||
}
|
||||
}
|
||||
|
||||
object KonanMetaFileType : FileType {
|
||||
override fun getName() = "KNM"
|
||||
override fun getDescription() = "Kotlin/Native Metadata"
|
||||
override fun getDefaultExtension() = "knm"
|
||||
override fun getIcon() = null
|
||||
override fun isBinary() = true
|
||||
override fun isReadOnly() = true
|
||||
override fun getCharset(file: VirtualFile, content: ByteArray) = null
|
||||
|
||||
const val STUB_VERSION = 2
|
||||
}
|
||||
|
||||
class KonanMetaFileTypeFactory : FileTypeFactory() {
|
||||
|
||||
override fun createFileTypes(consumer: FileTypeConsumer) = consumer.consume(KonanMetaFileType, KonanMetaFileType.defaultExtension)
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.analyser.index
|
||||
|
||||
import com.intellij.openapi.fileTypes.FileType
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.compiled.ClassFileDecompilers
|
||||
import org.jetbrains.konan.KonanDecompiledFile
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.idea.decompiler.KotlinDecompiledFileViewProvider
|
||||
import org.jetbrains.kotlin.idea.decompiler.common.createIncompatibleAbiVersionDecompiledText
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.buildDecompiledText
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
|
||||
import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl
|
||||
import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.serialization.SerializerExtensionProtocol
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ClassDeserializer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.FlexibleTypeDeserializer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.getClassId
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.io.IOException
|
||||
|
||||
//todo: Fix in Kotlin plugin
|
||||
abstract class KonanMetadataDecompilerBase<out V : BinaryVersion>(
|
||||
private val fileType: FileType,
|
||||
private val targetPlatform: TargetPlatform,
|
||||
private val serializerProtocol: SerializerExtensionProtocol,
|
||||
private val flexibleTypeDeserializer: FlexibleTypeDeserializer,
|
||||
private val expectedBinaryVersion: V,
|
||||
private val invalidBinaryVersion: V,
|
||||
stubVersion: Int
|
||||
) : ClassFileDecompilers.Full() {
|
||||
|
||||
private val stubBuilder = KonanMetadataStubBuilder(stubVersion, fileType, serializerProtocol, ::readFileSafely)
|
||||
|
||||
private val renderer = DescriptorRenderer.withOptions { defaultDecompilerRendererOptions() }
|
||||
|
||||
protected abstract fun doReadFile(file: VirtualFile): FileWithMetadata?
|
||||
|
||||
override fun accepts(file: VirtualFile) = file.fileType == fileType
|
||||
|
||||
override fun getStubBuilder() = stubBuilder
|
||||
|
||||
override fun createFileViewProvider(file: VirtualFile, manager: PsiManager, physical: Boolean) =
|
||||
KotlinDecompiledFileViewProvider(manager, file, physical) { provider -> KonanDecompiledFile(provider, ::buildDecompiledText) }
|
||||
|
||||
private fun readFileSafely(file: VirtualFile): FileWithMetadata? {
|
||||
if (!file.isValid) return null
|
||||
|
||||
return try {
|
||||
doReadFile(file)
|
||||
} catch (e: IOException) {
|
||||
// This is needed because sometimes we're given VirtualFile instances that point to non-existent .jar entries.
|
||||
// Such files are valid (isValid() returns true), but an attempt to read their contents results in a FileNotFoundException.
|
||||
// Note that although calling "refresh()" instead of catching an exception would seem more correct here,
|
||||
// it's not always allowed and also is likely to degrade performance
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildDecompiledText(virtualFile: VirtualFile): DecompiledText {
|
||||
assert(virtualFile.fileType == fileType) { "Unexpected file type ${virtualFile.fileType}" }
|
||||
|
||||
val file = readFileSafely(virtualFile)
|
||||
|
||||
return when (file) {
|
||||
is FileWithMetadata.Incompatible -> createIncompatibleAbiVersionDecompiledText(expectedBinaryVersion, file.version)
|
||||
is FileWithMetadata.Compatible -> decompiledText(file, targetPlatform, serializerProtocol, flexibleTypeDeserializer, renderer)
|
||||
null -> createIncompatibleAbiVersionDecompiledText(expectedBinaryVersion, invalidBinaryVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class FileWithMetadata {
|
||||
class Incompatible(val version: BinaryVersion) : FileWithMetadata()
|
||||
|
||||
open class Compatible(
|
||||
val proto: KonanProtoBuf.LinkDataPackageFragment,
|
||||
serializerProtocol: SerializerExtensionProtocol
|
||||
) : FileWithMetadata() {
|
||||
val nameResolver = NameResolverImpl(proto.stringTable, proto.nameTable)
|
||||
val packageFqName = FqName(nameResolver.getPackageFqName(proto.`package`.getExtension(serializerProtocol.packageFqName)))
|
||||
|
||||
open val classesToDecompile: List<ProtoBuf.Class> =
|
||||
proto.classes.classesList.filter { proto ->
|
||||
val classId = nameResolver.getClassId(proto.fqName)
|
||||
!classId.isNestedClass && classId !in ClassDeserializer.BLACK_LIST
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//todo: this function is extracted for KonanMetadataStubBuilder, that's the difference from Big Kotlin.
|
||||
fun decompiledText(
|
||||
file: FileWithMetadata.Compatible, targetPlatform: TargetPlatform,
|
||||
serializerProtocol: SerializerExtensionProtocol,
|
||||
flexibleTypeDeserializer: FlexibleTypeDeserializer,
|
||||
renderer: DescriptorRenderer
|
||||
): DecompiledText {
|
||||
val packageFqName = file.packageFqName
|
||||
val resolver = KonanMetadataDeserializerForDecompiler(
|
||||
packageFqName, file.proto, file.nameResolver,
|
||||
targetPlatform, serializerProtocol, flexibleTypeDeserializer
|
||||
)
|
||||
val declarations = arrayListOf<DeclarationDescriptor>()
|
||||
declarations.addAll(resolver.resolveDeclarationsInFacade(packageFqName))
|
||||
for (classProto in file.classesToDecompile) {
|
||||
val classId = file.nameResolver.getClassId(classProto.fqName)
|
||||
declarations.addIfNotNull(resolver.resolveTopLevelClass(classId))
|
||||
}
|
||||
return buildDecompiledText(packageFqName, declarations, renderer)
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.analyser.index
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import org.jetbrains.konan.createLoggingErrorReporter
|
||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.NotFoundClasses
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DeserializerForDecompilerBase
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.ResolveEverythingToKotlinAnyLocalClassifierResolver
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
|
||||
import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.serialization.SerializerExtensionProtocol
|
||||
import org.jetbrains.kotlin.serialization.deserialization.*
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope
|
||||
|
||||
//todo: Fix in Kotlin plugin
|
||||
class KonanMetadataDeserializerForDecompiler(
|
||||
packageFqName: FqName,
|
||||
private val proto: KonanProtoBuf.LinkDataPackageFragment,
|
||||
private val nameResolver: NameResolver,
|
||||
override val targetPlatform: TargetPlatform,
|
||||
serializerProtocol: SerializerExtensionProtocol,
|
||||
flexibleTypeDeserializer: FlexibleTypeDeserializer
|
||||
) : DeserializerForDecompilerBase(packageFqName) {
|
||||
override val builtIns: KotlinBuiltIns get() = DefaultBuiltIns.Instance
|
||||
|
||||
override val deserializationComponents: DeserializationComponents
|
||||
|
||||
init {
|
||||
val notFoundClasses = NotFoundClasses(storageManager, moduleDescriptor)
|
||||
|
||||
deserializationComponents = DeserializationComponents(
|
||||
storageManager, moduleDescriptor, DeserializationConfiguration.Default, KonanProtoBasedClassDataFinder(proto, nameResolver),
|
||||
AnnotationAndConstantLoaderImpl(moduleDescriptor, notFoundClasses, serializerProtocol), packageFragmentProvider,
|
||||
ResolveEverythingToKotlinAnyLocalClassifierResolver(builtIns), createLoggingErrorReporter(LOG),
|
||||
LookupTracker.DO_NOTHING, flexibleTypeDeserializer, emptyList(), notFoundClasses, ContractDeserializer.DEFAULT,
|
||||
extensionRegistryLite = serializerProtocol.extensionRegistry
|
||||
)
|
||||
}
|
||||
|
||||
override fun resolveDeclarationsInFacade(facadeFqName: FqName): List<DeclarationDescriptor> {
|
||||
assert(facadeFqName == directoryPackageFqName) {
|
||||
"Was called for $facadeFqName; only members of $directoryPackageFqName package are expected."
|
||||
}
|
||||
|
||||
val membersScope = DeserializedPackageMemberScope(
|
||||
createDummyPackageFragment(facadeFqName),
|
||||
proto.`package`,
|
||||
nameResolver,
|
||||
KonanMetadataVersion.DEFAULT_INSTANCE,
|
||||
containerSource = null,
|
||||
components = deserializationComponents
|
||||
) { emptyList() }
|
||||
|
||||
return membersScope.getContributedDescriptors().toList()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance(KonanMetadataDeserializerForDecompiler::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.analyser.index
|
||||
|
||||
import com.intellij.openapi.fileTypes.FileType
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.compiled.ClsStubBuilder
|
||||
import com.intellij.psi.impl.compiled.ClassFileStubBuilder
|
||||
import com.intellij.psi.stubs.PsiFileStub
|
||||
import com.intellij.util.indexing.FileContent
|
||||
import org.jetbrains.konan.createFileStub
|
||||
import org.jetbrains.kotlin.idea.decompiler.stubBuilder.createIncompatibleAbiVersionFileStub
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.konan.platform.KonanPlatform
|
||||
import org.jetbrains.kotlin.serialization.SerializerExtensionProtocol
|
||||
import org.jetbrains.kotlin.serialization.konan.NullFlexibleTypeDeserializer
|
||||
|
||||
//todo: Fix in Kotlin plugin
|
||||
open class KonanMetadataStubBuilder(
|
||||
private val version: Int,
|
||||
private val fileType: FileType,
|
||||
private val serializerProtocol: SerializerExtensionProtocol,
|
||||
private val readFile: (VirtualFile) -> FileWithMetadata?
|
||||
) : ClsStubBuilder() {
|
||||
|
||||
override fun getStubVersion() = ClassFileStubBuilder.STUB_VERSION + version
|
||||
|
||||
override fun buildFileStub(content: FileContent): PsiFileStub<*>? {
|
||||
val virtualFile = content.file
|
||||
assert(virtualFile.fileType == fileType) { "Unexpected file type ${virtualFile.fileType}" }
|
||||
|
||||
val file = readFile(virtualFile) ?: return null
|
||||
|
||||
return when (file) {
|
||||
is FileWithMetadata.Incompatible -> createIncompatibleAbiVersionFileStub()
|
||||
is FileWithMetadata.Compatible -> { //todo: this part is implemented in our own way
|
||||
val renderer = DescriptorRenderer.withOptions { defaultDecompilerRendererOptions() }
|
||||
val ktFileText = decompiledText(file, KonanPlatform, serializerProtocol, NullFlexibleTypeDeserializer, renderer)
|
||||
createFileStub(content.project, ktFileText.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.analyser.index
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.SourceElement
|
||||
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
|
||||
import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ClassData
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ClassDataFinder
|
||||
import org.jetbrains.kotlin.serialization.deserialization.getClassId
|
||||
|
||||
//todo: Fix in Kotlin plugin
|
||||
class KonanProtoBasedClassDataFinder(
|
||||
proto: KonanProtoBuf.LinkDataPackageFragment,
|
||||
private val nameResolver: NameResolver,
|
||||
private val classSource: (ClassId) -> SourceElement = { SourceElement.NO_SOURCE }
|
||||
) : ClassDataFinder {
|
||||
private val classIdToProto =
|
||||
proto.classes.classesList.associateBy { klass ->
|
||||
nameResolver.getClassId(klass.fqName)
|
||||
}
|
||||
|
||||
internal val allClassIds: Collection<ClassId> get() = classIdToProto.keys
|
||||
|
||||
override fun findClassData(classId: ClassId): ClassData? {
|
||||
val classProto = classIdToProto[classId] ?: return null
|
||||
return ClassData(nameResolver, classProto, KonanMetadataVersion.DEFAULT_INSTANCE, classSource(classId))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.settings
|
||||
|
||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind.*
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import java.nio.file.Path
|
||||
|
||||
data class KonanArtifact(
|
||||
val targetName: String,
|
||||
val moduleName: String,
|
||||
val type: CompilerOutputKind,
|
||||
val target: KonanTarget?,
|
||||
val libraryDependencies: List<String>,
|
||||
val sources: List<Path>,
|
||||
val output: Path
|
||||
)
|
||||
|
||||
val CompilerOutputKind.isLibrary: Boolean get() = this == LIBRARY || this == DYNAMIC || this == STATIC || this == FRAMEWORK
|
||||
val CompilerOutputKind.isExecutable: Boolean get() = this == PROGRAM
|
||||
val CompilerOutputKind.isTest: Boolean get() = false //TODO
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.settings;
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.messages.Topic;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
|
||||
@ApiStatus.Experimental
|
||||
public interface KonanModelProvider {
|
||||
Topic<Runnable> RELOAD_TOPIC = new Topic<>("Kotlin/Native Project Model Updater", Runnable.class);
|
||||
|
||||
ExtensionPointName<KonanModelProvider> EP_NAME = ExtensionPointName.create("org.jetbrains.kotlin.native.konanModelProvider");
|
||||
|
||||
@NotNull
|
||||
Collection<KonanArtifact> getArtifacts(@NotNull Project project);
|
||||
|
||||
@Nullable
|
||||
Path getKonanHome(@NotNull Project project);
|
||||
|
||||
boolean reloadLibraries(@NotNull Project project, @NotNull Collection<Path> libraryPaths);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.settings
|
||||
|
||||
import com.intellij.openapi.components.ProjectComponent
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.io.exists
|
||||
import com.intellij.util.io.isDirectory
|
||||
import org.jetbrains.konan.KotlinNativeToolchain
|
||||
import org.jetbrains.kotlin.konan.library.*
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.stream.Collectors
|
||||
|
||||
open class KonanPaths(protected val project: Project) : ProjectComponent {
|
||||
companion object {
|
||||
fun getInstance(project: Project): KonanPaths = project.getComponent(KonanPaths::class.java)
|
||||
|
||||
fun bundledKonanDist(): Path = KotlinNativeToolchain.BUNDLED.baseDir
|
||||
}
|
||||
|
||||
override fun getComponentName() = "Kotlin/Native Compiler Paths"
|
||||
|
||||
fun konanStdlib(): Path? = konanDist()?.resolve(konanCommonLibraryPath(KONAN_STDLIB_NAME))
|
||||
|
||||
open fun konanDist(): Path? {
|
||||
for (provider in KonanModelProvider.EP_NAME.extensions) {
|
||||
return provider.getKonanHome(project)
|
||||
}
|
||||
return bundledKonanDist()
|
||||
}
|
||||
|
||||
open fun libraryPaths(): Set<Path> = emptySet()
|
||||
|
||||
fun konanPlatformLibraries(): List<Path> {
|
||||
|
||||
val resolvedTargetName = HostManager.resolveAlias(target().name)
|
||||
val klibPath =
|
||||
konanDist()?.resolve(konanSpecificPlatformLibrariesPath(resolvedTargetName))?.takeIf { it.exists() } ?: return emptyList()
|
||||
|
||||
return Files.walk(klibPath, 1).filter {
|
||||
it.isDirectory() && it.fileName.toString() != KONAN_STDLIB_NAME && it != klibPath
|
||||
}.collect(Collectors.toList())
|
||||
}
|
||||
|
||||
//todo: this is wrong, we are not allowing multiple targets in project
|
||||
open fun target(): KonanTarget = HostManager.host
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.konan.settings
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.runWriteAction
|
||||
import com.intellij.openapi.components.ProjectComponent
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.roots.libraries.LibraryTable
|
||||
import com.intellij.openapi.vfs.*
|
||||
import org.jetbrains.konan.KotlinNativeToolchain
|
||||
import org.jetbrains.konan.settings.KonanModelProvider.RELOAD_TOPIC
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.nio.file.Path
|
||||
|
||||
abstract class KonanProjectComponent(val project: Project) : ProjectComponent {
|
||||
companion object {
|
||||
fun getInstance(project: Project): KonanProjectComponent = project.getComponent(KonanProjectComponent::class.java)
|
||||
}
|
||||
|
||||
protected var libraryPaths: Set<String> = emptySet()
|
||||
|
||||
override fun getComponentName(): String = "Kotlin/Native Project Component"
|
||||
|
||||
override fun projectOpened() {
|
||||
VirtualFileManager.getInstance().addVirtualFileListener(object : VirtualFileListener {
|
||||
override fun fileCreated(event: VirtualFileEvent) {
|
||||
val filePath = event.file.path
|
||||
if (libraryPaths.contains(filePath)) reloadLibraries()
|
||||
}
|
||||
}, project)
|
||||
|
||||
val connection = project.messageBus.connect(project)
|
||||
connection.subscribe(RELOAD_TOPIC, Runnable {
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
KotlinNativeToolchain.BUNDLED.ensureExists(project)
|
||||
reloadLibraries()
|
||||
}
|
||||
})
|
||||
|
||||
if (looksLikeKotlinNativeProject()) {
|
||||
reloadLibraries()
|
||||
}
|
||||
}
|
||||
|
||||
open fun reloadLibraries(): Unit = synchronized(this) {
|
||||
val libraryPaths: Set<Path> = collectLibraryPaths()
|
||||
this.libraryPaths = libraryPaths.asSequence().map(Path::toString).toSet()
|
||||
|
||||
// for (konanModelProvider in KonanModelProvider.EP_NAME.extensions) {
|
||||
// if (konanModelProvider.reloadLibraries(project, libraryPaths)) return
|
||||
// }
|
||||
|
||||
val module = ModuleManager.getInstance(project).modules.firstOrNull() ?: return
|
||||
val modifiableModel = ModuleRootManager.getInstance(module).modifiableModel
|
||||
val libraryTable: LibraryTable = modifiableModel.moduleLibraryTable
|
||||
libraryTable.libraries.forEach { libraryTable.removeLibrary(it) }
|
||||
|
||||
val localFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
|
||||
val jarFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JAR_PROTOCOL)
|
||||
|
||||
for (path in libraryPaths) {
|
||||
|
||||
fun createLibrary(vfs: VirtualFileSystem, rootPath: String) {
|
||||
val root = vfs.refreshAndFindFileByPath(rootPath) ?: return
|
||||
|
||||
val library = libraryTable.createLibrary(path.fileName.toString())
|
||||
val libraryModifiableModel = library.modifiableModel
|
||||
|
||||
libraryModifiableModel.addRoot(root, OrderRootType.CLASSES)
|
||||
|
||||
runWriteAction {
|
||||
libraryModifiableModel.commit()
|
||||
}
|
||||
}
|
||||
|
||||
val libraryRootPath = path.toString()
|
||||
val libraryRoot: VirtualFile = localFileSystem.refreshAndFindFileByPath(libraryRootPath) ?: continue
|
||||
|
||||
// use JAR FS if this is file (*.klib), otherwise - local FS
|
||||
createLibrary(if (libraryRoot.isDirectory) localFileSystem else jarFileSystem, libraryRootPath)
|
||||
}
|
||||
|
||||
runWriteAction {
|
||||
modifiableModel.commit()
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun collectLibraryPaths(): MutableSet<Path> {
|
||||
val konanPaths = KonanPaths.getInstance(project)
|
||||
return mutableSetOf<Path>().apply {
|
||||
addIfNotNull(konanPaths.konanStdlib())
|
||||
addAll(konanPaths.konanPlatformLibraries())
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun looksLikeKotlinNativeProject(): Boolean
|
||||
}
|
||||
@@ -10,4 +10,31 @@
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin">
|
||||
<buildSystemTypeDetector implementation="org.jetbrains.kotlin.idea.configuration.GradleDetector"/>
|
||||
</extensions>
|
||||
|
||||
<!-- NATIVE PART -->
|
||||
<application-components>
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.konan.gradle.internal.KotlinNativeIdeInitializer</implementation-class>
|
||||
</component>
|
||||
</application-components>
|
||||
|
||||
<project-components>
|
||||
<component>
|
||||
<interface-class>org.jetbrains.konan.settings.KonanProjectComponent</interface-class>
|
||||
<implementation-class>org.jetbrains.konan.gradle.GradleKonanProjectComponent</implementation-class>
|
||||
</component>
|
||||
</project-components>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin.native">
|
||||
<konanModelProvider implementation="org.jetbrains.konan.gradle.GradleKonanModelProvider"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.plugins.gradle">
|
||||
<projectResolve implementation="org.jetbrains.konan.gradle.KonanProjectResolver"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<externalProjectDataService implementation="org.jetbrains.konan.gradle.KonanProjectDataService"/>
|
||||
</extensions>
|
||||
<!-- /NATIVE PART -->
|
||||
</idea-plugin>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<application-components>
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.konan.KonanApplicationComponent</implementation-class>
|
||||
</component>
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.konan.analyser.index.KonanDescriptorManager</implementation-class>
|
||||
</component>
|
||||
</application-components>
|
||||
|
||||
<project-components>
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.konan.settings.KonanPaths</implementation-class>
|
||||
</component>
|
||||
</project-components>
|
||||
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.native.konanModelProvider" interface="org.jetbrains.konan.settings.KonanModelProvider"/>
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<postStartupActivity implementation="org.jetbrains.konan.SetupKotlinNativeStartupActivity"/>
|
||||
<psi.classFileDecompiler implementation="org.jetbrains.konan.analyser.index.KonanMetadataDecompiler"/>
|
||||
|
||||
<fileType.fileViewProviderFactory filetype="KNM"
|
||||
implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||
<filetype.stubBuilder filetype="KNM" implementationClass="com.intellij.psi.impl.compiled.ClassFileStubBuilder"/>
|
||||
<filetype.decompiler filetype="KNM" implementationClass="com.intellij.psi.impl.compiled.ClassFileDecompiler"/>
|
||||
<fileTypeFactory implementation="org.jetbrains.konan.analyser.index.KonanMetaFileTypeFactory"/>
|
||||
<fileBasedIndex implementation="org.jetbrains.konan.analyser.index.KonanMetaFileIndex"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin">
|
||||
<binaryExtension implementation="org.jetbrains.konan.analyser.index.KonanMetaBinary"/>
|
||||
<idePlatformSupport implementation="org.jetbrains.konan.analyser.KonanPlatformSupport" order="first"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -3097,6 +3097,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
||||
<xi:include href="jvm.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<!-- CIDR-PLUGIN-EXCLUDE-END -->
|
||||
|
||||
<xi:include href="native.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<xi:include href="tipsAndTricks.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<xi:include href="extensions/ide.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
@@ -3097,6 +3097,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
||||
<xi:include href="jvm.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<!-- CIDR-PLUGIN-EXCLUDE-END -->
|
||||
|
||||
<xi:include href="native.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<xi:include href="tipsAndTricks.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<xi:include href="extensions/ide.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
@@ -3095,6 +3095,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
||||
<xi:include href="jvm.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<!-- CIDR-PLUGIN-EXCLUDE-END -->
|
||||
|
||||
<xi:include href="native.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<xi:include href="tipsAndTricks.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<xi:include href="extensions/ide.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
@@ -3097,6 +3097,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
||||
<xi:include href="jvm.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<!-- CIDR-PLUGIN-EXCLUDE-END -->
|
||||
|
||||
<xi:include href="native.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<xi:include href="tipsAndTricks.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<xi:include href="extensions/ide.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
+5
-2
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.serialization.konan
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.konan.KonanModuleDescriptorFactory
|
||||
import org.jetbrains.kotlin.konan.library.KonanLibrary
|
||||
@@ -24,7 +25,8 @@ interface KonanDeserializedModuleDescriptorFactory {
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
storageManager: StorageManager,
|
||||
builtIns: KotlinBuiltIns,
|
||||
packageAccessedHandler: PackageAccessedHandler? = null
|
||||
packageAccessedHandler: PackageAccessedHandler? = null,
|
||||
customCapabilities: Map<ModuleDescriptor.Capability<*>, Any?> = emptyMap()
|
||||
): ModuleDescriptorImpl
|
||||
|
||||
/**
|
||||
@@ -35,6 +37,7 @@ interface KonanDeserializedModuleDescriptorFactory {
|
||||
library: KonanLibrary,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
storageManager: StorageManager,
|
||||
packageAccessedHandler: PackageAccessedHandler? = null
|
||||
packageAccessedHandler: PackageAccessedHandler? = null,
|
||||
customCapabilities: Map<ModuleDescriptor.Capability<*>, Any?> = emptyMap()
|
||||
): ModuleDescriptorImpl
|
||||
}
|
||||
|
||||
+3
-1
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.serialization.konan
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.konan.library.KonanLibrary
|
||||
import org.jetbrains.kotlin.konan.library.resolver.KonanLibraryResolveResult
|
||||
@@ -33,7 +34,8 @@ interface KonanResolvedModuleDescriptorsFactory {
|
||||
storageManager: StorageManager,
|
||||
builtIns: KotlinBuiltIns?,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
customAction: ((KonanLibrary, ModuleDescriptorImpl) -> Unit)? = null
|
||||
customAction: ((KonanLibrary, ModuleDescriptorImpl) -> Unit)? = null,
|
||||
customCapabilitiesGenerator: ((KonanLibrary) -> Map<ModuleDescriptor.Capability<*>, Any?>)? = null
|
||||
): KonanResolvedModuleDescriptors
|
||||
}
|
||||
|
||||
|
||||
+17
-7
@@ -33,22 +33,32 @@ internal class KonanDeserializedModuleDescriptorFactoryImpl(
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
storageManager: StorageManager,
|
||||
builtIns: KotlinBuiltIns,
|
||||
packageAccessedHandler: PackageAccessedHandler?
|
||||
) = createDescriptorOptionalBuiltIns(library, languageVersionSettings, storageManager, builtIns, packageAccessedHandler)
|
||||
packageAccessedHandler: PackageAccessedHandler?,
|
||||
customCapabilities: Map<ModuleDescriptor.Capability<*>, Any?>
|
||||
) = createDescriptorOptionalBuiltIns(
|
||||
library,
|
||||
languageVersionSettings,
|
||||
storageManager,
|
||||
builtIns,
|
||||
packageAccessedHandler,
|
||||
customCapabilities
|
||||
)
|
||||
|
||||
override fun createDescriptorAndNewBuiltIns(
|
||||
library: KonanLibrary,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
storageManager: StorageManager,
|
||||
packageAccessedHandler: PackageAccessedHandler?
|
||||
) = createDescriptorOptionalBuiltIns(library, languageVersionSettings, storageManager, null, packageAccessedHandler)
|
||||
packageAccessedHandler: PackageAccessedHandler?,
|
||||
customCapabilities: Map<ModuleDescriptor.Capability<*>, Any?>
|
||||
) = createDescriptorOptionalBuiltIns(library, languageVersionSettings, storageManager, null, packageAccessedHandler, customCapabilities)
|
||||
|
||||
private fun createDescriptorOptionalBuiltIns(
|
||||
library: KonanLibrary,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
storageManager: StorageManager,
|
||||
builtIns: KotlinBuiltIns?,
|
||||
packageAccessedHandler: PackageAccessedHandler?
|
||||
packageAccessedHandler: PackageAccessedHandler?,
|
||||
customCapabilities: Map<ModuleDescriptor.Capability<*>, Any?>
|
||||
): ModuleDescriptorImpl {
|
||||
|
||||
val libraryProto = parseModuleHeader(library.moduleHeaderData)
|
||||
@@ -57,9 +67,9 @@ internal class KonanDeserializedModuleDescriptorFactoryImpl(
|
||||
val moduleOrigin = DeserializedKonanModuleOrigin(library)
|
||||
|
||||
val moduleDescriptor = if (builtIns != null)
|
||||
descriptorFactory.createDescriptor(moduleName, storageManager, builtIns, moduleOrigin)
|
||||
descriptorFactory.createDescriptor(moduleName, storageManager, builtIns, moduleOrigin, customCapabilities)
|
||||
else
|
||||
descriptorFactory.createDescriptorAndNewBuiltIns(moduleName, storageManager, moduleOrigin)
|
||||
descriptorFactory.createDescriptorAndNewBuiltIns(moduleName, storageManager, moduleOrigin, customCapabilities)
|
||||
|
||||
val deserializationConfiguration = CompilerDeserializationConfiguration(languageVersionSettings)
|
||||
|
||||
|
||||
+22
-5
@@ -41,7 +41,8 @@ class KonanResolvedModuleDescriptorsFactoryImpl(
|
||||
storageManager: StorageManager,
|
||||
builtIns: KotlinBuiltIns?,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
customAction: ((KonanLibrary, ModuleDescriptorImpl) -> Unit)?
|
||||
customAction: ((KonanLibrary, ModuleDescriptorImpl) -> Unit)?,
|
||||
customCapabilitiesGenerator: ((KonanLibrary) -> Map<ModuleDescriptor.Capability<*>, Any?>)?
|
||||
): KonanResolvedModuleDescriptors {
|
||||
|
||||
val moduleDescriptors = mutableListOf<ModuleDescriptorImpl>()
|
||||
@@ -52,9 +53,11 @@ class KonanResolvedModuleDescriptorsFactoryImpl(
|
||||
resolvedLibraries.forEach { library, packageAccessedHandler ->
|
||||
profile("Loading ${library.libraryName}") {
|
||||
|
||||
val customCapabilities = customCapabilitiesGenerator?.invoke(library) ?: emptyMap()
|
||||
|
||||
// MutableModuleContext needs ModuleDescriptorImpl, rather than ModuleDescriptor.
|
||||
val moduleDescriptor = createDescriptorOptionalBuiltsIns(
|
||||
library, languageVersionSettings, storageManager, builtIns, packageAccessedHandler
|
||||
library, languageVersionSettings, storageManager, builtIns, packageAccessedHandler, customCapabilities
|
||||
)
|
||||
builtIns = moduleDescriptor.builtIns
|
||||
moduleDescriptors.add(moduleDescriptor)
|
||||
@@ -120,11 +123,25 @@ class KonanResolvedModuleDescriptorsFactoryImpl(
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
storageManager: StorageManager,
|
||||
builtIns: KotlinBuiltIns?,
|
||||
packageAccessedHandler: PackageAccessedHandler?
|
||||
packageAccessedHandler: PackageAccessedHandler?,
|
||||
customCapabilities: Map<ModuleDescriptor.Capability<*>, Any?>
|
||||
) = if (builtIns != null)
|
||||
moduleDescriptorFactory.createDescriptor(library, languageVersionSettings, storageManager, builtIns, packageAccessedHandler)
|
||||
moduleDescriptorFactory.createDescriptor(
|
||||
library,
|
||||
languageVersionSettings,
|
||||
storageManager,
|
||||
builtIns,
|
||||
packageAccessedHandler,
|
||||
customCapabilities
|
||||
)
|
||||
else
|
||||
moduleDescriptorFactory.createDescriptorAndNewBuiltIns(library, languageVersionSettings, storageManager, packageAccessedHandler)
|
||||
moduleDescriptorFactory.createDescriptorAndNewBuiltIns(
|
||||
library,
|
||||
languageVersionSettings,
|
||||
storageManager,
|
||||
packageAccessedHandler,
|
||||
customCapabilities
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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
|
||||
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
const val KLIB_FILE_EXTENSION = "klib"
|
||||
const val KLIB_FILE_EXTENSION_WITH_DOT = ".$KLIB_FILE_EXTENSION"
|
||||
|
||||
const val KONAN_STDLIB_NAME = "stdlib"
|
||||
|
||||
val KONAN_COMMON_LIBS_PATH: Path
|
||||
get() = Paths.get("klib", "common")
|
||||
|
||||
val KONAN_ALL_PLATFORM_LIBS_PATH: Path
|
||||
get() = Paths.get("klib", "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)
|
||||
@@ -8,17 +8,12 @@ package org.jetbrains.kotlin.konan.library
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
|
||||
const val KLIB_FILE_EXTENSION = "klib"
|
||||
const val KLIB_FILE_EXTENSION_WITH_DOT = ".$KLIB_FILE_EXTENSION"
|
||||
|
||||
const val KONAN_STDLIB_NAME = "stdlib"
|
||||
|
||||
interface SearchPathResolver {
|
||||
val searchRoots: List<File>
|
||||
fun resolve(givenPath: String): File
|
||||
fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List<File>
|
||||
}
|
||||
|
||||
interface SearchPathResolverWithTarget: SearchPathResolver {
|
||||
interface SearchPathResolverWithTarget : SearchPathResolver {
|
||||
val target: KonanTarget
|
||||
}
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.util
|
||||
|
||||
import java.io.*
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.net.URLConnection
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.StandardCopyOption
|
||||
import java.util.concurrent.*
|
||||
|
||||
internal typealias ProgressCallback = (url: String, currentBytes: Long, totalBytes: Long) -> Unit
|
||||
|
||||
internal class DependencyDownloader(
|
||||
var maxAttempts: Int = DEFAULT_MAX_ATTEMPTS,
|
||||
var attemptIntervalMs: Long = DEFAULT_ATTEMPT_INTERVAL_MS,
|
||||
customProgressCallback: ProgressCallback? = null
|
||||
) {
|
||||
|
||||
private val progressCallback = customProgressCallback ?: { url, currentBytes, totalBytes ->
|
||||
print("\rDownloading dependency: $url (${currentBytes.humanReadable}/${totalBytes.humanReadable}). ")
|
||||
}
|
||||
|
||||
val executor = ExecutorCompletionService<Unit>(Executors.newSingleThreadExecutor { r ->
|
||||
val thread = Thread(r)
|
||||
thread.name = "konan-dependency-downloader"
|
||||
thread.isDaemon = true
|
||||
|
||||
thread
|
||||
})
|
||||
|
||||
enum class ReplacingMode {
|
||||
/** Re-download the file and replace the existing one. */
|
||||
REPLACE,
|
||||
/** Throw FileAlreadyExistsException */
|
||||
THROW,
|
||||
/** Don't download the file and return the existing one*/
|
||||
RETURN_EXISTING
|
||||
}
|
||||
|
||||
class DownloadingProgress(@Volatile var currentBytes: Long) {
|
||||
fun update(readBytes: Int) {
|
||||
currentBytes += readBytes
|
||||
}
|
||||
}
|
||||
|
||||
private fun HttpURLConnection.checkHTTPResponse(expected: Int, originalUrl: URL = url) {
|
||||
if (responseCode != expected) {
|
||||
throw DependencyDownloaderHTTPResponseException(originalUrl, responseCode)
|
||||
}
|
||||
}
|
||||
|
||||
private fun HttpURLConnection.checkHTTPResponse(originalUrl: URL, predicate: (Int) -> Boolean) {
|
||||
if (!predicate(responseCode)) {
|
||||
throw DependencyDownloaderHTTPResponseException(originalUrl, responseCode)
|
||||
}
|
||||
}
|
||||
|
||||
private fun doDownload(
|
||||
originalUrl: URL,
|
||||
connection: URLConnection,
|
||||
tmpFile: File,
|
||||
currentBytes: Long,
|
||||
totalBytes: Long,
|
||||
append: Boolean
|
||||
) {
|
||||
val progress = DownloadingProgress(currentBytes)
|
||||
|
||||
// TODO: Implement multi-thread downloading.
|
||||
executor.submit {
|
||||
connection.getInputStream().use { from ->
|
||||
FileOutputStream(tmpFile, append).use { to ->
|
||||
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
|
||||
var read = from.read(buffer)
|
||||
while (read != -1) {
|
||||
if (Thread.interrupted()) {
|
||||
throw InterruptedException()
|
||||
}
|
||||
to.write(buffer, 0, read)
|
||||
progress.update(read)
|
||||
read = from.read(buffer)
|
||||
}
|
||||
if (progress.currentBytes != totalBytes) {
|
||||
throw EOFException("The stream closed before end of downloading.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result: Future<Unit>?
|
||||
do {
|
||||
progressCallback(originalUrl.toString(), progress.currentBytes, totalBytes)
|
||||
result = executor.poll(1, TimeUnit.SECONDS)
|
||||
} while (result == null)
|
||||
progressCallback(originalUrl.toString(), progress.currentBytes, totalBytes)
|
||||
|
||||
try {
|
||||
result.get()
|
||||
} catch (e: ExecutionException) {
|
||||
throw e.cause ?: e
|
||||
}
|
||||
}
|
||||
|
||||
private fun resumeDownload(originalUrl: URL, originalConnection: HttpURLConnection, tmpFile: File) {
|
||||
originalConnection.connect()
|
||||
val totalBytes = originalConnection.contentLengthLong
|
||||
val currentBytes = tmpFile.length()
|
||||
if (currentBytes >= totalBytes || originalConnection.getHeaderField("Accept-Ranges") != "bytes") {
|
||||
// The temporary file is bigger then expected or the server doesn't support resuming downloading.
|
||||
// Download the file from scratch.
|
||||
doDownload(originalUrl, originalConnection, tmpFile, 0, totalBytes, false)
|
||||
} else {
|
||||
originalConnection.disconnect()
|
||||
val rangeConnection = originalUrl.openConnection() as HttpURLConnection
|
||||
rangeConnection.setRequestProperty("range", "bytes=$currentBytes-")
|
||||
rangeConnection.connect()
|
||||
rangeConnection.checkHTTPResponse(originalUrl) {
|
||||
it == HttpURLConnection.HTTP_PARTIAL || it == HttpURLConnection.HTTP_OK
|
||||
}
|
||||
doDownload(originalUrl, rangeConnection, tmpFile, currentBytes, totalBytes, true)
|
||||
}
|
||||
}
|
||||
|
||||
/** Performs an attempt to download a specified file into the specified location */
|
||||
private fun tryDownload(url: URL, tmpFile: File) {
|
||||
val connection = url.openConnection()
|
||||
|
||||
(connection as? HttpURLConnection)?.checkHTTPResponse(HttpURLConnection.HTTP_OK, url)
|
||||
|
||||
if (connection is HttpURLConnection && tmpFile.exists()) {
|
||||
resumeDownload(url, connection, tmpFile)
|
||||
} else {
|
||||
connection.connect()
|
||||
val totalBytes = connection.contentLengthLong
|
||||
doDownload(url, connection, tmpFile, 0, totalBytes, false)
|
||||
}
|
||||
}
|
||||
|
||||
/** Downloads a file from [source] url to [destination]. Returns [destination]. */
|
||||
fun download(
|
||||
source: URL,
|
||||
destination: File,
|
||||
replace: ReplacingMode = ReplacingMode.RETURN_EXISTING
|
||||
): File {
|
||||
|
||||
if (destination.exists()) {
|
||||
when (replace) {
|
||||
ReplacingMode.RETURN_EXISTING -> return destination
|
||||
ReplacingMode.THROW -> throw FileAlreadyExistsException(destination)
|
||||
ReplacingMode.REPLACE -> Unit // Just continue with downloading.
|
||||
}
|
||||
}
|
||||
val tmpFile = File("${destination.canonicalPath}.$TMP_SUFFIX")
|
||||
|
||||
check(!tmpFile.isDirectory) {
|
||||
"A temporary file is a directory: ${tmpFile.canonicalPath}. Remove it and try again."
|
||||
}
|
||||
check(!destination.isDirectory) {
|
||||
"The destination file is a directory: ${destination.canonicalPath}. Remove it and try again."
|
||||
}
|
||||
|
||||
var attempt = 1
|
||||
var waitTime = 0L
|
||||
while (true) {
|
||||
try {
|
||||
tryDownload(source, tmpFile)
|
||||
break
|
||||
} catch (e: DependencyDownloaderHTTPResponseException) {
|
||||
throw e
|
||||
} catch (e: IOException) {
|
||||
if (attempt >= maxAttempts) {
|
||||
throw e
|
||||
}
|
||||
attempt++
|
||||
waitTime += attemptIntervalMs
|
||||
println(
|
||||
"Cannot download a dependency: $e\n" +
|
||||
"Waiting ${waitTime.toDouble() / 1000} sec and trying again (attempt: $attempt/$maxAttempts)."
|
||||
)
|
||||
// TODO: Wait better
|
||||
Thread.sleep(waitTime)
|
||||
}
|
||||
}
|
||||
|
||||
Files.move(tmpFile.toPath(), destination.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||
println("Done.")
|
||||
return destination
|
||||
}
|
||||
|
||||
private val Long.humanReadable: String
|
||||
get() {
|
||||
if (this < 0) {
|
||||
return "-"
|
||||
}
|
||||
if (this < 1024) {
|
||||
return "$this bytes"
|
||||
}
|
||||
val exp = (Math.log(this.toDouble()) / Math.log(1024.0)).toInt()
|
||||
val prefix = "kMGTPE"[exp - 1]
|
||||
return "%.1f %siB".format(this / Math.pow(1024.0, exp.toDouble()), prefix)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_MAX_ATTEMPTS = 10
|
||||
const val DEFAULT_ATTEMPT_INTERVAL_MS = 3000L
|
||||
|
||||
const val TMP_SUFFIX = "part"
|
||||
}
|
||||
}
|
||||
|
||||
class DependencyDownloaderHTTPResponseException(val url: URL, val responseCode: Int) :
|
||||
IOException("Server returned HTTP response code: $responseCode for URL: $url")
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.util
|
||||
|
||||
import org.jetbrains.kotlin.konan.file.unzipTo
|
||||
import java.io.File
|
||||
|
||||
internal class DependencyExtractor {
|
||||
private val useZip = System.getProperty("os.name").startsWith("Windows")
|
||||
|
||||
internal val archiveExtension = if (useZip) {
|
||||
"zip"
|
||||
} else {
|
||||
"tar.gz"
|
||||
}
|
||||
|
||||
private fun extractTarGz(tarGz: File, targetDirectory: File) {
|
||||
val tarProcess = ProcessBuilder().apply {
|
||||
command("tar", "-xzf", tarGz.canonicalPath)
|
||||
directory(targetDirectory)
|
||||
}.start()
|
||||
tarProcess.waitFor()
|
||||
if (tarProcess.exitValue() != 0) {
|
||||
throw RuntimeException("Cannot extract archive with dependency: ${tarGz.canonicalPath}")
|
||||
}
|
||||
}
|
||||
|
||||
fun extract(archive: File, targetDirectory: File) {
|
||||
if (useZip) {
|
||||
archive.toPath().unzipTo(targetDirectory.toPath())
|
||||
} else {
|
||||
extractTarGz(archive, targetDirectory)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.util
|
||||
|
||||
import org.jetbrains.kotlin.konan.file.use
|
||||
import org.jetbrains.kotlin.konan.properties.Properties
|
||||
import org.jetbrains.kotlin.konan.properties.propertyList
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.RandomAccessFile
|
||||
import java.net.InetAddress
|
||||
import java.net.URL
|
||||
import java.net.UnknownHostException
|
||||
import java.nio.file.Paths
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
private val Properties.dependenciesUrl: String
|
||||
get() = getProperty("dependenciesUrl")
|
||||
?: throw IllegalStateException("No such property in konan.properties: dependenciesUrl")
|
||||
|
||||
private val Properties.airplaneMode: Boolean
|
||||
get() = getProperty("airplaneMode")?.toBoolean() ?: false
|
||||
|
||||
private val Properties.downloadingAttempts: Int
|
||||
get() = getProperty("downloadingAttempts")?.toInt()
|
||||
?: DependencyDownloader.DEFAULT_MAX_ATTEMPTS
|
||||
|
||||
private val Properties.downloadingAttemptIntervalMs: Long
|
||||
get() = getProperty("downloadingAttemptPauseMs")?.toLong()
|
||||
?: DependencyDownloader.DEFAULT_ATTEMPT_INTERVAL_MS
|
||||
|
||||
private fun Properties.findCandidates(dependencies: List<String>): Map<String, List<DependencySource>> {
|
||||
val dependencyProfiles = this.propertyList("dependencyProfiles")
|
||||
return dependencies.map { dependency ->
|
||||
dependency to dependencyProfiles.flatMap { profile ->
|
||||
val candidateSpecs = propertyList("$dependency.$profile")
|
||||
if (profile == "default" && candidateSpecs.isEmpty()) {
|
||||
listOf(DependencySource.Remote.Public)
|
||||
} else {
|
||||
candidateSpecs.map { candidateSpec ->
|
||||
when (candidateSpec) {
|
||||
"remote:public" -> DependencySource.Remote.Public
|
||||
"remote:internal" -> DependencySource.Remote.Internal
|
||||
else -> DependencySource.Local(File(candidateSpec))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
sealed class DependencySource {
|
||||
data class Local(val path: File) : DependencySource()
|
||||
|
||||
sealed class Remote : DependencySource() {
|
||||
object Public : Remote()
|
||||
object Internal : Remote()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects dependencies and downloads all the missing ones into [dependenciesDirectory] from [dependenciesUrl].
|
||||
* If [airplaneMode] is true will throw a RuntimeException instead of downloading.
|
||||
*/
|
||||
class DependencyProcessor(
|
||||
dependenciesRoot: File,
|
||||
val dependenciesUrl: String,
|
||||
dependencyToCandidates: Map<String, List<DependencySource>>,
|
||||
homeDependencyCache: File = defaultDependencyCacheDir,
|
||||
val airplaneMode: Boolean = false,
|
||||
maxAttempts: Int = DependencyDownloader.DEFAULT_MAX_ATTEMPTS,
|
||||
attemptIntervalMs: Long = DependencyDownloader.DEFAULT_ATTEMPT_INTERVAL_MS,
|
||||
customProgressCallback: ProgressCallback? = null,
|
||||
val keepUnstable: Boolean = true
|
||||
) {
|
||||
|
||||
val dependenciesDirectory = dependenciesRoot.apply { mkdirs() }
|
||||
val cacheDirectory = homeDependencyCache.apply { mkdirs() }
|
||||
|
||||
val lockFile = File(cacheDirectory, ".lock").apply { if (!exists()) createNewFile() }
|
||||
|
||||
var showInfo = true
|
||||
private var isInfoShown = false
|
||||
|
||||
private val downloader = DependencyDownloader(maxAttempts, attemptIntervalMs, customProgressCallback)
|
||||
private val extractor = DependencyExtractor()
|
||||
|
||||
private val archiveExtension get() = extractor.archiveExtension
|
||||
|
||||
constructor(
|
||||
dependenciesRoot: File,
|
||||
properties: Properties,
|
||||
dependencies: List<String>,
|
||||
dependenciesUrl: String = properties.dependenciesUrl,
|
||||
keepUnstable: Boolean = true
|
||||
) : this(
|
||||
dependenciesRoot,
|
||||
dependenciesUrl,
|
||||
dependencyToCandidates = properties.findCandidates(dependencies),
|
||||
airplaneMode = properties.airplaneMode,
|
||||
maxAttempts = properties.downloadingAttempts,
|
||||
attemptIntervalMs = properties.downloadingAttemptIntervalMs,
|
||||
keepUnstable = keepUnstable
|
||||
)
|
||||
|
||||
class DependencyFile(directory: File, fileName: String) {
|
||||
val file = File(directory, fileName).apply { createNewFile() }
|
||||
private val dependencies = file.readLines().toMutableSet()
|
||||
|
||||
fun contains(dependency: String) = dependencies.contains(dependency)
|
||||
fun add(dependency: String) = dependencies.add(dependency)
|
||||
fun remove(dependency: String) = dependencies.remove(dependency)
|
||||
|
||||
fun removeAndSave(dependency: String) {
|
||||
remove(dependency)
|
||||
save()
|
||||
}
|
||||
|
||||
fun addAndSave(dependency: String) {
|
||||
add(dependency)
|
||||
save()
|
||||
}
|
||||
|
||||
fun save() {
|
||||
val writer = file.writer()
|
||||
writer.use {
|
||||
dependencies.forEach {
|
||||
writer.write(it)
|
||||
writer.write("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun downloadDependency(dependency: String, baseUrl: String) {
|
||||
val depDir = File(dependenciesDirectory, dependency)
|
||||
val depName = depDir.name
|
||||
|
||||
val fileName = "$depName.$archiveExtension"
|
||||
val archive = cacheDirectory.resolve(fileName)
|
||||
val url = URL("$baseUrl/$fileName")
|
||||
|
||||
val extractedDependencies = DependencyFile(dependenciesDirectory, ".extracted")
|
||||
if (extractedDependencies.contains(depName) &&
|
||||
depDir.exists() &&
|
||||
depDir.isDirectory &&
|
||||
depDir.list().isNotEmpty()
|
||||
) {
|
||||
|
||||
if (!keepUnstable && depDir.list().contains(".unstable")) {
|
||||
// The downloaded version of the dependency is unstable -> redownload it.
|
||||
depDir.deleteRecursively()
|
||||
archive.delete()
|
||||
extractedDependencies.removeAndSave(dependency)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (showInfo && !isInfoShown) {
|
||||
println("Downloading native dependencies (LLVM, sysroot etc). This is a one-time action performed only on the first run of the compiler.")
|
||||
isInfoShown = true
|
||||
}
|
||||
|
||||
if (!archive.exists()) {
|
||||
if (airplaneMode) {
|
||||
throw FileNotFoundException(
|
||||
"""
|
||||
Cannot find a dependency locally: $dependency.
|
||||
Set `airplaneMode = false` in konan.properties to download it.
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
downloader.download(url, archive)
|
||||
}
|
||||
println("Extracting dependency: $archive into $dependenciesDirectory")
|
||||
extractor.extract(archive, dependenciesDirectory)
|
||||
extractedDependencies.addAndSave(depName)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val lock = ReentrantLock()
|
||||
|
||||
val localKonanDir: File by lazy {
|
||||
File(System.getenv("KONAN_DATA_DIR") ?: (System.getProperty("user.home") + File.separator + ".konan"))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
val defaultDependenciesRoot: File
|
||||
get() = localKonanDir.resolve("dependencies")
|
||||
|
||||
val defaultDependencyCacheDir: File
|
||||
get() = localKonanDir.resolve("cache")
|
||||
}
|
||||
|
||||
private val resolvedDependencies = dependencyToCandidates.map { (dependency, candidates) ->
|
||||
val candidate = candidates.asSequence().mapNotNull { candidate ->
|
||||
when (candidate) {
|
||||
is DependencySource.Local -> candidate.takeIf { it.path.exists() }
|
||||
DependencySource.Remote.Public -> candidate
|
||||
DependencySource.Remote.Internal -> candidate.takeIf { InternalServer.isAvailable }
|
||||
}
|
||||
}.firstOrNull()
|
||||
|
||||
candidate ?: error("$dependency is not available; candidates:\n${candidates.joinToString("\n")}")
|
||||
|
||||
dependency to candidate
|
||||
}.toMap()
|
||||
|
||||
private fun resolveDependency(dependency: String): File {
|
||||
val candidate = resolvedDependencies[dependency]
|
||||
return when (candidate) {
|
||||
is DependencySource.Local -> candidate.path
|
||||
is DependencySource.Remote -> File(dependenciesDirectory, dependency)
|
||||
null -> error("$dependency not declared as dependency")
|
||||
}
|
||||
}
|
||||
|
||||
fun resolveRelative(relative: String): File {
|
||||
val path = Paths.get(relative)
|
||||
if (path.isAbsolute) error("not a relative path: $relative")
|
||||
|
||||
val dependency = path.first().toString()
|
||||
return resolveDependency(dependency).let {
|
||||
if (path.nameCount > 1) {
|
||||
it.toPath().resolve(path.subpath(1, path.nameCount)).toFile()
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun run() = lock.withLock {
|
||||
RandomAccessFile(lockFile, "rw").channel.lock().use {
|
||||
resolvedDependencies.forEach { (dependency, candidate) ->
|
||||
val baseUrl = when (candidate) {
|
||||
is DependencySource.Local -> null
|
||||
DependencySource.Remote.Public -> dependenciesUrl
|
||||
DependencySource.Remote.Internal -> InternalServer.url
|
||||
}
|
||||
// TODO: consider using different caches for different remotes.
|
||||
if (baseUrl != null) {
|
||||
downloadDependency(dependency, baseUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal object InternalServer {
|
||||
private val host = "repo.labs.intellij.net"
|
||||
val url = "http://$host/kotlin-native"
|
||||
|
||||
private val internalDomain = "labs.intellij.net"
|
||||
|
||||
val isAvailable by lazy {
|
||||
val envKey = "KONAN_USE_INTERNAL_SERVER"
|
||||
val envValue = System.getenv(envKey)
|
||||
when (envValue) {
|
||||
"0" -> false
|
||||
"1" -> true
|
||||
null -> checkAccessible()
|
||||
else -> error("unexpected environment: $envKey=$envValue")
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkAccessible(): Boolean {
|
||||
if (!InetAddress.getLocalHost().canonicalHostName.endsWith(".$internalDomain")) {
|
||||
// Fast path:
|
||||
return false
|
||||
}
|
||||
|
||||
return try {
|
||||
InetAddress.getByName(host)
|
||||
true
|
||||
} catch (e: UnknownHostException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,8 +34,10 @@ val projectsToShadow by extra(listOf(
|
||||
":compiler:frontend.script",
|
||||
":idea:ide-common",
|
||||
":idea",
|
||||
":idea:idea-native",
|
||||
":idea:idea-core",
|
||||
":idea:idea-gradle",
|
||||
//":idea:idea-gradle:native",
|
||||
//":idea-ultimate",
|
||||
":compiler:ir.psi2ir",
|
||||
":compiler:ir.tree",
|
||||
@@ -43,6 +45,9 @@ val projectsToShadow by extra(listOf(
|
||||
":js:js.frontend",
|
||||
":js:js.parser",
|
||||
":js:js.serializer",
|
||||
":konan:konan-serializer",
|
||||
":konan:konan-metadata",
|
||||
":konan:konan-utils",
|
||||
":compiler:light-classes",
|
||||
":compiler:plugin-api",
|
||||
":kotlin-preloader",
|
||||
|
||||
@@ -77,6 +77,8 @@ include ":kotlin-build-common",
|
||||
":idea:idea-android",
|
||||
":idea:idea-android-output-parser",
|
||||
":idea:idea-test-framework",
|
||||
":idea:idea-native",
|
||||
//":idea:idea-gradle:native",
|
||||
":idea",
|
||||
":idea-runner",
|
||||
":eval4j",
|
||||
|
||||
Reference in New Issue
Block a user