Compare commits

...

23 Commits

Author SHA1 Message Date
Ilya Matveev
2f0e20b1c7 WIP: Basic support for Kotlin/Native
* Target presets
 * Compilation into a klib
 * Compilation into native binraies: framework and executable
 * Basic dependencies between projects
2018-07-19 19:19:04 +07:00
Ilya Matveev
f924de871f Add dependency on KN shared in gradle-plugin 2018-07-19 19:18:45 +07:00
Alexander Podkhalyuzin
f953726f64 Kotlin/Native support in IntelliJ IDEA 2018-07-16 12:31:29 +07:00
Sergey Igushkin
d32afbf28c Fix non-MPP builds failing due to an invalid type cast 2018-07-12 17:54:53 +03:00
Sergey Igushkin
2f567f0fdb Fix Android, clashes due to the naming conventions; refactor 2018-07-12 14:37:11 +03:00
Sergey Igushkin
c94386cff7 Fix old use cases and tests 2018-07-09 18:53:45 +03:00
Sergey Igushkin
2cb379c7da (incomplete) new MPP design implementation 2018-07-06 17:21:09 +03:00
Sergey Igushkin
eeb9281b25 ~ Partial implementation 2018-07-02 12:26:02 +03:00
Sergey Igushkin
698d69070c (incomplete) new MPP design implementation 2018-06-29 14:00:49 +03:00
Sergey Igushkin
59cc320abc ~ Fix build after rebase on master 2018-06-26 15:33:40 +03:00
Sergey Igushkin
6bd1675a8b ~ Fix Android in experimental MPP (source sets were not detected) 2018-06-25 18:40:46 +03:00
Sergey Igushkin
d8d69fca98 Partial implementation of Android support in experimental MPP 2018-06-25 18:40:45 +03:00
Sergey Igushkin
504a9ec48f Publishing with variants 2018-06-25 18:39:34 +03:00
Sergey Igushkin
712a108c7f (minor) Make allSource include kotlin and resources. 2018-06-25 18:39:34 +03:00
Sergey Igushkin
ab9536b64a Add jvmWithJava platform. 2018-06-25 18:39:33 +03:00
Sergey Igushkin
0072b62d76 Variants, expectedBy relation for single platform 2018-06-25 18:38:51 +03:00
Sergey Igushkin
83d9e9363e Support JVM platform 2018-06-25 18:38:49 +03:00
Sergey Igushkin
150f8f34c7 Preparations for JVM platform in the experimental MPP plugin. 2018-06-25 18:36:32 +03:00
Sergey Igushkin
3bb1afb05a Implement common and js platforms in the multiplatform plugin 2018-06-25 18:32:54 +03:00
Sergey Igushkin
0aaf084ff6 Add experimental multiplatform plugin 2018-06-25 18:32:54 +03:00
Sergey Igushkin
415006e7b2 Factor out some abstractions for Kotlin platforms, which should allow
implementing a MPP plugin that just calls those abstractions.
2018-06-25 18:32:51 +03:00
Sergey Igushkin
6631d539ad Кefactor KotlinSourceSet & base plugin, support Android & MPP 2018-06-25 18:28:43 +03:00
Sergey Igushkin
204c36fea2 Add Kotlin source sets that are Java-agnostic, use them instead of the
Java source sets.
2018-06-25 17:52:02 +03:00
84 changed files with 4840 additions and 558 deletions

View File

@@ -15,6 +15,6 @@
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<method />
<method v="2" />
</configuration>
</component>

View File

@@ -22,6 +22,7 @@ buildscript {
"https://jcenter.bintray.com/",
"https://plugins.gradle.org/m2",
"http://dl.bintray.com/kotlin/kotlinx",
"https://jetbrains.bintray.com/kotlin-native-dependencies",
"https://repo.gradle.org/gradle/ext-releases-local", // for native-platform
"https://jetbrains.bintray.com/intellij-third-party-dependencies", // for jflex
"https://dl.bintray.com/jetbrains/markdown" // for org.jetbrains:markdown

View File

@@ -123,4 +123,4 @@ class KotlinScriptDependenciesClassFinder(project: Project,
private fun String.splitByLastDot(): Pair<String, String> {
return Pair(substringBeforeLast('.', missingDelimiterValue = ""), substringAfterLast('.'))
}
}
}

View File

@@ -0,0 +1,82 @@
import com.github.jk1.tcdeps.KotlinScriptDslAdapter.teamcityServer
import com.github.jk1.tcdeps.KotlinScriptDslAdapter.tc
plugins {
kotlin("jvm")
id("jps-compatible")
id("com.github.jk1.tcdeps") version "0.17"
}
repositories {
teamcityServer {
setUrl("http://buildserver.labs.intellij.net")
credentials {
username = "guest"
password = "guest"
}
}
}
val kotlinNativeVersion = "0.9-dev-2798"
dependencies {
compile(tc("Kotlin_KotlinNative_Master_KotlinNativeLinuxDist:$kotlinNativeVersion:shared.jar"))
compile(tc("Kotlin_KotlinNative_Master_KotlinNativeLinuxDist:$kotlinNativeVersion:backend.native.jar"))
compile("org.jetbrains.kotlin:kotlin-native-gradle-plugin:0.9-dev-2809") { isTransitive = false }
compileOnly(project(":idea:idea-gradle"))
compileOnly(project(":idea:idea-native"))
testRuntime(intellijDep())
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"))
testCompile(projectTests(":idea"))
testCompile(projectTests(":idea:idea-test-framework"))
testCompile(intellijPluginDep("gradle"))
testCompileOnly(intellijPluginDep("Groovy"))
testCompileOnly(intellijDep())
testRuntime(projectDist(":kotlin-reflect"))
testRuntime(project(":idea:idea-jvm"))
testRuntime(project(":idea:idea-android"))
testRuntime(project(":plugins:kapt3-idea"))
testRuntime(project(":plugins:android-extensions-ide"))
testRuntime(project(":plugins:lint"))
testRuntime(project(":sam-with-receiver-ide-plugin"))
testRuntime(project(":allopen-ide-plugin"))
testRuntime(project(":noarg-ide-plugin"))
testRuntime(project(":kotlin-scripting-idea"))
// TODO: the order of the plugins matters here, consider avoiding order-dependency
testRuntime(intellijPluginDep("junit"))
testRuntime(intellijPluginDep("testng"))
testRuntime(intellijPluginDep("properties"))
testRuntime(intellijPluginDep("gradle"))
testRuntime(intellijPluginDep("Groovy"))
testRuntime(intellijPluginDep("coverage"))
testRuntime(intellijPluginDep("maven"))
testRuntime(intellijPluginDep("android"))
testRuntime(intellijPluginDep("smali"))
}
sourceSets {
"main" { projectDefault() }
"test" { none() }
}
configureInstrumentation()

View File

@@ -0,0 +1,73 @@
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 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")
}
}

View File

@@ -0,0 +1,23 @@
package org.jetbrains.konan.gradle
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager
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 projectOpened() {
super.projectOpened()
ExternalProjectsManager.getInstance(project).runWhenInitialized {
ApplicationManager.getApplication().invokeLater {
reloadLibraries()
}
}
}
override fun looksLikeKotlinNativeProject(): Boolean {
//TODO not just any gradle project
return GradleSettings.getInstance(project).linkedProjectsSettings.isNotEmpty()
}
}

View File

@@ -0,0 +1,63 @@
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)
}
}
}
}
}
}

View File

@@ -0,0 +1,155 @@
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())
if (isDirectory(path)) {
library.addPath(LibraryPathType.BINARY, path.resolve("linkdata").toAbsolutePath().toString())
}
else {
library.addPath(LibraryPathType.BINARY, path.toString() + "!/linkdata")
}
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)
}
}

View File

@@ -0,0 +1,27 @@
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);
}
}
}
}

View File

@@ -0,0 +1,44 @@
import com.github.jk1.tcdeps.KotlinScriptDslAdapter.teamcityServer
import com.github.jk1.tcdeps.KotlinScriptDslAdapter.tc
plugins {
kotlin("jvm")
//application
id("com.github.jk1.tcdeps") version "0.17"
}
repositories {
teamcityServer {
setUrl("http://buildserver.labs.intellij.net")
credentials {
username = "guest"
password = "guest"
}
}
}
val kotlinNativeVersion = "0.9-dev-2798"
dependencies {
compile(tc("Kotlin_KotlinNative_Master_KotlinNativeLinuxDist:$kotlinNativeVersion:shared.jar"))
compile(tc("Kotlin_KotlinNative_Master_KotlinNativeLinuxDist:$kotlinNativeVersion:backend.native.jar"))
compile(project(":idea"))
compile(project(":idea:idea-core"))
compile(project(":compiler:frontend"))
compileOnly(intellijDep())
}
sourceSets {
"main" { projectDefault() }
"test" { none() }
}
configureInstrumentation()
runtimeJar {
archiveName = "native-ide.jar"
}

View File

@@ -0,0 +1,24 @@
package org.jetbrains.konan
import com.intellij.ide.highlighter.ArchiveFileType
import com.intellij.ide.plugins.cl.PluginClassLoader
import com.intellij.ide.util.TipAndTrickBean
import com.intellij.openapi.components.ApplicationComponent
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.fileTypes.FileTypeManager
import org.jetbrains.kotlin.psi.KtElement
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)
}
}
}
}

View File

@@ -0,0 +1,29 @@
package org.jetbrains.konan
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.stubs.StubTree
import org.jetbrains.konan.analyser.index.KonanDescriptorManager
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) {
override fun getStubTree(): StubTree? {
val vFile = virtualFile
val cache = KonanDescriptorManager.INSTANCE
val cached = cache.getStub(vFile)
if (cached != null) {
return cached
}
val stubTree = super.getStubTree()
if (stubTree != null) {
cache.cacheStub(vFile, stubTree)
}
return stubTree
}
}

View File

@@ -0,0 +1,93 @@
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.DependencyDownloader
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 DependencyDownloader.HTTPResponseException) {
"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)
}
}

View File

@@ -0,0 +1,110 @@
package org.jetbrains.konan;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiFileFactoryImpl;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.stubs.PsiFileStub;
import com.intellij.testFramework.LightVirtualFile;
import kotlin.Pair;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.analyzer.ModuleContent;
import org.jetbrains.kotlin.analyzer.ModuleInfo;
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.KotlinDecompiledFileViewProvider;
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText;
import org.jetbrains.kotlin.idea.decompiler.textBuilder.LoggingErrorReporter;
import org.jetbrains.kotlin.idea.references.KtReference;
import org.jetbrains.kotlin.psi.KtBlockExpression;
import org.jetbrains.kotlin.psi.KtExpressionCodeFragment;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.psi.KtPsiFactory;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes;
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory;
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService;
import java.util.Collection;
public class KotlinWorkaroundUtil {
@NotNull
public static PsiFileStub createFileStub(@NotNull Project project, @NotNull String text) {
final LightVirtualFile virtualFile = new LightVirtualFile("dummy.kt", KotlinFileType.INSTANCE, text);
virtualFile.setLanguage(KotlinLanguage.INSTANCE);
SingleRootFileViewProvider.doNotCheckFileSizeLimit(virtualFile);
PsiFileFactoryImpl psiFileFactory = (PsiFileFactoryImpl)PsiFileFactory.getInstance(project);
PsiFile file = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, false, false);
assert file != null;
return (PsiFileStub)KtStubElementTypes.FILE.getBuilder().buildStubTree(file);
}
@NotNull
public static KtExpressionCodeFragment createCodeFragment(@NotNull Project project,
@NotNull String referenceText,
@NotNull KtBlockExpression blockExpression) {
return new KtPsiFactory(project, false).createExpressionCodeFragment(referenceText, blockExpression);
}
@Nullable
public static PsiElement resolveMainReference(@NotNull KtReference mainReference) {
return mainReference.resolve();
}
@Nullable
public static KtBlockExpression getNonStrictParentOfType(@Nullable PsiElement contextElement) {
if (contextElement == null) return null;
return PsiUtilsKt.getNonStrictParentOfType(contextElement, KtBlockExpression.class);
}
@NotNull
public static LoggingErrorReporter createLoggingErrorReporter(@NotNull Logger log) {
return new LoggingErrorReporter(log);
}
@NotNull
public static FileViewProvider createDecompiledFileViewProvider(@NotNull PsiManager manager,
@NotNull VirtualFile file,
boolean physical,
@NotNull Function1<VirtualFile, DecompiledText> buildDecompiledText) {
return new KotlinDecompiledFileViewProvider(manager, file, physical,
provider -> new KonanDecompiledFile(provider, buildDecompiledText));
}
@NotNull
public static Project getProject(@NotNull ModuleContext moduleContext) {
return moduleContext.getProject();
}
@NotNull
public static <M extends ModuleInfo> Pair<Collection<KtFile>, GlobalSearchScope> destructModuleContent(@NotNull ModuleContent<M> moduleContent) {
return new Pair<>(moduleContent.component2(), moduleContent.component3());
}
@NotNull
public static <M extends ModuleInfo> DeclarationProviderFactory createDeclarationProviderFactory(@NotNull Project project,
@NotNull ModuleContext moduleContext,
@NotNull Collection<KtFile> syntheticFiles,
@NotNull M moduleInfo,
@Nullable GlobalSearchScope globalSearchScope) {
return DeclarationProviderFactoryService.createDeclarationProviderFactory(
project,
moduleContext.getStorageManager(),
syntheticFiles,
globalSearchScope,
moduleInfo);
}
@NotNull
public static LibraryInfo createLibraryInfo(@NotNull Project project, @NotNull Library library) {
return new LibraryInfo(project, library);
}
}

View File

@@ -0,0 +1,19 @@
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)
}
}

View File

@@ -0,0 +1,161 @@
package org.jetbrains.konan.analyser
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VfsUtil
import org.jetbrains.konan.KotlinWorkaroundUtil.*
import org.jetbrains.konan.analyser.index.KonanDescriptorManager
import org.jetbrains.konan.settings.KonanPaths
import org.jetbrains.kotlin.analyzer.*
import org.jetbrains.kotlin.backend.konan.KonanPlatform
import org.jetbrains.kotlin.backend.konan.descriptors.createForwardDeclarationsModule
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.PackagePartProvider
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.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingTraceContext
import org.jetbrains.kotlin.resolve.TargetEnvironment
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.storage.StorageManager
import java.nio.file.Path
/**
* @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,
packagePartProvider: PackagePartProvider): ResolverForModule {
val (syntheticFiles, moduleContentScope) = destructModuleContent(moduleContent)
val project = getProject(moduleContext)
val declarationProviderFactory = createDeclarationProviderFactory(project, moduleContext, syntheticFiles, moduleContent.moduleInfo,
moduleContentScope)
val container = createContainerForLazyResolve(
moduleContext,
declarationProviderFactory,
BindingTraceContext(),
KonanPlatform,
TargetPlatformVersion.NoVersion,
targetEnvironment,
languageVersionSettings
)
val packageFragmentProvider = container.get<ResolveSession>().packageFragmentProvider
val konanPaths = KonanPaths.getInstance(project)
val libraryPaths = konanPaths.libraryPaths().toMutableList()
libraryPaths.addAll(konanPaths.konanPlatformLibraries())
val module = resolveModule(syntheticFiles, project)
fun setLibraryForDescriptor(path: Path, descriptor: ModuleDescriptorImpl) {
val library = findLibrary(module, path) ?: return
//todo: replace reflection by changes to kotlin-native?
descriptor.setField("capabilities", { oldValue ->
val capabilities = oldValue as Map<*, *>
val libraryInfo = createLibraryInfo(project, library)
capabilities + Pair(ModuleInfo.Capability, libraryInfo)
})
}
fun createDescriptor(path: Path): ModuleDescriptorImpl? {
val file = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(path.toFile())
return if (file != null && file.exists()) {
val descriptor = KonanDescriptorManager.INSTANCE.getDescriptor(file, languageVersionSettings)
setLibraryForDescriptor(path, descriptor)
descriptor
}
else null
}
val descriptors = libraryPaths.mapNotNull(::createDescriptor)
val stdlibDescriptor = konanPaths.konanStdlib()?.let(::createDescriptor)
val dependencies = descriptors.toMutableList()
if (stdlibDescriptor != null)
dependencies.add(stdlibDescriptor)
// Create module for handling `cnames.structs` opaque declarations. It should be singleton and last dependency in the list.
val builtIns = moduleContext.module.builtIns
val storageManager: StorageManager = moduleContext.storageManager
val forwardDeclarationsModule = createForwardDeclarationsModule(builtIns, storageManager)
for (descriptor in descriptors) {
descriptor.setField("dependencies", { null })
descriptor.setDependencies(descriptor, stdlibDescriptor!!, forwardDeclarationsModule)
}
stdlibDescriptor?.let {
it.setField("dependencies", { null })
it.setDependencies(listOf(it))
}
val fragmentProviders = mutableListOf(packageFragmentProvider)
descriptors.mapTo(fragmentProviders) { it.packageFragmentProvider }
fragmentProviders.add(forwardDeclarationsModule.packageFragmentProvider)
return ResolverForModule(CompositePackageFragmentProvider(fragmentProviders), container)
}
private fun resolveModule(syntheticFiles: MutableCollection<KtFile>,
project: Project): Module {
val virtualFilePath = try {
syntheticFiles.firstOrNull()?.virtualFilePath
}
catch (e: IllegalStateException) {
null
}
return virtualFilePath
?.run { VfsUtil.findFileByIoFile(java.io.File(this), false) }
?.run { ProjectFileIndex.SERVICE.getInstance(project).getModuleForFile(this) }
?: ModuleManager.getInstance(project).modules.first()
}
private fun findLibrary(module: Module, path: Path): Library? {
var lib: Library? = null
ModuleRootManager.getInstance(module).orderEntries().forEachLibrary {
if (it.name?.substringAfter(": ") == path.fileName.toString()) {
lib = it
false
}
else {
true
}
}
return lib
}
}
internal fun Any.setField(fieldName: String, update: (Any?) -> Any?) {
val field = javaClass.declaredFields.find { it.name == fieldName }
if (field != null) {
field.isAccessible = true
val oldValue = field.get(this)
val newValue = update(oldValue)
field.set(this, newValue)
}
}

View File

@@ -0,0 +1,58 @@
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 com.intellij.util.io.exists
import org.jetbrains.konan.analyser.index.KonanDescriptorManager
import org.jetbrains.konan.settings.KonanPaths
import org.jetbrains.kotlin.analyzer.ResolverForModuleFactory
import org.jetbrains.kotlin.backend.konan.KonanBuiltIns
import org.jetbrains.kotlin.backend.konan.KonanPlatform
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.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.idea.caches.resolve.PlatformAnalysisSettings
import org.jetbrains.kotlin.resolve.TargetPlatform
/**
* @author Alefas
*/
class KonanPlatformSupport : IdePlatformSupport() {
override val resolverForModuleFactory: ResolverForModuleFactory
get() = KonanAnalyzerFacade()
override val libraryKind: PersistentLibraryKind<*>?
get() = null
override val platform: TargetPlatform
get() = KonanPlatform
override fun createBuiltIns(settings: PlatformAnalysisSettings, sdkContext: GlobalContextImpl): KotlinBuiltIns {
val storageManager = sdkContext.storageManager
val builtIns = KonanBuiltIns(storageManager)
//todo: it depends on a random project's stdlib, propagate the actual project here
val konanStdlib = ProjectManager.getInstance().openProjects.asSequence().mapNotNull { KonanPaths.getInstance(it).konanStdlib() }.firstOrNull()
if (konanStdlib != null && konanStdlib.exists()) {
val stdLib = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(konanStdlib.toFile())
if (stdLib != null) {
val manager = KonanDescriptorManager.INSTANCE
val builtInsModule: ModuleDescriptorImpl = manager.getDescriptor(stdLib, LanguageVersionSettingsImpl.DEFAULT)
builtInsModule.setField("dependencies", { null })
builtInsModule.setDependencies(builtInsModule)
builtIns.builtInsModule = builtInsModule
}
}
return builtIns
}
override fun isModuleForPlatform(module: Module): Boolean {
return true
}
}

View File

@@ -0,0 +1,68 @@
package org.jetbrains.konan.analyser.index
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.ApplicationComponent
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileEvent
import com.intellij.openapi.vfs.VirtualFileListener
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.stubs.StubTree
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.backend.konan.library.impl.LibraryReaderImpl
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.metadata.KonanLinkData
import java.util.concurrent.ConcurrentMap
private const val currentAbiVersion = 1
class KonanDescriptorManager : ApplicationComponent {
companion object {
@JvmStatic
val INSTANCE: KonanDescriptorManager
get() = ApplicationManager.getApplication().getComponent(KonanDescriptorManager::class.java)
}
override fun initComponent() {
VirtualFileManager.getInstance().addVirtualFileListener(object : VirtualFileListener {
override fun fileCreated(event: VirtualFileEvent) {
val file = event.file
descriptorCache.values.forEach { it.remove(file) }
protoCache.remove(file)
stubCache.remove(file)
}
})
}
private val descriptorCache = ContainerUtil.createConcurrentSoftValueMap<LanguageVersionSettings, ConcurrentMap<VirtualFile, ModuleDescriptorImpl>>()
private val protoCache = ContainerUtil.createConcurrentSoftValueMap<VirtualFile, KonanLinkData.LinkDataPackageFragment>()
private val stubCache = ContainerUtil.createConcurrentSoftValueMap<VirtualFile, StubTree>()
fun getDescriptor(file: VirtualFile, specifics: LanguageVersionSettings): ModuleDescriptorImpl {
val cache = descriptorCache.computeIfAbsent(specifics, {
ContainerUtil.createConcurrentSoftValueMap()
})
return cache.computeIfAbsent(file, {
val reader = LibraryReaderImpl(File(file.path), currentAbiVersion)
reader.moduleDescriptor(specifics)
})
}
fun parsePackageFragment(file: VirtualFile): KonanLinkData.LinkDataPackageFragment {
return protoCache.computeIfAbsent(file, {
val bytes = file.contentsToByteArray(false)
org.jetbrains.kotlin.backend.konan.serialization.parsePackageFragment(bytes)
})
}
fun getStub(file: VirtualFile): StubTree? {
return stubCache[file]
}
fun cacheStub(file: VirtualFile, stubTree: StubTree?) {
stubCache[file] = stubTree
}
}

View File

@@ -0,0 +1,9 @@
package org.jetbrains.konan.analyser.index;
import org.jetbrains.kotlin.idea.util.KotlinBinaryExtension;
public class KonanMetaBinary extends KotlinBinaryExtension {
public KonanMetaBinary() {
super(KonanMetaFileType.INSTANCE);
}
}

View File

@@ -0,0 +1,40 @@
package org.jetbrains.konan.analyser.index;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.indexing.FileContent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.vfilefinder.KotlinFileIndexBase;
import org.jetbrains.kotlin.metadata.KonanLinkData;
import org.jetbrains.kotlin.name.FqName;
public class KonanMetaFileIndex extends KotlinFileIndexBase<KonanMetaFileIndex> {
private static final int VERSION = 4;
/*todo: check version?!*/
private final DataIndexer<FqName, Void, FileContent> INDEXER = indexer(fileContent -> {
KonanLinkData.LinkDataPackageFragment fragment = KonanDescriptorManager.getINSTANCE().parsePackageFragment(fileContent.getFile());
return new FqName(fragment.getFqName());
});
public KonanMetaFileIndex() {
super(KonanMetaFileIndex.class);
}
@NotNull
@Override
public FileBasedIndex.InputFilter getInputFilter() {
return file -> file.getFileType() == KonanMetaFileType.INSTANCE;
}
@NotNull
@Override
public DataIndexer<FqName, Void, FileContent> getIndexer() {
return INDEXER;
}
@Override
public int getVersion() {
return VERSION;
}
}

View File

@@ -0,0 +1,51 @@
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.backend.konan.KonanPlatform
import org.jetbrains.kotlin.backend.konan.serialization.KonanSerializerProtocol
import org.jetbrains.kotlin.backend.konan.serialization.NullFlexibleTypeDeserializer
import org.jetbrains.kotlin.metadata.KonanLinkData
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
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: KonanLinkData.LinkDataPackageFragment = KonanDescriptorManager.INSTANCE.parsePackageFragment(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)
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.konan.analyser.index
import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.FileViewProvider
import com.intellij.psi.PsiManager
import com.intellij.psi.compiled.ClassFileDecompilers
import org.jetbrains.konan.KotlinWorkaroundUtil.createDecompiledFileViewProvider
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
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.KonanLinkData
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl
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, this::readFile)
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): FileViewProvider {
return createDecompiledFileViewProvider(manager, file, physical, this::buildDecompiledText)
}
private fun readFile(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 {
if (virtualFile.fileType != fileType) {
error("Unexpected file type ${virtualFile.fileType}")
}
val file = readFile(virtualFile)
return when (file) {
null -> {
createIncompatibleAbiVersionDecompiledText(expectedBinaryVersion, invalidBinaryVersion)
}
is FileWithMetadata.Incompatible -> {
createIncompatibleAbiVersionDecompiledText(expectedBinaryVersion, file.version)
}
is FileWithMetadata.Compatible -> {
decompiledText(file, targetPlatform, serializerProtocol, flexibleTypeDeserializer, renderer)
}
}
}
}
sealed class FileWithMetadata {
class Incompatible(val version: BinaryVersion) : FileWithMetadata()
open class Compatible(
val proto: KonanLinkData.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)
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.konan.analyser.index
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.konan.KotlinWorkaroundUtil.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.KonanLinkData
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
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: KonanLinkData.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, containerSource = null,
components = deserializationComponents
) { emptyList() }
return membersScope.getContributedDescriptors().toList()
}
companion object {
private val LOG = Logger.getInstance(KonanMetadataDeserializerForDecompiler::class.java)
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.KotlinWorkaroundUtil.createFileStub
import org.jetbrains.kotlin.backend.konan.KonanPlatform
import org.jetbrains.kotlin.backend.konan.serialization.NullFlexibleTypeDeserializer
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.serialization.SerializerExtensionProtocol
//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
when (file) {
is FileWithMetadata.Incompatible -> {
return 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)
return createFileStub(content.project, ktFileText.text)
}
}
}
}

View File

@@ -0,0 +1,28 @@
package org.jetbrains.konan.analyser.index
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.metadata.KonanLinkData
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
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: KonanLinkData.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, classSource(classId))
}
}

View File

@@ -0,0 +1,20 @@
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

View File

@@ -0,0 +1,26 @@
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<>("Konan 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);
}

View File

@@ -0,0 +1,47 @@
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.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 {
private const val STDLIB_PATH = "klib/common/stdlib" //todo: move to kotlin/native
fun bundledKonanDist(): Path = KotlinNativeToolchain.BUNDLED.baseDir
fun getInstance(project: Project): KonanPaths = project.getComponent(KonanPaths::class.java)
}
override fun getComponentName() = "Konan Compiler Paths"
fun konanStdlib(): Path? = konanDist()?.resolve(STDLIB_PATH)
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("klib/platform/${resolvedTargetName}") ?: return emptyList()
if (!klibPath.exists()) return emptyList()
return Files.walk(klibPath, 1).filter {
it.isDirectory() && it.fileName.toString() != "stdlib" && it != klibPath
}.collect(Collectors.toList())
}
//todo: this is wrong, we are not allowing multiple targets in project
open fun target(): KonanTarget = HostManager.host
}

View File

@@ -0,0 +1,105 @@
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
private const val LINK_DATA: String = "linkdata"
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: Path in libraryPaths) {
fun createLibrary(vfs: VirtualFileSystem, linkDataPath: String) {
val linkData = vfs.refreshAndFindFileByPath(linkDataPath) ?: return
val library = libraryTable.createLibrary(path.fileName.toString())
val libraryModifiableModel = library.modifiableModel
libraryModifiableModel.addRoot(linkData, OrderRootType.CLASSES)
runWriteAction {
libraryModifiableModel.commit()
}
}
val libraryRootPath = path.toString()
val libraryRoot: VirtualFile = localFileSystem.refreshAndFindFileByPath(libraryRootPath) ?: continue
if (libraryRoot.isDirectory) {
createLibrary(localFileSystem, "$libraryRootPath/$LINK_DATA")
}
else { //zip
createLibrary(jarFileSystem, "$libraryRootPath!/$LINK_DATA")
}
}
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
}

View File

@@ -78,4 +78,31 @@
<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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -2940,6 +2940,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/*)"/>

View File

@@ -2939,6 +2939,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/*)"/>

View File

@@ -2938,6 +2938,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/*)"/>

View File

@@ -16,13 +16,10 @@
package example
import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin
import org.gradle.api.Project
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.gradle.plugin.JetBrainsSubpluginArtifact
import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
class ExampleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
@@ -31,12 +28,12 @@ class ExampleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
}
override fun apply(
project: Project,
kotlinCompile: AbstractCompile,
javaCompile: AbstractCompile,
variantData: Any?,
androidProjectHandler: Any?,
javaSourceSet: SourceSet?
project: Project,
kotlinCompile: AbstractCompile,
javaCompile: AbstractCompile?,
variantData: Any?,
androidProjectHandler: Any?,
kotlinCompilation: KotlinCompilation?
): List<SubpluginOption> {
println("ExampleSubplugin loaded")
return listOf(SubpluginOption("exampleKey", "exampleValue"))

View File

@@ -18,14 +18,8 @@ package org.jetbrains.kotlin.allopen.gradle
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.internal.AbstractTask
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.gradle.plugin.JetBrainsSubpluginArtifact
import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin
import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
import org.jetbrains.kotlin.gradle.plugin.*
class AllOpenGradleSubplugin : Plugin<Project> {
companion object {
@@ -52,12 +46,12 @@ class AllOpenKotlinGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
override fun isApplicable(project: Project, task: AbstractCompile) = AllOpenGradleSubplugin.isEnabled(project)
override fun apply(
project: Project,
kotlinCompile: AbstractCompile,
javaCompile: AbstractCompile,
variantData: Any?,
androidProjectHandler: Any?,
javaSourceSet: SourceSet?
project: Project,
kotlinCompile: AbstractCompile,
javaCompile: AbstractCompile?,
variantData: Any?,
androidProjectHandler: Any?,
kotlinCompilation: KotlinCompilation?
): List<SubpluginOption> {
if (!AllOpenGradleSubplugin.isEnabled(project)) return emptyList()

View File

@@ -1,12 +1,25 @@
plugins {
id 'com.github.jk1.tcdeps' version '0.17'
}
apply plugin: 'kotlin'
apply plugin: 'maven'
apply plugin: 'jps-compatible'
def kotlinNativeVersion = "0.9-dev-2798" // TODO: Get from root dir
configureJvmProject(project)
configurePublishing(project)
repositories {
mavenLocal()
teamcityServer {
url = "http://buildserver.labs.intellij.net"
credentials {
username = "guest"
password = "guest"
}
}
}
pill {
@@ -15,7 +28,8 @@ pill {
dependencies {
compile project(':kotlin-stdlib')
compileOnly tc("Kotlin_KotlinNative_Master_KotlinNativeLinuxDist:$kotlinNativeVersion:shared.jar")
compileOnly gradleApi()
compileOnly 'com.android.tools.build:gradle:0.4.2'
}

View File

@@ -0,0 +1,28 @@
/*
* 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.gradle.plugin
import groovy.lang.Closure
interface KotlinDependencyHandler {
fun api(dependencyNotation: Any)
fun implementation(dependencyNotation: Any)
fun compileOnly(dependencyNotation: Any)
fun runtimeOnly(dependencyNotation: Any)
}
interface HasKotlinDependencies {
fun dependencies(configure: KotlinDependencyHandler.() -> Unit)
fun dependencies(configureClosure: Closure<Any?>)
val apiConfigurationName: String
val implementationConfigurationName: String
val compileOnlyConfigurationName: String
val runtimeOnlyConfigurationName: String
val relatedConfigurationNames: List<String>
get() = listOf(apiConfigurationName, implementationConfigurationName, compileOnlyConfigurationName, runtimeOnlyConfigurationName)
}

View File

@@ -0,0 +1,51 @@
/*
* 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.gradle.plugin
import org.gradle.api.Named
import org.gradle.api.attributes.HasAttributes
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.SourceSetOutput
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
interface KotlinCompilation: Named, HasAttributes, HasKotlinDependencies {
val target: KotlinTarget
val compilationName: String
val kotlinSourceSets: List<KotlinSourceSet>
val compileDependencyConfigurationName: String
var compileDependencyFiles: FileCollection
val output: SourceSetOutput
val platformType get() = target.platformType
val compileKotlinTaskName: String
val compileAllTaskName: String
companion object {
const val MAIN_COMPILATION_NAME = "main"
const val TEST_COMPILATION_NAME = "test"
}
fun source(sourceSet: KotlinSourceSet)
override fun getName(): String = compilationName
}
interface KotlinCompilationToRunnableFiles : KotlinCompilation {
val runtimeDependencyConfigurationName: String
var runtimeDependencyFiles: FileCollection
}
interface KotlinCompilationWithResources : KotlinCompilation {
val processResourcesTaskName: String
}

View File

@@ -17,8 +17,8 @@
package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.Project
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
import java.io.File
open class SubpluginOption(val key: String, val value: String)
@@ -52,12 +52,12 @@ interface KotlinGradleSubplugin<in KotlinCompile : AbstractCompile> {
fun isApplicable(project: Project, task: AbstractCompile): Boolean
fun apply(
project: Project,
kotlinCompile: KotlinCompile,
javaCompile: AbstractCompile,
variantData: Any?,
androidProjectHandler: Any?,
javaSourceSet: SourceSet?
project: Project,
kotlinCompile: KotlinCompile,
javaCompile: AbstractCompile?,
variantData: Any?,
androidProjectHandler: Any?,
kotlinCompilation: KotlinCompilation?
): List<SubpluginOption>
fun getSubpluginKotlinTasks(

View File

@@ -0,0 +1,39 @@
/*
* 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.gradle.plugin
import org.gradle.api.Named
import org.gradle.api.attributes.Attribute
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.Serializable
// TODO: Do we really need a separate platform type for each native target?
open class KotlinPlatformType(private val name: String) : Named, Serializable {
override fun toString(): String = name
override fun getName(): String = name
companion object {
val COMMON = KotlinPlatformType("common")
val JVM = KotlinPlatformType("JVM")
val JS = KotlinPlatformType("JS")
val attribute = Attribute.of(
"org.jetbrains.kotlin.platform.type",
KotlinPlatformType::class.java
)
}
}
// TODO: Make KonanTarget serializable
data class KotlinNativePlatformType(val konanTargetName: String) : KotlinPlatformType(konanTargetName) {
val konanTarget: KonanTarget
get() = HostManager().targetByName(konanTargetName)
}
fun KonanTarget.toKotlinPlatformType(): KotlinNativePlatformType = KotlinNativePlatformType(name)

View File

@@ -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.kotlin.gradle.plugin
import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.attributes.HasAttributes
import org.gradle.api.internal.component.UsageContext
interface KotlinTarget: Named, HasAttributes {
val targetName: String
val disambiguationClassifier: String? get() = targetName
val platformType: KotlinPlatformType
val compilations: NamedDomainObjectContainer<out KotlinCompilation>
val project: Project
val artifactsTaskName: String
val defaultConfigurationName: String
val apiElementsConfigurationName: String
val runtimeElementsConfigurationName: String
fun createUsageContexts(): Set<UsageContext>
override fun getName(): String = targetName
}

View File

@@ -0,0 +1,12 @@
/*
* 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.gradle.plugin
import org.gradle.api.Named
interface KotlinTargetPreset<T: KotlinTarget> : Named {
fun createTarget(name: String): T
}

View File

@@ -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.gradle.plugin.source
import groovy.lang.Closure
import org.gradle.api.Named
import org.gradle.api.file.SourceDirectorySet
import org.jetbrains.kotlin.gradle.plugin.HasKotlinDependencies
interface KotlinSourceSet : Named, HasKotlinDependencies {
val kotlin: SourceDirectorySet
fun kotlin(configureClosure: Closure<Any?>): SourceDirectorySet
companion object {
const val COMMON_MAIN_SOURCE_SET_NAME = "commonMain"
const val COMMON_TEST_SOURCE_SET_NAME = "commonTest"
}
}
interface KotlinSourceSetWithResources : KotlinSourceSet {
val resources: SourceDirectorySet
}

View File

@@ -1,10 +1,3 @@
apply plugin: 'java'
apply plugin: 'java-gradle-plugin'
apply plugin: 'kotlin'
apply plugin: 'groovy'
apply plugin: 'org.jetbrains.dokka'
apply plugin: 'jps-compatible'
buildscript {
repositories {
jcenter()
@@ -14,6 +7,17 @@ buildscript {
}
}
plugins {
id 'com.github.jk1.tcdeps' version '0.17'
}
apply plugin: 'java'
apply plugin: 'java-gradle-plugin'
apply plugin: 'kotlin'
apply plugin: 'groovy'
apply plugin: 'org.jetbrains.dokka'
apply plugin: 'jps-compatible'
configureJvmProject(project)
configurePublishing(project)
@@ -21,6 +25,14 @@ repositories {
jcenter()
mavenLocal()
maven { url 'https://maven.google.com' }
teamcityServer {
url = "http://buildserver.labs.intellij.net"
credentials {
username = "guest"
password = "guest"
}
}
}
configurations {
@@ -31,8 +43,12 @@ pill {
variant = "FULL"
}
def kotlinNativeVersion = "0.9-dev-2798" // TODO: Get from root dir
dependencies {
compile project(':kotlin-gradle-plugin-api')
compileOnly tc("Kotlin_KotlinNative_Master_KotlinNativeLinuxDist:$kotlinNativeVersion:shared.jar")
compileOnly project(':compiler')
compileOnly project(':compiler:incremental-compilation-impl')
compileOnly project(':compiler:daemon-common')

View File

@@ -11,6 +11,8 @@ import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
import org.jetbrains.kotlin.gradle.internal.KaptTask
import org.jetbrains.kotlin.gradle.internal.KaptVariantData
import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
import org.jetbrains.kotlin.gradle.plugin.base.usesPlatformOf
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.io.File
@@ -36,13 +38,15 @@ class Android25ProjectHandler(kotlinConfigurationTools: KotlinConfigurationTools
}
}
override fun wireKotlinTasks(project: Project,
androidPlugin: BasePlugin,
androidExt: BaseExtension,
variantData: BaseVariant,
javaTask: AbstractCompile,
kotlinTask: KotlinCompile) {
override fun wireKotlinTasks(
project: Project,
compilation: KotlinJvmAndroidCompilation,
androidPlugin: BasePlugin,
androidExt: BaseExtension,
variantData: BaseVariant,
javaTask: AbstractCompile,
kotlinTask: KotlinCompile
) {
val preJavaKotlinOutputFiles = mutableListOf<File>().apply {
add(kotlinTask.destinationDir)
if (Kapt3GradleSubplugin.isEnabled(project)) {
@@ -57,8 +61,9 @@ class Android25ProjectHandler(kotlinConfigurationTools: KotlinConfigurationTools
kotlinTask.dependsOn(variantData.getSourceFolders(SourceKind.JAVA))
kotlinTask.mapClasspath {
val kotlinCompilationDependencies = compilation.compileDependencyFiles
val kotlinClasspath = variantData.getCompileClasspath(preJavaClasspathKey)
kotlinClasspath + project.files(AndroidGradleWrapper.getRuntimeJars(androidPlugin, androidExt))
kotlinClasspath + project.files(AndroidGradleWrapper.getRuntimeJars(androidPlugin, androidExt)) + kotlinCompilationDependencies
}
// Find the classpath entries that comes from the tested variant and register it as the friend path, lazily
@@ -95,6 +100,11 @@ class Android25ProjectHandler(kotlinConfigurationTools: KotlinConfigurationTools
return variantData.mergeResources?.computeResourceSetList0() ?: emptyList()
}
override fun setUpDependencyResolution(variant: BaseVariant, compilation: KotlinCompilation) {
variant.compileConfiguration.usesPlatformOf(compilation.target)
variant.runtimeConfiguration.usesPlatformOf(compilation.target)
}
private inner class KaptVariant(variantData: BaseVariant) : KaptVariantData<BaseVariant>(variantData) {
override val name: String = getVariantName(variantData)
override val sourceProviders: Iterable<SourceProvider> = getSourceProviders(variantData)

View File

@@ -0,0 +1,143 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.compilerRunner
import org.gradle.api.Named
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.util.DependencyProcessor
internal enum class KotlinNativeProjectProperty(val propertyName: String) {
KONAN_HOME ("konan.home"),
KONAN_JVM_ARGS ("konan.jvmArgs"),
KONAN_USE_ENVIRONMENT_VARIABLES("konan.useEnvironmentVariables"),
DOWNLOAD_COMPILER ("download.compiler"),
// Properties used instead of env vars until https://github.com/gradle/gradle/issues/3468 is fixed.
// TODO: Remove them when an API for env vars is provided.
KONAN_CONFIGURATION_BUILD_DIR ("konan.configuration.build.dir"),
KONAN_DEBUGGING_SYMBOLS ("konan.debugging.symbols"),
KONAN_OPTIMIZATIONS_ENABLE ("konan.optimizations.enable"),
KONAN_PUBLICATION_ENABLED ("konan.publication.enabled")
}
internal fun Project.hasProperty(property: KotlinNativeProjectProperty) = hasProperty(property.propertyName)
internal fun Project.findProperty(property: KotlinNativeProjectProperty): Any? = findProperty(property.propertyName)
internal fun Project.getProperty(property: KotlinNativeProjectProperty) = findProperty(property)
?: throw IllegalArgumentException("No such property in the project: ${property.propertyName}")
internal val Project.jvmArgs
get() = (findProperty(KotlinNativeProjectProperty.KONAN_JVM_ARGS) as String?)?.split("\\s+".toRegex()).orEmpty()
// konanHome extension is set by downloadKonanCompiler task.
internal val Project.konanHome: String
get() {
assert(hasProperty(KotlinNativeProjectProperty.KONAN_HOME))
return project.file(getProperty(KotlinNativeProjectProperty.KONAN_HOME)).canonicalPath
}
internal interface KonanToolRunner: Named {
val mainClass: String
val classpath: FileCollection
val jvmArgs: List<String>
val environment: Map<String, Any>
val additionalSystemProperties: Map<String, String>
fun run(args: List<String>)
fun run(vararg args: String) = run(args.toList())
}
internal abstract class KonanCliRunner(
val toolName: String,
val fullName: String,
val project: Project,
private val additionalJvmArgs: List<String>
): KonanToolRunner {
override val mainClass = "org.jetbrains.kotlin.cli.utilities.MainKt"
override fun getName() = toolName
// We need to unset some environment variables which are set by XCode and may potentially affect the tool executed.
protected val blacklistEnvironment: List<String> by lazy {
KonanToolRunner::class.java.getResourceAsStream("/env_blacklist")?.let { stream ->
stream.reader().use { it.readLines() }
} ?: emptyList<String>()
}
override val classpath: FileCollection =
project.fileTree("${project.konanHome}/konan/lib/")
.apply { include("*.jar") }
override val jvmArgs = mutableListOf("-ea").apply {
if (additionalJvmArgs.none { it.startsWith("-Xmx") } &&
project.jvmArgs.none { it.startsWith("-Xmx") }) {
add("-Xmx3G")
}
addAll(additionalJvmArgs)
addAll(project.jvmArgs)
}
override val additionalSystemProperties = mutableMapOf(
"konan.home" to project.konanHome,
"java.library.path" to "${project.konanHome}/konan/nativelib"
)
override val environment = mutableMapOf("LIBCLANG_DISABLE_CRASH_RECOVERY" to "1")
override fun run(args: List<String>) {
project.logger.info("Run tool: $toolName with args: ${args.joinToString(separator = " ")}")
if (classpath.isEmpty) {
throw IllegalStateException("Classpath of the tool is empty: $toolName\n" +
"Probably the 'konan.home' project property contains an incorrect path.\n" +
"Please change it to the compiler root directory and rerun the build.")
}
project.javaexec { spec ->
spec.main = mainClass
spec.classpath = classpath
spec.jvmArgs(jvmArgs)
spec.systemProperties(System.getProperties().map { (k, v) -> k.toString() to v }.toMap())
spec.systemProperties(additionalSystemProperties)
spec.args(listOf(toolName) + args)
blacklistEnvironment.forEach { spec.environment.remove(it) }
spec.environment(environment)
}
}
}
internal class KonanInteropRunner(project: Project, additionalJvmArgs: List<String> = emptyList())
: KonanCliRunner("cinterop", "Kotlin/Native cinterop tool", project, additionalJvmArgs)
{
init {
if (HostManager.host == KonanTarget.MINGW_X64) {
//TODO: Oh-ho-ho fix it in more convinient way.
environment.put("PATH", DependencyProcessor.defaultDependenciesRoot.absolutePath +
"\\msys2-mingw-w64-x86_64-gcc-7.2.0-clang-llvm-5.0.0-windows-x86-64" +
"\\bin;${environment.get("PATH")}")
}
}
}
internal class KonanCompilerRunner(project: Project, additionalJvmArgs: List<String> = emptyList())
: KonanCliRunner("konanc", "Kotlin/Native compiler", project, additionalJvmArgs)
internal class KonanKlibRunner(project: Project, additionalJvmArgs: List<String> = emptyList())
: KonanCliRunner("klib", "Klib management tool", project, additionalJvmArgs)

View File

@@ -0,0 +1,18 @@
/*
* 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.gradle.dsl
import org.gradle.api.NamedDomainObjectCollection
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset
open class KotlinMultiplatformExtension : KotlinProjectExtension() {
lateinit var presets: NamedDomainObjectCollection<KotlinTargetPreset<*>>
internal set
lateinit var targets: NamedDomainObjectCollection<KotlinTarget>
internal set
}

View File

@@ -16,21 +16,40 @@
package org.jetbrains.kotlin.gradle.dsl
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.internal.plugins.DslObject
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetProvider
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
import kotlin.reflect.KClass
internal fun Project.createKotlinExtension(extensionClass: KClass<out KotlinProjectExtension>) {
val kotlinExt = extensions.create("kotlin", extensionClass.java)
private const val KOTLIN_PROJECT_EXTENSION_NAME = "kotlin"
internal fun Project.createKotlinExtension(extensionClass: KClass<out KotlinProjectExtension>): KotlinProjectExtension {
val kotlinExt = extensions.create(KOTLIN_PROJECT_EXTENSION_NAME, extensionClass.java)
DslObject(kotlinExt).extensions.create("experimental", ExperimentalExtension::class.java)
return kotlinExtension
}
internal val Project.kotlinExtension: KotlinProjectExtension
get() = extensions.getByName(KOTLIN_PROJECT_EXTENSION_NAME) as KotlinProjectExtension
open class KotlinProjectExtension {
val experimental: ExperimentalExtension
get() = DslObject(this).extensions.getByType(ExperimentalExtension::class.java)!!
get() = DslObject(this).extensions.getByType(ExperimentalExtension::class.java)
var sourceSets: NamedDomainObjectContainer<out KotlinSourceSet>
@Suppress("UNCHECKED_CAST")
get() = DslObject(this).extensions.getByName("sourceSets") as NamedDomainObjectContainer<out KotlinSourceSet>
internal set(value) { DslObject(this).extensions.add("sourceSets", value) }
}
open class KotlinJvmProjectExtension : KotlinProjectExtension() {
open class KotlinSingleTargetProjectExtension : KotlinProjectExtension() {
internal lateinit var target: KotlinTarget
}
open class KotlinJvmProjectExtension : KotlinSingleTargetProjectExtension() {
/**
* With Gradle 4.0+, disables the separate output directory for Kotlin, falling back to sharing the deprecated
* single classes directory per source set. With Gradle < 4.0, has no effect.
@@ -38,6 +57,11 @@ open class KotlinJvmProjectExtension : KotlinProjectExtension() {
var copyClassesToJavaOutput = false
}
internal val KotlinProjectExtension.sourceSetProvider
get() = object : KotlinSourceSetProvider {
override fun provideSourceSet(displayName: String): KotlinSourceSet = sourceSets.maybeCreate(displayName)
}
open class ExperimentalExtension {
var coroutines: Coroutines? = null
}

View File

@@ -25,7 +25,6 @@ import com.android.build.gradle.internal.variant.TestVariantData
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.UnknownDomainObjectException
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@ -81,12 +80,12 @@ class AndroidSubplugin : KotlinGradleSubplugin<KotlinCompile> {
}
override fun apply(
project: Project,
kotlinCompile: KotlinCompile,
javaCompile: AbstractCompile,
variantData: Any?,
androidProjectHandler: Any?,
javaSourceSet: SourceSet?
project: Project,
kotlinCompile: KotlinCompile,
javaCompile: AbstractCompile?,
variantData: Any?,
androidProjectHandler: Any?,
kotlinCompilation: KotlinCompilation?
): List<SubpluginOption> {
val androidExtension = project.extensions.getByName("android") as? BaseExtension ?: return emptyList()
val androidExtensionsExtension = project.extensions.getByType(AndroidExtensionsExtension::class.java)

View File

@@ -1,71 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.gradle.internal
import groovy.lang.Closure
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.internal.file.DefaultSourceDirectorySet
import org.gradle.api.internal.file.FileResolver
import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetProvider
import java.lang.reflect.Constructor
internal class KotlinSourceSetProviderImpl constructor(private val fileResolver: FileResolver) : KotlinSourceSetProvider {
override fun create(displayName: String): KotlinSourceSet =
KotlinSourceSetImpl(displayName, fileResolver)
}
private class KotlinSourceSetImpl(displayName: String, resolver: FileResolver) : KotlinSourceSet {
override val kotlin: SourceDirectorySet =
createDefaultSourceDirectorySet(displayName + " Kotlin source", resolver)
init {
kotlin.filter?.include("**/*.java", "**/*.kt", "**/*.kts")
}
override fun kotlin(configureClosure: Closure<Any?>?): KotlinSourceSet {
ConfigureUtil.configure(configureClosure, kotlin)
return this
}
}
private val createDefaultSourceDirectorySet: (name: String?, resolver: FileResolver?) -> SourceDirectorySet = run {
val klass = DefaultSourceDirectorySet::class.java
val defaultConstructor = klass.constructorOrNull(String::class.java, FileResolver::class.java)
if (defaultConstructor != null && defaultConstructor.getAnnotation(java.lang.Deprecated::class.java) == null) {
// TODO: drop when gradle < 2.12 are obsolete
{ name, resolver -> defaultConstructor.newInstance(name, resolver) }
}
else {
val directoryFileTreeFactoryClass = Class.forName("org.gradle.api.internal.file.collections.DirectoryFileTreeFactory")
val alternativeConstructor = klass.getConstructor(String::class.java, FileResolver::class.java, directoryFileTreeFactoryClass)
val defaultFileTreeFactoryClass = Class.forName("org.gradle.api.internal.file.collections.DefaultDirectoryFileTreeFactory")
val defaultFileTreeFactory = defaultFileTreeFactoryClass.getConstructor().newInstance()
return@run { name, resolver -> alternativeConstructor.newInstance(name, resolver, defaultFileTreeFactory) }
}
}
private fun <T> Class<T>.constructorOrNull(vararg parameterTypes: Class<*>): Constructor<T>? =
try {
getConstructor(*parameterTypes)
}
catch (e: NoSuchMethodException) {
null
}

View File

@@ -139,10 +139,10 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
private inner class Kapt3SubpluginContext(
val project: Project,
val kotlinCompile: KotlinCompile,
val javaCompile: AbstractCompile,
val javaCompile: AbstractCompile?,
val kaptVariantData: KaptVariantData<*>?,
val sourceSetName: String,
val javaSourceSet: SourceSet?,
val kotlinCompilation: KotlinCompilation?,
val kaptExtension: KaptExtension,
val kaptClasspathConfigurations: List<Configuration>
) {
@@ -152,14 +152,14 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
}
override fun apply(
project: Project,
kotlinCompile: KotlinCompile,
javaCompile: AbstractCompile,
variantData: Any?,
androidProjectHandler: Any?,
javaSourceSet: SourceSet?
project: Project,
kotlinCompile: KotlinCompile,
javaCompile: AbstractCompile?,
variantData: Any?,
androidProjectHandler: Any?,
kotlinCompilation: KotlinCompilation?
): List<SubpluginOption> {
assert((variantData != null) xor (javaSourceSet != null))
assert((variantData != null) xor (kotlinCompilation != null))
val buildDependencies = arrayListOf<TaskDependency>()
val kaptConfigurations = arrayListOf<Configuration>()
@@ -181,10 +181,10 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
kaptVariantData.name
}
else {
if (javaSourceSet == null) error("Java source set should not be null")
if (kotlinCompilation == null) error("Java source set should not be null")
handleSourceSet(javaSourceSet.name)
javaSourceSet.name
handleSourceSet(kotlinCompilation.compilationName)
kotlinCompilation.compilationName
}
val kaptExtension = project.extensions.getByType(KaptExtension::class.java)
@@ -193,7 +193,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
val context = Kapt3SubpluginContext(
project, kotlinCompile, javaCompile,
kaptVariantData, sourceSetName, javaSourceSet, kaptExtension, nonEmptyKaptConfigurations
kaptVariantData, sourceSetName, kotlinCompilation, kaptExtension, nonEmptyKaptConfigurations
)
val kaptGenerateStubsTask = context.createKaptGenerateStubsTask()
@@ -226,7 +226,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
pluginOptions += SubpluginOption("aptMode", aptMode)
disableAnnotationProcessingInJavaTask()
javaCompile.source(generatedFilesDir)
javaCompile?.source(generatedFilesDir)
pluginOptions += FilesSubpluginOption("sources", listOf(generatedFilesDir))
pluginOptions += FilesSubpluginOption("classes", listOf(getKaptGeneratedClassesDir(project, sourceSetName)))
@@ -342,18 +342,22 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
kaptTask.kotlinSourcesDestinationDir = kotlinSourcesOutputDir
kaptTask.classesDir = classesOutputDir
javaSourceSet?.output?.apply {
if (tryAddClassesDir { project.files(classesOutputDir).builtBy(kaptTask) }) {
kotlinCompile.attachClassesDir { classesOutputDir }
kotlinCompilation?.run {
output.apply {
if (tryAddClassesDir { project.files(classesOutputDir).builtBy(kaptTask) }) {
kotlinCompile.attachClassesDir { classesOutputDir }
}
}
}
kotlinCompile.source(sourcesOutputDir, kotlinSourcesOutputDir)
if (kaptVariantData != null) {
kaptVariantData.registerGeneratedJavaSource(project, kaptTask, javaCompile)
} else {
registerGeneratedJavaSource(kaptTask, javaCompile)
if (javaCompile != null) {
if (kaptVariantData != null) {
kaptVariantData.registerGeneratedJavaSource(project, kaptTask, javaCompile)
} else {
registerGeneratedJavaSource(kaptTask, javaCompile)
}
}
kaptTask.kaptClasspathConfigurations = kaptClasspathConfigurations

View File

@@ -131,7 +131,7 @@ open class KaptJavacOptionsDelegate {
fun execute(closure: Closure<*>) = executeClosure(closure)
}
private fun Any?.executeClosure(closure: Closure<*>) {
internal fun Any?.executeClosure(closure: Closure<*>) {
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure.delegate = this
closure.call()

View File

@@ -21,10 +21,10 @@ import org.gradle.api.*
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.SourceSetContainer
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.utils.matchSymmetricallyByNames
abstract class KotlinPlatformPluginBase(protected val platformName: String) : Plugin<Project> {
companion object {
@@ -110,70 +110,65 @@ open class KotlinPlatformImplementationPluginBase(platformName: String) : Kotlin
// Since the two projects may add source sets in arbitrary order, and both may do that after the plugin is applied,
// we need to handle all source sets of the two projects and connect them once we get a match:
// todo warn if no match found
matchSymmetricallyByNames(commonProject.sourceSets, namedSourceSetsContainer(platformProject)) { commonSourceSet, _ ->
matchSymmetricallyByNames(
getKotlinSourceSetsSafe(commonProject),
namedSourceSetsContainer(platformProject)
) { commonSourceSet: Named, _ ->
addCommonSourceSetToPlatformSourceSet(commonSourceSet, platformProject)
}
}
}
/**
* Applies [whenMatched] to pairs of items with the same name in [containerA] and [containerB],
* regardless of the order in which they are added to the containers.
*/
private fun <A, B> matchSymmetricallyByNames(
containerA: NamedDomainObjectContainer<A>,
containerB: NamedDomainObjectContainer<B>,
whenMatched: (A, B) -> Unit
) {
val matchedNames = mutableSetOf<String>()
fun <T, R> NamedDomainObjectContainer<T>.matchAllWith(other: NamedDomainObjectContainer<R>, match: (T, R) -> Unit) {
this@matchAllWith.all { item ->
val itemName = this@matchAllWith.namer.determineName(item)
if (itemName !in matchedNames) {
val otherItem = other.findByName(itemName)
if (otherItem != null) {
matchedNames += itemName
match(item, otherItem)
}
}
}
}
containerA.matchAllWith(containerB) { a, b -> whenMatched(a, b) }
containerB.matchAllWith(containerA) { b, a -> whenMatched(a, b) }
}
protected open fun namedSourceSetsContainer(project: Project): NamedDomainObjectContainer<*> =
project.sourceSets
project.kotlinExtension.sourceSets
protected open fun addCommonSourceSetToPlatformSourceSet(commonSourceSet: SourceSet, platformProject: Project) {
protected open fun addCommonSourceSetToPlatformSourceSet(commonSourceSet: Named, platformProject: Project) {
val platformTask = platformProject.tasks
.filterIsInstance<AbstractKotlinCompile<*>>()
.firstOrNull { it.sourceSetName == commonSourceSet.name }
platformTask?.source(commonSourceSet.kotlin!!)
platformTask?.source(getKotlinSourceDirectorySetSafe(commonSourceSet))
}
protected val SourceSet.kotlin: SourceDirectorySet?
get() {
// Access through reflection, because another project's KotlinSourceSet might be loaded
// by a different class loader:
val convention = (getConvention("kotlin") ?: getConvention("kotlin2js")) ?: return null
val kotlinSourceSetIface = convention.javaClass.interfaces.find { it.name == KotlinSourceSet::class.qualifiedName }
val getKotlin = kotlinSourceSetIface?.methods?.find { it.name == "getKotlin" } ?: return null
return getKotlin(convention) as? SourceDirectorySet
private fun getKotlinSourceSetsSafe(project: Project): NamedDomainObjectCollection<out Named> {
// Access through reflection, because another project's KotlinBaseSourceSet might be loaded
// by a different class loader:
val kotlinExt = project.extensions.getByName("kotlin")
@Suppress("UNCHECKED_CAST")
val sourceSets = kotlinExt.javaClass.getMethod("getSourceSets").invoke(kotlinExt) as NamedDomainObjectCollection<out Named>
return sourceSets
}
protected fun getKotlinSourceDirectorySetSafe(from: Any): SourceDirectorySet? {
// Access through reflection, because another project's KotlinBaseSourceSet might be loaded
// by a different class loader:
fun getImplementedKotlinSourceSetInterface(from: Any): Class<*>? {
fun findImplementedInterface(name: String, derivedClass: Class<*>): Class<*>? {
if (derivedClass.canonicalName == name)
return derivedClass
else {
for (parentInterface in derivedClass.interfaces + derivedClass.superclass) {
findImplementedInterface(name, parentInterface)?.let { return it }
}
return null
}
}
return findImplementedInterface(KotlinSourceSet::class.qualifiedName!!, from.javaClass)
}
companion object {
@JvmStatic
protected fun <T> Project.whenEvaluated(fn: Project.() -> T) {
if (state.executed) {
fn()
}
else {
afterEvaluate { it.fn() }
}
}
val getKotlin = from.javaClass.getMethod("getKotlin")
return getKotlin(from) as? SourceDirectorySet
}
}
internal fun <T> Project.whenEvaluated(fn: Project.() -> T) {
if (state.executed) {
fn()
}
else {
afterEvaluate { it.fn() }
}
}
@@ -190,11 +185,12 @@ open class KotlinPlatformAndroidPlugin : KotlinPlatformImplementationPluginBase(
override fun namedSourceSetsContainer(project: Project): NamedDomainObjectContainer<*> =
(project.extensions.getByName("android") as BaseExtension).sourceSets
override fun addCommonSourceSetToPlatformSourceSet(commonSourceSet: SourceSet, platformProject: Project) {
override fun addCommonSourceSetToPlatformSourceSet(commonSourceSet: Named, platformProject: Project) {
val androidExtension = platformProject.extensions.getByName("android") as BaseExtension
val androidSourceSet = androidExtension.sourceSets.findByName(commonSourceSet.name) ?: return
val kotlinSourceSet = androidSourceSet.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet ?: return
kotlinSourceSet.kotlin.source(commonSourceSet.kotlin!!)
val kotlinSourceSet = androidSourceSet.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet
?: return
kotlinSourceSet.kotlin.source(getKotlinSourceDirectorySetSafe(commonSourceSet)!!)
}
}
@@ -210,7 +206,4 @@ open class KotlinPlatformJsPlugin : KotlinPlatformImplementationPluginBase("js")
project.applyPlugin<Kotlin2JsPluginWrapper>()
super.apply(project)
}
}
private val Project.sourceSets: SourceSetContainer
get() = convention.getPlugin(JavaPluginConvention::class.java).sourceSets
}

View File

@@ -3,9 +3,9 @@ package org.jetbrains.kotlin.gradle.plugin
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.BasePlugin
import com.android.builder.model.SourceProvider
import com.intellij.openapi.util.text.StringUtil.compareVersionNumbers
import groovy.lang.Closure
import org.gradle.api.*
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ExternalDependency
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
@@ -13,7 +13,6 @@ import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.gradle.api.plugins.InvalidPluginException
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.CompileClasspathNormalizer
@@ -25,10 +24,15 @@ import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetProjectExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.dsl.sourceSetProvider
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedClassesDir
import org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin
import org.jetbrains.kotlin.gradle.internal.KaptVariantData
import org.jetbrains.kotlin.gradle.internal.checkAndroidAnnotationProcessorDependencyUsage
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.utils.*
@@ -45,58 +49,90 @@ val KOTLIN_JS_DSL_NAME = "kotlin2js"
val KOTLIN_OPTIONS_DSL_NAME = "kotlinOptions"
internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
val project: Project,
val javaBasePlugin: JavaBasePlugin,
val sourceSet: SourceSet,
val tasksProvider: KotlinTasksProvider,
val kotlinSourceSetProvider: KotlinSourceSetProvider,
val dslExtensionName: String,
val compileTaskNameSuffix: String,
val taskDescription: String
val project: Project,
val tasksProvider: KotlinTasksProvider,
val taskDescription: String,
val kotlinCompilation: KotlinCompilation
) {
abstract protected fun doTargetSpecificProcessing()
protected val logger = Logging.getLogger(this.javaClass)!!
protected val isSeparateClassesDirSupported: Boolean by lazy {
!CopyClassesToJavaOutputStatus.isEnabled(project) &&
sourceSet.output.javaClass.methods.any { it.name == "getClassesDirs" }
kotlinCompilation.output.javaClass.methods.any { it.name == "getClassesDirs" }
}
protected val sourceSetName: String = sourceSet.name
protected val sourceRootDir: String = "src/$sourceSetName/kotlin"
protected val kotlinSourceSet: KotlinSourceSet = createKotlinSourceSet()
protected val sourceSetName: String = kotlinCompilation.compilationName
protected val kotlinTask: T = createKotlinCompileTask()
protected open val defaultKotlinDestinationDir: File
get() = if (isSeparateClassesDirSupported)
File(project.buildDir, "classes/kotlin/${sourceSet.name}") else
sourceSet.output.classesDir
get() {
return if (isSeparateClassesDirSupported) {
val kotlinExt = project.kotlinExtension
val targetSubDirectory =
if (kotlinExt is KotlinSingleTargetProjectExtension)
"" // In single-target projects, don't add the target name part to this path
else
kotlinCompilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty()
File(project.buildDir, "classes/kotlin/$targetSubDirectory${kotlinCompilation.compilationName}")
} else {
kotlinCompilation.output.classesDir
}
}
fun run() {
addKotlinDirSetToSources()
private fun createKotlinCompileTask(): T {
val name = kotlinCompilation.compileKotlinTaskName
logger.kotlinDebug("Creating kotlin compile task $name")
val kotlinCompile = doCreateTask(project, name)
kotlinCompile.description = taskDescription
kotlinCompile.mapClasspath { kotlinCompilation.compileDependencyFiles }
kotlinCompile.setDestinationDir { defaultKotlinDestinationDir }
kotlinCompilation.output.tryAddClassesDir { project.files(kotlinTask.destinationDir).builtBy(kotlinTask) }
return kotlinCompile
}
open fun run() {
doTargetSpecificProcessing()
}
private fun createKotlinSourceSet(): KotlinSourceSet {
logger.kotlinDebug("Creating KotlinSourceSet for $sourceSet")
val kotlinSourceSet = kotlinSourceSetProvider.create(sourceSet.name)
kotlinSourceSet.kotlin.srcDir(project.file(sourceRootDir))
sourceSet.addConvention(dslExtensionName, kotlinSourceSet)
return kotlinSourceSet
protected abstract fun doCreateTask(project: Project, taskName: String): T
}
internal abstract class KotlinJavaSourceSetProcessor<T : AbstractKotlinCompile<*>>(
project: Project,
tasksProvider: KotlinTasksProvider,
taskDescription: String,
val javaSourceSet: SourceSet?,
kotlinCompilation: KotlinCompilation
): KotlinSourceSetProcessor<T>(
project, tasksProvider, taskDescription, kotlinCompilation
) {
override fun run() {
addKotlinDirSetToSources()
super.run()
}
private fun addKotlinDirSetToSources() {
val kotlinDirSet = kotlinSourceSet.kotlin
if (javaSourceSet == null)
return
val kotlinDirSets = kotlinCompilation.kotlinSourceSets.map(KotlinSourceSet::kotlin)
// Try to avoid duplicate Java sources in allSource:
val kotlinSrcDirsToAdd = filterOutJavaSrcDirsIfPossible(kotlinDirSet)
val kotlinSrcDirsToAdd = kotlinDirSets.map { filterOutJavaSrcDirsIfPossible(it) }
sourceSet.allJava.srcDirs(kotlinSrcDirsToAdd)
sourceSet.allSource.srcDirs(kotlinSrcDirsToAdd)
sourceSet.resources.filter.exclude { it.file in kotlinDirSet }
kotlinSrcDirsToAdd.forEach { kotlinSrcDirs ->
javaSourceSet.allJava.srcDirs(kotlinSrcDirs)
javaSourceSet.allSource.srcDirs(kotlinSrcDirs)
javaSourceSet.resources.filter.exclude { it.file in kotlinSrcDirs }
}
}
private fun filterOutJavaSrcDirsIfPossible(sourceDirectorySet: SourceDirectorySet): FileCollection {
if (javaSourceSet == null)
return sourceDirectorySet
// If the API used below is not available, fall back to not filtering the Java sources.
if (SourceDirectorySet::class.java.methods.none { it.name == "getSourceDirectories" }) {
return sourceDirectorySet
@@ -108,35 +144,19 @@ internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
}
// Build a lazily-resolved file collection that filters out Java sources from sources of this sourceDirectorySet
return getSourceDirectories(sourceDirectorySet).minus(getSourceDirectories(sourceSet.java))
return getSourceDirectories(sourceDirectorySet).minus(getSourceDirectories(javaSourceSet.java))
}
private fun createKotlinCompileTask(): T {
val name = sourceSet.getCompileTaskName(compileTaskNameSuffix)
logger.kotlinDebug("Creating kotlin compile task $name")
val kotlinCompile = doCreateTask(project, name)
kotlinCompile.description = taskDescription
kotlinCompile.mapClasspath { sourceSet.compileClasspath }
kotlinCompile.setDestinationDir { defaultKotlinDestinationDir }
sourceSet.output.tryAddClassesDir { project.files(kotlinTask.destinationDir).builtBy(kotlinTask) }
return kotlinCompile
}
protected abstract fun doCreateTask(project: Project, taskName: String): T
}
internal class Kotlin2JvmSourceSetProcessor(
project: Project,
javaBasePlugin: JavaBasePlugin,
sourceSet: SourceSet,
tasksProvider: KotlinTasksProvider,
kotlinSourceSetProvider: KotlinSourceSetProvider,
private val kotlinPluginVersion: String
) : KotlinSourceSetProcessor<KotlinCompile>(
project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider,
dslExtensionName = KOTLIN_DSL_NAME,
compileTaskNameSuffix = "kotlin",
taskDescription = "Compiles the $sourceSet.kotlin."
project: Project,
tasksProvider: KotlinTasksProvider,
kotlinCompilation: KotlinCompilation,
private val kotlinPluginVersion: String
) : KotlinJavaSourceSetProcessor<KotlinCompile>(
project, tasksProvider, taskDescription = "Compiles the $kotlinCompilation.",
javaSourceSet = if (kotlinCompilation is KotlinWithJavaCompilation) kotlinCompilation.javaSourceSet else null,
kotlinCompilation = kotlinCompilation
) {
override val defaultKotlinDestinationDir: File
get() = if (!isSeparateClassesDirSupported)
@@ -144,44 +164,44 @@ internal class Kotlin2JvmSourceSetProcessor(
super.defaultKotlinDestinationDir
override fun doCreateTask(project: Project, taskName: String): KotlinCompile =
tasksProvider.createKotlinJVMTask(project, taskName, sourceSet.name)
tasksProvider.createKotlinJVMTask(project, taskName, kotlinCompilation.compilationName)
override fun doTargetSpecificProcessing() {
kotlinSourceSet.kotlin.source(sourceSet.java)
Kapt3KotlinGradleSubplugin.createAptConfigurationIfNeeded(project, sourceSet.name)
Kapt3KotlinGradleSubplugin.createAptConfigurationIfNeeded(project, kotlinCompilation.compilationName)
project.afterEvaluate { project ->
if (project != null) {
val javaTask = project.tasks.findByName(sourceSet.compileJavaTaskName)
val javaTask = javaSourceSet?.let { project.tasks.findByName(it.compileJavaTaskName) as JavaCompile }
val subpluginEnvironment = loadSubplugins(project, kotlinPluginVersion)
val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
project, kotlinTask, javaTask as JavaCompile, null, null, sourceSet)
val subpluginEnvironment = loadSubplugins(project, kotlinPluginVersion)
val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
project, kotlinTask, javaTask, null, null, kotlinCompilation
)
// KotlinCompile.source(kotlinDirSet) should be called only after all java roots are added to kotlinDirSet
// otherwise some java roots can be ignored
kotlinTask.source(kotlinSourceSet.kotlin)
appliedPlugins
.flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) }
.forEach { it.source(kotlinSourceSet.kotlin) }
// KotlinCompile.source(kotlinDirSet) should be called only after all java roots are added to kotlinDirSet
// otherwise some java roots can be ignored
for (compiledSourceSet in kotlinCompilation.kotlinSourceSets) {
kotlinTask.source(compiledSourceSet.kotlin)
}
configureJavaTask(kotlinTask, javaTask, logger)
appliedPlugins
.flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) }
.forEach { plugin -> kotlinCompilation.kotlinSourceSets.forEach { sourceSet -> plugin.source(sourceSet.kotlin) } }
var syncOutputTask: SyncOutputTask? = null
javaTask?.let { configureJavaTask(kotlinTask, it, logger) }
if (!isSeparateClassesDirSupported) {
syncOutputTask = createSyncOutputTask(project, kotlinTask, javaTask, sourceSetName)
}
if (project.pluginManager.hasPlugin("java-library") && sourceSetName == SourceSet.MAIN_SOURCE_SET_NAME) {
val (classesProviderTask, classesDirectory) = when {
isSeparateClassesDirSupported -> kotlinTask.let { it to it.destinationDir }
else -> syncOutputTask!!.let { it to it.javaOutputDir }
}
registerKotlinOutputForJavaLibrary(classesDirectory, classesProviderTask)
var syncOutputTask: SyncOutputTask? = null
if (!isSeparateClassesDirSupported && javaTask != null) {
syncOutputTask = createSyncOutputTask(project, kotlinTask, javaTask, sourceSetName)
}
if (project.pluginManager.hasPlugin("java-library") && sourceSetName == SourceSet.MAIN_SOURCE_SET_NAME) {
val (classesProviderTask, classesDirectory) = when {
isSeparateClassesDirSupported -> kotlinTask.let { it to it.destinationDir }
else -> syncOutputTask!!.let { it to it.javaOutputDir }
}
registerKotlinOutputForJavaLibrary(classesDirectory, classesProviderTask)
}
}
}
@@ -231,37 +251,38 @@ internal fun SourceSetOutput.tryAddClassesDir(
}
internal class Kotlin2JsSourceSetProcessor(
project: Project,
javaBasePlugin: JavaBasePlugin,
sourceSet: SourceSet,
tasksProvider: KotlinTasksProvider,
kotlinSourceSetProvider: KotlinSourceSetProvider,
private val kotlinPluginVersion: String
project: Project,
tasksProvider: KotlinTasksProvider,
kotlinCompilation: KotlinCompilation,
private val kotlinPluginVersion: String
) : KotlinSourceSetProcessor<Kotlin2JsCompile>(
project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider,
dslExtensionName = KOTLIN_JS_DSL_NAME,
taskDescription = "Compiles the kotlin sources in $sourceSet to JavaScript.",
compileTaskNameSuffix = "kotlin2Js"
project, tasksProvider, taskDescription = "Compiles the Kotlin sources in $kotlinCompilation to JavaScript.",
kotlinCompilation = kotlinCompilation
) {
override fun doCreateTask(project: Project, taskName: String): Kotlin2JsCompile =
tasksProvider.createKotlinJSTask(project, taskName, sourceSet.name)
tasksProvider.createKotlinJSTask(project, taskName, kotlinCompilation.compilationName)
override fun doTargetSpecificProcessing() {
project.tasks.findByName(sourceSet.classesTaskName)!!.dependsOn(kotlinTask)
kotlinTask.source(kotlinSourceSet.kotlin)
project.tasks.findByName(kotlinCompilation.compileAllTaskName)!!.dependsOn(kotlinTask)
createCleanSourceMapTask()
sourceSet.clearJavaSrcDirs()
//TODO check for complete removal, as we don't need Java anymore
// sourceSet.clearJavaSrcDirs()
// outputFile can be set later during the configuration phase, get it only after the phase:
project.afterEvaluate { project ->
val subpluginEnvironment: SubpluginEnvironment = loadSubplugins(project, kotlinPluginVersion)
val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
project, kotlinTask, kotlinTask, null, null, sourceSet)
kotlinTask.kotlinOptions.outputFile = kotlinTask.outputFile.absolutePath
val outputDir = kotlinTask.outputFile.parentFile
for (compiledSourceSet in kotlinCompilation.kotlinSourceSets) {
kotlinTask.source(compiledSourceSet.kotlin)
}
val subpluginEnvironment: SubpluginEnvironment = loadSubplugins(project, kotlinPluginVersion)
val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
project, kotlinTask, null, null, null, kotlinCompilation)
if (outputDir.isParentOf(project.rootDir))
throw InvalidUserDataException(
"The output directory '$outputDir' (defined by outputFile of $kotlinTask) contains or " +
@@ -272,17 +293,17 @@ internal class Kotlin2JsSourceSetProcessor(
kotlinTask.destinationDir = outputDir
if (!isSeparateClassesDirSupported) {
sourceSet.output.setClassesDirCompatible(kotlinTask.destinationDir)
kotlinCompilation.output.setClassesDirCompatible(kotlinTask.destinationDir)
}
appliedPlugins
.flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) }
.forEach { it.source(kotlinSourceSet.kotlin) }
.forEach { task -> kotlinCompilation.kotlinSourceSets.forEach { sourceSet -> task.source(sourceSet.kotlin) } }
}
}
private fun createCleanSourceMapTask() {
val taskName = sourceSet.getTaskName("clean", "sourceMap")
val taskName = kotlinCompilation.composeName("clean", "sourceMap")
val task = project.tasks.create(taskName, Delete::class.java)
task.onlyIf { kotlinTask.kotlinOptions.sourceMap }
task.delete(object : Closure<String>(this) {
@@ -293,178 +314,265 @@ internal class Kotlin2JsSourceSetProcessor(
}
internal class KotlinCommonSourceSetProcessor(
project: Project,
javaBasePlugin: JavaBasePlugin,
sourceSet: SourceSet,
tasksProvider: KotlinTasksProvider,
kotlinSourceSetProvider: KotlinSourceSetProvider,
private val kotlinPluginVersion: String
project: Project,
compilation: KotlinCompilation,
tasksProvider: KotlinTasksProvider,
private val kotlinPluginVersion: String
) : KotlinSourceSetProcessor<KotlinCompileCommon>(
project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider,
dslExtensionName = KOTLIN_DSL_NAME,
taskDescription = "Compiles the kotlin sources in $sourceSet to Metadata.",
compileTaskNameSuffix = "kotlinCommon"
project, tasksProvider, taskDescription = "Compiles the kotlin sources in $compilation to Metadata.",
kotlinCompilation = compilation
) {
override fun doTargetSpecificProcessing() {
sourceSet.clearJavaSrcDirs()
project.tasks.findByName(kotlinCompilation.compileAllTaskName)!!.dependsOn(kotlinTask)
// can be missing (e.g. in case of tests)
if (kotlinCompilation.compilationName == KotlinCompilation.MAIN_COMPILATION_NAME) {
project.tasks.findByName(kotlinCompilation.target.artifactsTaskName)?.dependsOn(kotlinTask)
}
project.afterEvaluate { project ->
kotlinTask.source(kotlinSourceSet.kotlin)
kotlinCompilation.kotlinSourceSets.forEach { sourceSet ->
kotlinTask.source(sourceSet.kotlin)
}
val subpluginEnvironment: SubpluginEnvironment = loadSubplugins(project, kotlinPluginVersion)
val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
project, kotlinTask, kotlinTask, null, null, sourceSet)
project.tasks.findByName(sourceSet.classesTaskName)!!.dependsOn(kotlinTask)
// can be missing (e.g. in case of tests)
project.tasks.findByName(sourceSet.jarTaskName)?.dependsOn(kotlinTask)
project, kotlinTask, null, null, null, kotlinCompilation
)
appliedPlugins
.flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) }
.forEach { it.source(kotlinSourceSet.kotlin) }
.flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) }
.forEach { kotlinCompilation.kotlinSourceSets.forEach { sourceSet -> it.source(sourceSet.kotlin) } }
}
}
override fun doCreateTask(project: Project, taskName: String): KotlinCompileCommon =
tasksProvider.createKotlinCommonTask(project, taskName, sourceSet.name)
tasksProvider.createKotlinCommonTask(project, taskName, kotlinCompilation.compilationName)
}
internal abstract class AbstractKotlinPlugin(
val tasksProvider: KotlinTasksProvider,
val kotlinSourceSetProvider: KotlinSourceSetProvider,
protected val kotlinPluginVersion: String
val tasksProvider: KotlinTasksProvider,
protected val kotlinPluginVersion: String
) : Plugin<Project> {
internal abstract fun buildSourceSetProcessor(project: Project, javaBasePlugin: JavaBasePlugin, sourceSet: SourceSet, kotlinPluginVersion: String): KotlinSourceSetProcessor<*>
internal abstract fun buildSourceSetProcessor(project: Project, compilation: KotlinCompilation, kotlinPluginVersion: String): KotlinSourceSetProcessor<*>
protected abstract val platformType: KotlinPlatformType
override fun apply(project: Project) {
val javaBasePlugin = project.plugins.apply(JavaBasePlugin::class.java)
val javaPluginConvention = project.convention.getPlugin(JavaPluginConvention::class.java)
project.plugins.apply(JavaPlugin::class.java)
configureSourceSetDefaults(project, javaBasePlugin, javaPluginConvention)
configureDefaultVersionsResolutionStrategy(project)
configureClassInspectionForIC(project)
configureTarget(
(project.kotlinExtension as KotlinSingleTargetProjectExtension).target,
{ compilation -> buildSourceSetProcessor(project, compilation, kotlinPluginVersion) }
)
configureProjectGlobalSettings(project, kotlinPluginVersion)
}
open protected fun configureSourceSetDefaults(
project: Project,
javaBasePlugin: JavaBasePlugin,
javaPluginConvention: JavaPluginConvention
) {
javaPluginConvention.sourceSets?.all { sourceSet ->
buildSourceSetProcessor(project, javaBasePlugin, sourceSet, kotlinPluginVersion).run()
companion object {
fun configureProjectGlobalSettings(project: Project, kotlinPluginVersion: String) {
configureDefaultVersionsResolutionStrategy(project, kotlinPluginVersion)
configureClassInspectionForIC(project)
}
}
private fun configureDefaultVersionsResolutionStrategy(project: Project) {
project.configurations.all { configuration ->
if (isGradleVersionAtLeast(4, 4)) {
// Use the API introduced in Gradle 4.4 to modify the dependencies directly before they are resolved:
configuration.withDependencies { dependencySet ->
dependencySet.filterIsInstance<ExternalDependency>()
.filter { it.group == "org.jetbrains.kotlin" && it.version.isNullOrEmpty() }
.forEach { it.version { constraint -> constraint.prefer(kotlinPluginVersion) } }
}
fun configureTarget(
target: KotlinTarget,
buildSourceSetProcessor: (KotlinCompilation) -> KotlinSourceSetProcessor<*>
) {
setUpJavaSourceSets(target)
configureSourceSetDefaults(target, buildSourceSetProcessor)
}
private fun configureClassInspectionForIC(project: Project) {
val classesTask = project.tasks.findByName(JavaPlugin.CLASSES_TASK_NAME)
val jarTask = project.tasks.findByName(JavaPlugin.JAR_TASK_NAME)
if (classesTask == null || jarTask !is Jar) {
project.logger.info(
"Could not configure class inspection task " +
"(classes task = ${classesTask?.javaClass?.canonicalName}, " +
"jar task = ${classesTask?.javaClass?.canonicalName}"
)
return
}
else {
configuration.resolutionStrategy.eachDependency { details ->
val requested = details.requested
if (requested.group == "org.jetbrains.kotlin" && requested.version.isEmpty()) {
details.useVersion(kotlinPluginVersion)
}
}
val inspectTask = project.tasks.create("inspectClassesForKotlinIC", InspectClassesForMultiModuleIC::class.java)
inspectTask.sourceSetName = SourceSet.MAIN_SOURCE_SET_NAME
inspectTask.jarTask = jarTask
inspectTask.dependsOn(classesTask)
jarTask.dependsOn(inspectTask)
}
private fun setUpJavaSourceSets(
kotlinTarget: KotlinTarget
) {
val project = kotlinTarget.project
val javaSourceSets = project.convention.getPlugin(JavaPluginConvention::class.java).sourceSets
javaSourceSets.all { javaSourceSet ->
val kotlinCompilation = kotlinTarget.compilations.maybeCreate(javaSourceSet.name) as KotlinWithJavaCompilation
kotlinCompilation.source(javaSourceSet)
val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(kotlinCompilation.fullName)
javaSourceSet.addConvention(KOTLIN_DSL_NAME, kotlinSourceSet)
kotlinCompilation.source(kotlinSourceSet)
}
kotlinTarget.compilations.all { kotlinCompilation -> javaSourceSets.maybeCreate(kotlinCompilation.fullName) }
}
fun configureSourceSetDefaults(
kotlinTarget: KotlinTarget,
buildSourceSetProcessor: (KotlinCompilation) -> KotlinSourceSetProcessor<*>
) {
kotlinTarget.compilations.all { compilation ->
buildSourceSetProcessor(compilation).run()
}
}
}
}
private fun configureClassInspectionForIC(project: Project) {
val classesTask = project.tasks.findByName(JavaPlugin.CLASSES_TASK_NAME)
val jarTask = project.tasks.findByName(JavaPlugin.JAR_TASK_NAME)
if (classesTask == null || jarTask !is Jar) {
project.logger.info(
"Could not configure class inspection task " +
"(classes task = ${classesTask?.javaClass?.canonicalName}, " +
"jar task = ${classesTask?.javaClass?.canonicalName}"
)
return
internal fun configureDefaultVersionsResolutionStrategy(project: Project, kotlinPluginVersion: String) {
project.configurations.all { configuration ->
if (isGradleVersionAtLeast(4, 4)) {
// Use the API introduced in Gradle 4.4 to modify the dependencies directly before they are resolved:
configuration.withDependencies { dependencySet ->
dependencySet.filterIsInstance<ExternalDependency>()
.filter { it.group == "org.jetbrains.kotlin" && it.version.isNullOrEmpty() }
.forEach { it.version { constraint -> constraint.prefer(kotlinPluginVersion) } }
}
} else {
configuration.resolutionStrategy.eachDependency { details ->
val requested = details.requested
if (requested.group == "org.jetbrains.kotlin" && requested.version.isEmpty()) {
details.useVersion(kotlinPluginVersion)
}
}
}
val inspectTask = project.tasks.create("inspectClassesForKotlinIC", InspectClassesForMultiModuleIC::class.java)
inspectTask.sourceSetName = SourceSet.MAIN_SOURCE_SET_NAME
inspectTask.jarTask = jarTask
inspectTask.dependsOn(classesTask)
jarTask.dependsOn(inspectTask)
}
}
internal open class KotlinPlugin(
tasksProvider: KotlinTasksProvider,
kotlinSourceSetProvider: KotlinSourceSetProvider,
kotlinPluginVersion: String
) : AbstractKotlinPlugin(tasksProvider, kotlinSourceSetProvider, kotlinPluginVersion) {
override fun buildSourceSetProcessor(project: Project, javaBasePlugin: JavaBasePlugin, sourceSet: SourceSet, kotlinPluginVersion: String) =
Kotlin2JvmSourceSetProcessor(project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider, kotlinPluginVersion)
tasksProvider: KotlinTasksProvider,
kotlinPluginVersion: String
) : AbstractKotlinPlugin(tasksProvider, kotlinPluginVersion) {
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.JVM
override fun buildSourceSetProcessor(project: Project, compilation: KotlinCompilation, kotlinPluginVersion: String) =
Kotlin2JvmSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
override fun apply(project: Project) {
val target = KotlinWithJavaTarget(project, KotlinPlatformType.JS, "kotlin").apply {
disambiguationClassifier = null // don't add anything to the task names
}
(project.kotlinExtension as KotlinSingleTargetProjectExtension).target = target
project.pluginManager.apply(ScriptingGradleSubplugin::class.java)
super.apply(project)
}
}
internal open class KotlinCommonPlugin(
tasksProvider: KotlinTasksProvider,
kotlinSourceSetProvider: KotlinSourceSetProvider,
tasksProvider: KotlinTasksProvider,
kotlinPluginVersion: String
) : AbstractKotlinPlugin(tasksProvider, kotlinPluginVersion) {
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.COMMON
override fun buildSourceSetProcessor(
project: Project,
compilation: KotlinCompilation,
kotlinPluginVersion: String
) : AbstractKotlinPlugin(tasksProvider, kotlinSourceSetProvider, kotlinPluginVersion) {
override fun buildSourceSetProcessor(project: Project, javaBasePlugin: JavaBasePlugin, sourceSet: SourceSet, kotlinPluginVersion: String) =
KotlinCommonSourceSetProcessor(project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider, kotlinPluginVersion)
): KotlinSourceSetProcessor<*> =
KotlinCommonSourceSetProcessor(project, compilation, tasksProvider, kotlinPluginVersion)
override fun apply(project: Project) {
val target = KotlinWithJavaTarget(project, KotlinPlatformType.JS, "common").apply {
disambiguationClassifier = "common"
}
(project.kotlinExtension as KotlinSingleTargetProjectExtension).target = target
super.apply(project)
}
}
internal open class Kotlin2JsPlugin(
tasksProvider: KotlinTasksProvider,
kotlinSourceSetProvider: KotlinSourceSetProvider,
tasksProvider: KotlinTasksProvider,
kotlinPluginVersion: String
) : AbstractKotlinPlugin(tasksProvider, kotlinPluginVersion) {
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.JS
override fun buildSourceSetProcessor(
project: Project,
compilation: KotlinCompilation,
kotlinPluginVersion: String
) : AbstractKotlinPlugin(tasksProvider, kotlinSourceSetProvider, kotlinPluginVersion) {
override fun buildSourceSetProcessor(project: Project, javaBasePlugin: JavaBasePlugin, sourceSet: SourceSet, kotlinPluginVersion: String) =
Kotlin2JsSourceSetProcessor(project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider, kotlinPluginVersion)
): KotlinSourceSetProcessor<*> =
Kotlin2JsSourceSetProcessor(
project, tasksProvider, compilation, kotlinPluginVersion
)
override fun apply(project: Project) {
val target = KotlinWithJavaTarget(project, KotlinPlatformType.JS, "2Js").apply {
disambiguationClassifier = "2Js"
}
(project.kotlinExtension as KotlinSingleTargetProjectExtension).target = target
super.apply(project)
}
}
internal open class KotlinAndroidPlugin(
val tasksProvider: KotlinTasksProvider,
private val kotlinSourceSetProvider: KotlinSourceSetProvider,
private val kotlinPluginVersion: String
val tasksProvider: KotlinTasksProvider,
private val kotlinPluginVersion: String
) : Plugin<Project> {
override fun apply(project: Project) {
val version = loadAndroidPluginVersion()
if (version != null) {
val minimalVersion = "1.1.0"
if (compareVersionNumbers(version, minimalVersion) < 0) {
throw IllegalStateException("Kotlin: Unsupported version of com.android.tools.build:gradle plugin: version $minimalVersion or higher should be used with kotlin-android plugin")
}
}
val androidTarget = KotlinAndroidTarget(project)
val kotlinTools = KotlinConfigurationTools(
applyToTarget(
project, androidTarget, project.kotlinExtension.sourceSetProvider,
tasksProvider, kotlinPluginVersion
)
}
companion object {
fun applyToTarget(
project: Project,
kotlinTarget: KotlinAndroidTarget,
kotlinSourceSetProvider: KotlinSourceSetProvider,
tasksProvider: KotlinTasksProvider,
kotlinPluginVersion: String
) {
val version = loadAndroidPluginVersion()
if (version != null) {
val minimalVersion = "1.1.0"
if (compareVersionNumbers(version, minimalVersion) < 0) {
throw IllegalStateException("Kotlin: Unsupported version of com.android.tools.build:gradle plugin: version $minimalVersion or higher should be used with kotlin-android plugin")
}
}
val kotlinTools = KotlinConfigurationTools(
kotlinSourceSetProvider,
tasksProvider,
kotlinPluginVersion)
kotlinPluginVersion
)
val legacyVersionThreshold = "2.5.0"
val legacyVersionThreshold = "2.5.0"
val variantProcessor = if (compareVersionNumbers(version, legacyVersionThreshold) < 0) {
LegacyAndroidAndroidProjectHandler(kotlinTools)
}
else {
val android25ProjectHandlerClass = Class.forName("org.jetbrains.kotlin.gradle.plugin.Android25ProjectHandler")
val ctor = android25ProjectHandlerClass.constructors.single {
it.parameterTypes.contentEquals(arrayOf(kotlinTools.javaClass))
val variantProcessor = if (compareVersionNumbers(version, legacyVersionThreshold) < 0) {
LegacyAndroidAndroidProjectHandler(kotlinTools)
} else {
val android25ProjectHandlerClass = Class.forName("org.jetbrains.kotlin.gradle.plugin.Android25ProjectHandler")
val ctor = android25ProjectHandlerClass.constructors.single {
it.parameterTypes.contentEquals(arrayOf(kotlinTools.javaClass))
}
ctor.newInstance(kotlinTools) as AbstractAndroidProjectHandler<*>
}
ctor.newInstance(kotlinTools) as AbstractAndroidProjectHandler<*>
}
variantProcessor.handleProject(project)
variantProcessor.handleProject(project, kotlinTarget)
}
}
}
@@ -490,23 +598,31 @@ abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationT
protected open fun checkVariantIsValid(variant: V) = Unit
protected abstract fun wireKotlinTasks(project: Project,
androidPlugin: BasePlugin,
androidExt: BaseExtension,
variantData: V,
javaTask: AbstractCompile,
kotlinTask: KotlinCompile)
protected open fun setUpDependencyResolution(variant: V, compilation: KotlinCompilation) = Unit
protected abstract fun wireKotlinTasks(
project: Project,
compilation: KotlinJvmAndroidCompilation,
androidPlugin: BasePlugin,
androidExt: BaseExtension,
variantData: V,
javaTask: AbstractCompile,
kotlinTask: KotlinCompile
)
protected abstract fun wrapVariantDataForKapt(variantData: V): KaptVariantData<V>
fun handleProject(project: Project) {
fun handleProject(project: Project, kotlinAndroidTarget: KotlinAndroidTarget) {
val ext = project.extensions.getByName("android") as BaseExtension
val aptConfigurations = hashMapOf<String, Configuration>()
ext.sourceSets.all { sourceSet ->
logger.kotlinDebug("Creating KotlinSourceSet for source set $sourceSet")
val kotlinSourceSet = kotlinConfigurationTools.kotlinSourceSetProvider.create(sourceSet.name)
kotlinSourceSet.kotlin.srcDir(project.file(project.file("src/${sourceSet.name}/kotlin")))
logger.kotlinDebug("Creating KotlinBaseSourceSet for source set $sourceSet")
val kotlinSourceSet = kotlinConfigurationTools.kotlinSourceSetProvider.provideSourceSet(
lowerCamelCaseName(kotlinAndroidTarget.disambiguationClassifier, sourceSet.name)
).apply {
kotlin.srcDir(project.file(project.file("src/${sourceSet.name}/kotlin")))
kotlin.srcDirs(sourceSet.java.srcDirs)
}
sourceSet.addConvention(KOTLIN_DSL_NAME, kotlinSourceSet)
Kapt3KotlinGradleSubplugin.createAptConfigurationIfNeeded(project, sourceSet.name)
}
@@ -516,42 +632,46 @@ abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationT
ext.addExtension(KOTLIN_OPTIONS_DSL_NAME, kotlinOptions)
project.afterEvaluate { project ->
if (project != null) {
val androidPluginIds = listOf("android", "com.android.application", "android-library", "com.android.library",
"com.android.test", "com.android.feature", "com.android.dynamic-feature")
val plugin = androidPluginIds.asSequence()
.mapNotNull { project.plugins.findPlugin(it) as? BasePlugin }
.firstOrNull()
?: throw InvalidPluginException("'kotlin-android' expects one of the Android Gradle " +
"plugins to be applied to the project:\n\t" +
androidPluginIds.joinToString("\n\t") { "* $it" })
val androidPluginIds = listOf("android", "com.android.application", "android-library", "com.android.library",
"com.android.test", "com.android.feature", "com.android.dynamic-feature")
val plugin = androidPluginIds.asSequence()
.mapNotNull { project.plugins.findPlugin(it) as? BasePlugin }
.firstOrNull()
?: throw InvalidPluginException("'kotlin-android' expects one of the Android Gradle " +
"plugins to be applied to the project:\n\t" +
androidPluginIds.joinToString("\n\t") { "* $it" })
val subpluginEnvironment = loadSubplugins(project, kotlinConfigurationTools.kotlinPluginVersion)
val subpluginEnvironment = loadSubplugins(project, kotlinConfigurationTools.kotlinPluginVersion)
checkAndroidAnnotationProcessorDependencyUsage(project)
checkAndroidAnnotationProcessorDependencyUsage(project)
forEachVariant(project) {
processVariant(it, project, ext, plugin, aptConfigurations, kotlinOptions,
kotlinConfigurationTools.kotlinTasksProvider, subpluginEnvironment)
}
forEachVariant(project) {
processVariant(
it, kotlinAndroidTarget, project, ext, plugin, kotlinOptions, kotlinConfigurationTools.kotlinTasksProvider,
subpluginEnvironment
)
}
}
}
private fun processVariant(variantData: V,
project: Project,
androidExt: BaseExtension,
androidPlugin: BasePlugin,
aptConfigurations: Map<String, Configuration>,
rootKotlinOptions: KotlinJvmOptionsImpl,
tasksProvider: KotlinTasksProvider,
subpluginEnvironment: SubpluginEnvironment) {
private fun processVariant(
variantData: V,
target: KotlinAndroidTarget,
project: Project,
androidExt: BaseExtension,
androidPlugin: BasePlugin,
rootKotlinOptions: KotlinJvmOptionsImpl,
tasksProvider: KotlinTasksProvider,
subpluginEnvironment: SubpluginEnvironment
) {
checkVariantIsValid(variantData)
val variantDataName = getVariantName(variantData)
logger.kotlinDebug("Process variant [$variantDataName]")
val compilation = target.compilations.create(variantDataName)
setUpDependencyResolution(variantData, compilation)
val javaTask = getJavaTask(variantData)
if (javaTask == null) {
@@ -559,7 +679,7 @@ abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationT
return
}
val kotlinTaskName = "compile${variantDataName.capitalize()}Kotlin"
val kotlinTaskName = compilation.compileKotlinTaskName
// todo: Investigate possibility of creating and configuring kotlinTask before evaluation
val kotlinTask = tasksProvider.createKotlinJVMTask(project, kotlinTaskName, variantDataName)
kotlinTask.parentKotlinOptionsImpl = rootKotlinOptions
@@ -568,22 +688,35 @@ abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationT
kotlinTask.destinationDir = File(project.buildDir, "tmp/kotlin-classes/$variantDataName")
kotlinTask.description = "Compiles the $variantDataName kotlin."
configureSources(kotlinTask, variantData)
wireKotlinTasks(project, androidPlugin, androidExt, variantData, javaTask, kotlinTask)
configureSources(kotlinTask, variantData, compilation)
// In MPPs, add the common main Kotlin sources to non-test variants, the common test sources to test variants
val commonSourceSetName = if (getTestedVariantData(variantData) == null)
KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME else
KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME
project.kotlinExtension.sourceSets.findByName(commonSourceSetName)?.let {
compilation.source(it)
}
wireKotlinTasks(project, compilation, androidPlugin, androidExt, variantData, javaTask, kotlinTask)
val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
project, kotlinTask, javaTask, wrapVariantDataForKapt(variantData), this, null)
appliedPlugins.flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) }
.forEach { configureSources(it, variantData) }
.forEach { configureSources(it, variantData, null) }
}
private fun configureSources(compileTask: AbstractCompile, variantData: V) {
private fun configureSources(compileTask: AbstractCompile, variantData: V, compilation: KotlinCompilation?) {
val logger = compileTask.project.logger
for (provider in getSourceProviders(variantData)) {
val kotlinSourceSet = provider.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet ?: continue
compileTask.source(kotlinSourceSet.kotlin)
if (compilation != null) {
compilation.source(kotlinSourceSet)
} else {
compileTask.source(kotlinSourceSet.kotlin)
}
}
for (javaSrcDir in getAllJavaSources(variantData)) {
@@ -685,10 +818,10 @@ internal class SubpluginEnvironment(
fun <C: CommonCompilerArguments> addSubpluginOptions(
project: Project,
kotlinTask: AbstractKotlinCompile<C>,
javaTask: AbstractCompile,
javaTask: AbstractCompile?,
variantData: Any?,
androidProjectHandler: AbstractAndroidProjectHandler<out Any?>?,
javaSourceSet: SourceSet?
kotlinCompilation: KotlinCompilation?
): List<KotlinGradleSubplugin<AbstractKotlinCompile<C>>> {
val pluginOptions = kotlinTask.pluginOptions
@@ -705,7 +838,7 @@ internal class SubpluginEnvironment(
project.logger.kotlinDebug { "Adding '$mavenCoordinate' to '$PLUGIN_CLASSPATH_CONFIGURATION_NAME' configuration" }
project.dependencies.add(PLUGIN_CLASSPATH_CONFIGURATION_NAME, mavenCoordinate)
val subpluginOptions = subplugin.apply(project, kotlinTask, javaTask, variantData, androidProjectHandler, javaSourceSet)
val subpluginOptions = subplugin.apply(project, kotlinTask, javaTask, variantData, androidProjectHandler, kotlinCompilation)
val subpluginId = subplugin.getCompilerPluginId()
kotlinTask.registerSubpluginOptionsAsInputs(subpluginId, subpluginOptions)

View File

@@ -21,20 +21,30 @@ import org.gradle.api.Project
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.dsl.createKotlinExtension
import org.jetbrains.kotlin.gradle.internal.KotlinSourceSetProviderImpl
import org.gradle.internal.cleanup.BuildOutputCleanupRegistry
import org.gradle.internal.reflect.Instantiator
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSetFactory
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinSourceSetFactory
import org.jetbrains.kotlin.gradle.tasks.*
import java.io.FileNotFoundException
import java.util.*
import javax.inject.Inject
import kotlin.reflect.KClass
abstract class KotlinBasePluginWrapper(protected val fileResolver: FileResolver): Plugin<Project> {
abstract class KotlinBasePluginWrapper(
protected val fileResolver: FileResolver
): Plugin<Project> {
private val log = Logging.getLogger(this.javaClass)
val kotlinPluginVersion = loadKotlinVersionFromResource(log)
open val projectExtensionClass: KClass<out KotlinProjectExtension> get() = KotlinProjectExtension::class
internal open fun kotlinSourceSetFactory(project: Project): KotlinSourceSetFactory<out KotlinSourceSet> =
DefaultKotlinSourceSetFactory(project, fileResolver)
override fun apply(project: Project) {
project.configurations.maybeCreate(COMPILER_CLASSPATH_CONFIGURATION_NAME).defaultDependencies {
it.add(project.dependencies.create("$KOTLIN_MODULE_GROUP:$KOTLIN_COMPILER_EMBEDDABLE:$kotlinPluginVersion"))
@@ -48,38 +58,82 @@ abstract class KotlinBasePluginWrapper(protected val fileResolver: FileResolver)
System.setProperty(org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY, "true")
val kotlinGradleBuildServices = KotlinGradleBuildServices.getInstance(project.gradle)
project.createKotlinExtension(projectExtensionClass)
project.createKotlinExtension(projectExtensionClass).apply {
fun <T : KotlinSourceSet> kotlinSourceSetContainer(factory: KotlinSourceSetFactory<T>) =
project.container(factory.itemClass, factory)
val plugin = getPlugin(kotlinGradleBuildServices)
project.kotlinExtension.sourceSets = kotlinSourceSetContainer(kotlinSourceSetFactory(project))
}
val plugin = getPlugin(project, kotlinGradleBuildServices)
plugin.apply(project)
}
internal abstract fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices): Plugin<Project>
open val projectExtensionClass: KClass<out KotlinProjectExtension> get() = KotlinProjectExtension::class
internal abstract fun getPlugin(
project: Project,
kotlinGradleBuildServices: KotlinGradleBuildServices
): Plugin<Project>
}
open class KotlinPluginWrapper @Inject constructor(fileResolver: FileResolver): KotlinBasePluginWrapper(fileResolver) {
override fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices) =
KotlinPlugin(KotlinTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion)
open class KotlinPluginWrapper @Inject constructor(
fileResolver: FileResolver
): KotlinBasePluginWrapper(fileResolver) {
override fun getPlugin(project: Project, kotlinGradleBuildServices: KotlinGradleBuildServices): Plugin<Project> =
KotlinPlugin(KotlinTasksProvider(), kotlinPluginVersion)
override val projectExtensionClass: KClass<out KotlinJvmProjectExtension>
get() = KotlinJvmProjectExtension::class
}
open class KotlinCommonPluginWrapper @Inject constructor(fileResolver: FileResolver): KotlinBasePluginWrapper(fileResolver) {
override fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices) =
KotlinCommonPlugin(KotlinCommonTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion)
open class KotlinCommonPluginWrapper @Inject constructor(
fileResolver: FileResolver,
private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry
): KotlinBasePluginWrapper(fileResolver) {
override fun getPlugin(project: Project, kotlinGradleBuildServices: KotlinGradleBuildServices): Plugin<Project> =
KotlinCommonPlugin(KotlinCommonTasksProvider(), kotlinPluginVersion)
override val projectExtensionClass: KClass<out KotlinSingleTargetProjectExtension>
get() = KotlinSingleTargetProjectExtension::class
}
open class KotlinAndroidPluginWrapper @Inject constructor(fileResolver: FileResolver): KotlinBasePluginWrapper(fileResolver) {
override fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices) =
KotlinAndroidPlugin(AndroidTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion)
open class KotlinMultiplatformPluginWrapper @Inject constructor(
fileResolver: FileResolver,
private val instantiator: Instantiator,
private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry
): KotlinBasePluginWrapper(fileResolver) {
override fun getPlugin(project: Project, kotlinGradleBuildServices: KotlinGradleBuildServices): Plugin<Project> =
KotlinMultiplatformPlugin(
buildOutputCleanupRegistry, fileResolver,
instantiator, kotlinPluginVersion
)
override val projectExtensionClass: KClass<out KotlinMultiplatformExtension>
get() = KotlinMultiplatformExtension::class
}
open class Kotlin2JsPluginWrapper @Inject constructor(fileResolver: FileResolver): KotlinBasePluginWrapper(fileResolver) {
override fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices) =
Kotlin2JsPlugin(Kotlin2JsTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion)
open class KotlinAndroidPluginWrapper @Inject constructor(
fileResolver: FileResolver
): KotlinBasePluginWrapper(fileResolver) {
override fun getPlugin(project: Project, kotlinGradleBuildServices: KotlinGradleBuildServices): Plugin<Project> =
KotlinAndroidPlugin(
AndroidTasksProvider(),
kotlinPluginVersion
)
override fun kotlinSourceSetFactory(project: Project): KotlinSourceSetFactory<out KotlinSourceSet> {
return super.kotlinSourceSetFactory(project)
}
}
open class Kotlin2JsPluginWrapper @Inject constructor(
fileResolver: FileResolver,
private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry
): KotlinBasePluginWrapper(fileResolver) {
override fun getPlugin(project: Project, kotlinGradleBuildServices: KotlinGradleBuildServices): Plugin<Project> =
Kotlin2JsPlugin(Kotlin2JsTasksProvider(), kotlinPluginVersion)
override val projectExtensionClass: KClass<out KotlinSingleTargetProjectExtension>
get() = KotlinSingleTargetProjectExtension::class
}
fun Project.getKotlinPluginVersion(): String? {

View File

@@ -1,26 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.gradle.plugin
import groovy.lang.Closure
import org.gradle.api.file.SourceDirectorySet
interface KotlinSourceSet {
val kotlin: SourceDirectorySet
fun kotlin(configureClosure: Closure<Any?>?): KotlinSourceSet
}

View File

@@ -16,6 +16,8 @@
package org.jetbrains.kotlin.gradle.plugin
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
internal interface KotlinSourceSetProvider {
fun create(displayName: String): KotlinSourceSet
fun provideSourceSet(displayName: String): KotlinSourceSet
}

View File

@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.gradle.internal.KaptTask
import org.jetbrains.kotlin.gradle.internal.KaptVariantData
import org.jetbrains.kotlin.gradle.internal.registerGeneratedJavaSource
import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.utils.checkedReflection
import java.io.File
@@ -40,12 +41,14 @@ internal class LegacyAndroidAndroidProjectHandler(kotlinConfigurationTools: Kotl
variantManager.variantDataList.forEach(action)
}
override fun wireKotlinTasks(project: Project,
androidPlugin: BasePlugin,
androidExt: BaseExtension,
variantData: BaseVariantData<out BaseVariantOutputData>,
javaTask: AbstractCompile,
kotlinTask: KotlinCompile
override fun wireKotlinTasks(
project: Project,
compilation: KotlinJvmAndroidCompilation,
androidPlugin: BasePlugin,
androidExt: BaseExtension,
variantData: BaseVariantData<out BaseVariantOutputData>,
javaTask: AbstractCompile,
kotlinTask: KotlinCompile
) {
kotlinTask.dependsOn(*javaTask.dependsOn.toTypedArray())

View File

@@ -0,0 +1,43 @@
/*
* 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.gradle.plugin.base
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.disambiguateName
import kotlin.reflect.KProperty
private val configurationNameFromPlatformExtension = NameFromKotlinTargetDelegate("ConfigurationName")
private val taskNameFromPlatformExtension = NameFromKotlinTargetDelegate("TaskName")
//internal val KotlinTarget.defaultConfigurationName by configurationNameFromPlatformExtension
//internal val KotlinTarget.compileConfigurationName by configurationNameFromPlatformExtension
//internal val KotlinTarget.testCompileConfigurationName by configurationNameFromPlatformExtension
//
//internal val KotlinTarget.compileClasspathConfigurationName by configurationNameFromPlatformExtension
//internal val KotlinTarget.testCompileClasspathConfigurationName by configurationNameFromPlatformExtension
//internal val KotlinTarget.testRuntimeClasspathConfigurationName by configurationNameFromPlatformExtension
//
//internal val KotlinTarget.apiElementsConfigurationName by configurationNameFromPlatformExtension
//internal val KotlinTarget.runtimeElementsConfigurationName by configurationNameFromPlatformExtension
//internal val KotlinTarget.implementationConfigurationName by configurationNameFromPlatformExtension
//internal val KotlinTarget.testImplementationConfigurationName by configurationNameFromPlatformExtension
//internal val KotlinTarget.runtimeConfigurationName by configurationNameFromPlatformExtension
//internal val KotlinTarget.runtimeOnlyConfigurationName by configurationNameFromPlatformExtension
//internal val KotlinTarget.testRuntimeConfigurationName by configurationNameFromPlatformExtension
//internal val KotlinTarget.testRuntimeOnlyConfigurationName by configurationNameFromPlatformExtension
//
//internal val KotlinTarget.classesTaskName by taskNameFromPlatformExtension
//internal val KotlinTarget.jarTaskName by taskNameFromPlatformExtension
//internal val KotlinTarget.processResourcesTaskName by taskNameFromPlatformExtension
private class NameFromKotlinTargetDelegate(val propertySuffix: String) {
operator fun getValue(thisRef: KotlinTarget, property: KProperty<*>): String {
require(property.name.endsWith(propertySuffix))
val nameToDisambiguate = property.name.substringBeforeLast(propertySuffix)
.also { require(it.isNotEmpty()) }
return thisRef.disambiguateName(nameToDisambiguate)
}
}

View File

@@ -0,0 +1,109 @@
/*
* 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.gradle.plugin.base
import org.gradle.api.NamedDomainObjectFactory
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.SourceSetContainer
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.*
interface KotlinCompilationFactory<T: KotlinCompilation> : NamedDomainObjectFactory<T> {
val itemClass: Class<T>
}
private fun Project.createSourceSetOutput(name: String) =
KotlinSourceSetOutput(this, buildDir.resolve("processedResources/$name"))
class KotlinCommonCompilationFactory(
val project: Project,
val target: KotlinOnlyTarget<KotlinCommonCompilation>
) : KotlinCompilationFactory<KotlinCommonCompilation> {
override val itemClass: Class<KotlinCommonCompilation>
get() = KotlinCommonCompilation::class.java
override fun create(name: String): KotlinCommonCompilation =
KotlinCommonCompilation(target, name, project.createSourceSetOutput(name))
}
class KotlinJvmCompilationFactory(
val project: Project,
val target: KotlinOnlyTarget<KotlinJvmCompilation>
) : KotlinCompilationFactory<KotlinJvmCompilation> {
override val itemClass: Class<KotlinJvmCompilation>
get() = KotlinJvmCompilation::class.java
override fun create(name: String): KotlinJvmCompilation = KotlinJvmCompilation(
target,
name,
KotlinSourceSetOutput(project, project.buildDir.resolve("resources/$name"))
)
}
class KotlinWithJavaCompilationFactory(
val project: Project,
val target: KotlinWithJavaTarget
) : KotlinCompilationFactory<KotlinWithJavaCompilation> {
private val javaSourceSets: SourceSetContainer
get() = project.convention.getPlugin(JavaPluginConvention::class.java).sourceSets
override val itemClass: Class<KotlinWithJavaCompilation>
get() = KotlinWithJavaCompilation::class.java
override fun create(name: String): KotlinWithJavaCompilation {
val javaSourceSet = javaSourceSets.maybeCreate(name)
val result = KotlinWithJavaCompilation(target, name, javaSourceSet)
return result
}
}
class KotlinJvmAndroidCompilationFactory(
val project: Project,
val target: KotlinAndroidTarget
) : KotlinCompilationFactory<KotlinJvmAndroidCompilation> {
override val itemClass: Class<KotlinJvmAndroidCompilation>
get() = KotlinJvmAndroidCompilation::class.java
override fun create(name: String): KotlinJvmAndroidCompilation {
val output = project.createSourceSetOutput(name)
val result = KotlinJvmAndroidCompilation(target, name, output)
return result
}
}
class KotlinJsCompilationFactory(
val project: Project,
val target: KotlinOnlyTarget<KotlinJsCompilation>
) : KotlinCompilationFactory<KotlinJsCompilation> {
override val itemClass: Class<KotlinJsCompilation>
get() = KotlinJsCompilation::class.java
override fun create(name: String): KotlinJsCompilation =
KotlinJsCompilation(target, name, project.createSourceSetOutput(name))
}
class KotlinNativeCompilationFactory(
val project: Project,
val target: KotlinNativeTarget
) : KotlinCompilationFactory<KotlinNativeCompilation> {
override val itemClass: Class<KotlinNativeCompilation>
get() = KotlinNativeCompilation::class.java
override fun create(name: String): KotlinNativeCompilation =
KotlinNativeCompilation(target, name, project.createSourceSetOutput(name)).apply {
if (name == KotlinCompilation.TEST_COMPILATION_NAME) {
outputKinds = mutableListOf(NativeOutputKind.EXECUTABLE)
buildTypes = mutableListOf(NativeBuildType.DEBUG)
isTestCompilation = true
} else {
buildTypes = mutableListOf(NativeBuildType.DEBUG, NativeBuildType.RELEASE)
}
}
}

View File

@@ -0,0 +1,360 @@
/*
* 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.gradle.plugin.base
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.type.ArtifactTypeDefinition
import org.gradle.api.attributes.Usage
import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.artifacts.ArtifactAttributes
import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact
import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet
import org.gradle.api.internal.plugins.DslObject
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.tasks.bundling.Jar
import org.gradle.internal.cleanup.BuildOutputCleanupRegistry
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.gradle.language.jvm.tasks.ProcessResources
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.disambiguateName
import org.jetbrains.kotlin.gradle.plugin.mpp.fullName
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import java.io.File
import java.util.concurrent.Callable
open class KotlinOnlyTargetConfigurator(
private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry
) {
fun <KotlinCompilationType: KotlinCompilation> configureTarget(
project: Project,
target: KotlinOnlyTarget<KotlinCompilationType>
) {
configureCompilationDefaults(project, target)
configureCompilations(project, target)
defineConfigurationsForTarget(project, target)
configureArchivesAndComponent(project, target)
configureBuild(project, target)
setCompatibilityOfAbstractCompileTasks(project)
}
private fun <KotlinCompilationType: KotlinCompilation> configureCompilations(
project: Project,
platformTarget: KotlinOnlyTarget<KotlinCompilationType>
) {
val main = platformTarget.compilations.create(KotlinCompilation.MAIN_COMPILATION_NAME)
platformTarget.compilations.create(KotlinCompilation.TEST_COMPILATION_NAME).apply {
compileDependencyFiles = project.files(main.output, project.configurations.maybeCreate(compileDependencyConfigurationName))
if (this is KotlinCompilationToRunnableFiles) {
runtimeDependencyFiles = project.files(output, main.output, project.configurations.maybeCreate(runtimeDependencyConfigurationName))
}
}
platformTarget.compilations.all {
buildOutputCleanupRegistry.registerOutputs(it.output)
}
}
private fun <KotlinCompilationType: KotlinCompilation> configureCompilationDefaults(project: Project, target: KotlinOnlyTarget<KotlinCompilationType>) {
target.compilations.all { compilation ->
defineConfigurationsForCompilation(compilation, target, project.configurations)
project.kotlinExtension.sourceSets.maybeCreate(compilation.fullName).also { sourceSet ->
compilation.source(sourceSet) // also adds dependencies, requires the configurations for target and source set to exist at this point
}
if (compilation is KotlinCompilationWithResources) {
configureResourceProcessing(project, compilation, project.files())
}
createLifecycleTask(compilation, project)
}
}
private fun configureArchivesAndComponent(project: Project, target: KotlinOnlyTarget<*>) {
val mainCompilation = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
val jar = project.tasks.create(target.artifactsTaskName, Jar::class.java)
jar.description = "Assembles a jar archive containing the main classes."
jar.group = BasePlugin.BUILD_GROUP
jar.from(mainCompilation.output)
val jarArtifact = ArchivePublishArtifact(jar)
val apiElementsConfiguration = project.configurations.getByName(target.apiElementsConfigurationName)
target.disambiguationClassifier?.let { jar.classifier = it }
project.extensions.getByType(DefaultArtifactPublicationSet::class.java).addCandidate(jarArtifact)
addJar(apiElementsConfiguration, jarArtifact)
if (mainCompilation is KotlinCompilationToRunnableFiles) {
val runtimeConfiguration = project.configurations.getByName(mainCompilation.deprecatedRuntimeConfigurationName)
val runtimeElementsConfiguration = project.configurations.getByName(target.runtimeElementsConfigurationName)
addJar(runtimeConfiguration, jarArtifact)
addJar(runtimeElementsConfiguration, jarArtifact) // TODO Check Gradle's special split into variants for classes & resources
}
// note: there's no variant configuration for now
// FIXME ensure this dependency through configurations instead:
project.tasks.getByName("assemble").dependsOn(jar)
}
private fun addJar(configuration: Configuration, jarArtifact: ArchivePublishArtifact) {
val publications = configuration.outgoing
// Configure an implicit variant
publications.artifacts.add(jarArtifact)
publications.attributes.attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.JAR_TYPE)
}
private fun configureResourceProcessing(
project: Project,
compilation: KotlinCompilationWithResources,
resourceSet: FileCollection
) {
compilation.output.setResourcesDir(Callable {
val classesDirName = "resources/" + compilation.compilationName
File(project.buildDir, classesDirName)
})
val resourcesTask = project.tasks.maybeCreate(compilation.processResourcesTaskName, ProcessResources::class.java)
resourcesTask.description = "Processes $resourceSet."
DslObject(resourcesTask).conventionMapping.map("destinationDir") { compilation.output.resourcesDir }
resourcesTask.from(resourceSet)
}
private fun createLifecycleTask(compilation: KotlinCompilation, project: Project) {
(compilation.output.classesDirs as ConfigurableFileCollection).from(project.files().builtBy(compilation.compileAllTaskName))
project.tasks.create(compilation.compileAllTaskName).apply {
group = LifecycleBasePlugin.BUILD_GROUP
description = "Assembles " + compilation.output + "."
dependsOn(
compilation.output.dirs,
compilation.compileKotlinTaskName
)
if (compilation is KotlinCompilationWithResources) {
dependsOn(compilation.processResourcesTaskName)
}
}
}
private val KotlinCompilation.deprecatedCompileConfigurationName: String
get() = disambiguateName("compile")
private val KotlinCompilationToRunnableFiles.deprecatedRuntimeConfigurationName: String
get() = disambiguateName("runtime")
internal fun defineConfigurationsForCompilation(
compilation: KotlinCompilation,
target: KotlinTarget,
configurations: ConfigurationContainer
) {
val compileConfiguration = configurations.maybeCreate(compilation.deprecatedCompileConfigurationName).apply {
usesPlatformOf(target)
isVisible = false
description = "Dependencies for $compilation (deprecated, use '${compilation.implementationConfigurationName} ' instead)."
}
val apiConfiguration = configurations.maybeCreate(compilation.apiConfigurationName).apply {
extendsFrom(compileConfiguration)
usesPlatformOf(target)
isVisible = false
isCanBeConsumed = false
isCanBeResolved = false
description = "API dependencies for $compilation."
}
val implementationConfiguration = configurations.maybeCreate(compilation.implementationConfigurationName).apply {
extendsFrom(compileConfiguration, apiConfiguration)
usesPlatformOf(target)
isVisible = false
isCanBeConsumed = false
isCanBeResolved = false
description = "Implementation only dependencies for $compilation."
}
val compileOnlyConfiguration = configurations.maybeCreate(compilation.compileOnlyConfigurationName).apply {
usesPlatformOf(target)
isVisible = false
description = "Compile only dependencies for $compilation."
}
val compileClasspathConfiguration = configurations.maybeCreate(compilation.compileDependencyConfigurationName).apply {
extendsFrom(compileOnlyConfiguration, implementationConfiguration)
usesPlatformOf(target)
isVisible = false
isCanBeConsumed = false
attributes.attribute(USAGE_ATTRIBUTE, compilation.target.project.usageByName(Usage.JAVA_API))
description = "Compile classpath for $compilation."
}
compilation.compileDependencyFiles = compileClasspathConfiguration
if (compilation is KotlinCompilationToRunnableFiles) {
val runtimeConfiguration = configurations.maybeCreate(compilation.deprecatedRuntimeConfigurationName).apply {
extendsFrom(compileConfiguration)
usesPlatformOf(target)
isVisible = false
description =
"Runtime dependencies for $compilation (deprecated, use '${compilation.runtimeOnlyConfigurationName} ' instead)."
}
val runtimeOnlyConfiguration = configurations.maybeCreate(compilation.runtimeOnlyConfigurationName).apply {
usesPlatformOf(target)
isVisible = false
isCanBeConsumed = false
isCanBeResolved = false
description = "Runtime only dependencies for $compilation."
}
val runtimeClasspathConfiguration = configurations.maybeCreate(compilation.runtimeDependencyConfigurationName).apply {
extendsFrom(runtimeOnlyConfiguration, runtimeConfiguration, implementationConfiguration)
usesPlatformOf(target)
isVisible = false
isCanBeConsumed = false
isCanBeResolved = true
attributes.attribute(USAGE_ATTRIBUTE, compilation.target.project.usageByName(Usage.JAVA_RUNTIME))
description = "Runtime classpath of $compilation."
}
compilation.runtimeDependencyFiles = compilation.output.plus(runtimeClasspathConfiguration)
}
}
private fun defineConfigurationsForTarget(project: Project, target: KotlinOnlyTarget<*>) {
val configurations = project.configurations
val defaultConfiguration = configurations.maybeCreate(target.defaultConfigurationName)
val mainCompilation = target.compilations.maybeCreate(KotlinCompilation.MAIN_COMPILATION_NAME)
val testCompilation = target.compilations.maybeCreate(KotlinCompilation.TEST_COMPILATION_NAME)
val compileConfiguration = configurations.maybeCreate(mainCompilation.deprecatedCompileConfigurationName)
val implementationConfiguration = configurations.maybeCreate(mainCompilation.implementationConfigurationName)
val runtimeOnlyConfiguration = configurations.maybeCreate(mainCompilation.runtimeOnlyConfigurationName)
val compileTestsConfiguration = configurations.maybeCreate(testCompilation.deprecatedCompileConfigurationName)
val testImplementationConfiguration = configurations.maybeCreate(testCompilation.implementationConfigurationName)
val testRuntimeOnlyConfiguration = configurations.maybeCreate(testCompilation.runtimeOnlyConfigurationName)
compileTestsConfiguration.extendsFrom(compileConfiguration).usesPlatformOf(target)
testImplementationConfiguration.extendsFrom(implementationConfiguration).usesPlatformOf(target)
testRuntimeOnlyConfiguration.extendsFrom(runtimeOnlyConfiguration).usesPlatformOf(target)
configurations.maybeCreate(target.apiElementsConfigurationName).apply {
description = "API elements for main."
isVisible = false
isCanBeResolved = false
isCanBeConsumed = true
attributes.attribute<Usage>(USAGE_ATTRIBUTE, project.usageByName(Usage.JAVA_API))
extendsFrom(configurations.maybeCreate(mainCompilation.apiConfigurationName))
if (mainCompilation is KotlinCompilationToRunnableFiles) {
val runtimeConfiguration = configurations.maybeCreate(mainCompilation.deprecatedRuntimeConfigurationName)
extendsFrom(runtimeConfiguration)
}
usesPlatformOf(target)
}
val runtimeElementsConfiguration = configurations.maybeCreate(target.runtimeElementsConfigurationName).apply {
description = "Elements of runtime for main."
isVisible = false
isCanBeConsumed = true
isCanBeResolved = false
attributes.attribute<Usage>(USAGE_ATTRIBUTE, project.usageByName(Usage.JAVA_RUNTIME_JARS))
if (mainCompilation is KotlinCompilationToRunnableFiles) {
val runtimeConfiguration = configurations.maybeCreate(mainCompilation.deprecatedRuntimeConfigurationName)
extendsFrom(implementationConfiguration, runtimeOnlyConfiguration, runtimeConfiguration)
}
usesPlatformOf(target)
}
if (mainCompilation is KotlinCompilationToRunnableFiles && testCompilation is KotlinCompilationToRunnableFiles) {
val runtimeConfiguration = configurations.maybeCreate(mainCompilation.deprecatedRuntimeConfigurationName)
val testRuntimeConfiguration = configurations.maybeCreate(testCompilation.deprecatedRuntimeConfigurationName)
testRuntimeConfiguration.extendsFrom(runtimeConfiguration).usesPlatformOf(target)
}
defaultConfiguration.extendsFrom(runtimeElementsConfiguration).usesPlatformOf(target)
}
private fun configureBuild(project: Project, target: KotlinOnlyTarget<*>) {
val testCompilation = target.compilations.getByName(KotlinCompilation.TEST_COMPILATION_NAME)
project.tasks.maybeCreate(buildNeededTaskName, DefaultTask::class.java).apply {
description = "Assembles and tests this project and all projects it depends on."
group = "build"
dependsOn("build")
if (testCompilation is KotlinCompilationToRunnableFiles) {
addDependsOnTaskInOtherProjects(this@apply, true, name, testCompilation.deprecatedRuntimeConfigurationName)
}
}
project.tasks.maybeCreate(buildDependentTaskName, DefaultTask::class.java).apply {
setDescription("Assembles and tests this project and all projects that depend on it.")
setGroup("build")
dependsOn("build")
doFirst {
if (!project.gradle.includedBuilds.isEmpty()) {
project.logger.warn("[composite-build] Warning: `" + path + "` task does not build included builds.")
}
}
if (testCompilation is KotlinCompilationToRunnableFiles) {
addDependsOnTaskInOtherProjects(this@apply, false, name, testCompilation.deprecatedRuntimeConfigurationName)
}
}
}
private fun addDependsOnTaskInOtherProjects(
task: Task, useDependedOn: Boolean, otherProjectTaskName: String,
configurationName: String
) {
val project = task.project
val configuration = project.configurations.getByName(configurationName)
task.dependsOn(configuration.getTaskDependencyFromProjectDependency(useDependedOn, otherProjectTaskName))
}
private fun setCompatibilityOfAbstractCompileTasks(project: Project) = with (project) {
tasks.withType(AbstractKotlinCompile::class.java).all {
// Workaround: these are input properties and should not hold null values:
it.targetCompatibility = ""
it.sourceCompatibility = ""
}
}
internal companion object {
const val buildNeededTaskName = "buildAllNeeded"
const val buildDependentTaskName = "buildAllDependents"
}
}
internal fun Project.usageByName(usageName: String): Usage =
if (isGradleVersionAtLeast(4, 0)) {
// `project.objects` is an API introduced in Gradle 4.0
project.objects.named(Usage::class.java, usageName)
} else {
val usagesClass = Class.forName("org.gradle.api.internal.attributes")
val usagesMethod = usagesClass.getMethod("usage", String::class.java)
usagesMethod(null, usageName) as Usage
}
fun Configuration.usesPlatformOf(target: KotlinTarget): Configuration {
attributes.attribute(KotlinPlatformType.attribute, target.platformType)
return this
}

View File

@@ -0,0 +1,34 @@
/*
* 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.gradle.plugin.mpp
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer
import java.util.*
// TODO better implementation: attribute invariants (no attrs with same name and different types), thread safety?
class HierarchyAttributeContainer(val parent: AttributeContainer?) : AttributeContainer {
private val attributesMap = Collections.synchronizedMap(mutableMapOf<Attribute<*>, Any>())
override fun contains(key: Attribute<*>): Boolean =
attributesMap.contains(key) || parent?.contains(key) ?: false
@Suppress("UNCHECKED_CAST")
override fun <T : Any?> getAttribute(key: Attribute<T>?): T? =
attributesMap.get(key as Attribute<*>) as T? ?: parent?.getAttribute(key)
override fun isEmpty(): Boolean = attributesMap.isEmpty() && parent?.isEmpty ?: false
override fun keySet(): Set<Attribute<*>> = attributesMap.keys + parent?.keySet().orEmpty()
override fun <T : Any?> attribute(key: Attribute<T>?, value: T): AttributeContainer {
val checkedValue = requireNotNull(value as Any?) { "null values for attributes are not supported" }
attributesMap[key as Attribute<*>] = checkedValue
return this
}
override fun getAttributes(): AttributeContainer = this
}

View File

@@ -0,0 +1,248 @@
/*
* 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.gradle.plugin.mpp
import groovy.lang.Closure
import org.gradle.api.Project
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.SourceSetOutput
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
internal fun KotlinCompilation.composeName(prefix: String? = null, suffix: String? = null): String {
val compilationNamePart = compilationName.takeIf { it != "main" }
val targetNamePart = target.disambiguationClassifier
return lowerCamelCaseName(prefix, targetNamePart, compilationNamePart, suffix)
}
internal val KotlinCompilation.fullName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName)
internal class DefaultKotlinDependencyHandler(
val parent: HasKotlinDependencies,
val project: Project
) : KotlinDependencyHandler {
override fun api(dependencyNotation: Any) = addDependency(parent.apiConfigurationName, dependencyNotation)
override fun implementation(dependencyNotation: Any) = addDependency(parent.implementationConfigurationName, dependencyNotation)
override fun compileOnly(dependencyNotation: Any) = addDependency(parent.compileOnlyConfigurationName, dependencyNotation)
override fun runtimeOnly(dependencyNotation: Any) = addDependency(parent.runtimeOnlyConfigurationName, dependencyNotation)
private fun addDependency(configurationName: String, dependencyNotation: Any) {
project.dependencies.add(configurationName, dependencyNotation)
}
}
abstract class AbstractKotlinCompilation<T : KotlinTarget>(
final override val target: T,
override val compilationName: String
) : KotlinCompilation, HasKotlinDependencies {
private val attributeContainer = HierarchyAttributeContainer(target.attributes)
override fun getAttributes(): AttributeContainer = attributeContainer
override val kotlinSourceSets: MutableList<KotlinSourceSet> = mutableListOf()
override fun source(sourceSet: KotlinSourceSet) {
kotlinSourceSets += sourceSet
with(target.project) {
whenEvaluated {
(target.project.tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).source(sourceSet.kotlin)
}
addExtendsFromRelation(apiConfigurationName, sourceSet.apiConfigurationName)
addExtendsFromRelation(implementationConfigurationName, sourceSet.implementationConfigurationName)
addExtendsFromRelation(compileOnlyConfigurationName, sourceSet.compileOnlyConfigurationName)
if (this is KotlinCompilationToRunnableFiles) {
addExtendsFromRelation(runtimeOnlyConfigurationName, sourceSet.runtimeOnlyConfigurationName)
}
}
}
protected fun Project.addExtendsFromRelation(extendingConfigurationName: String, extendsFromConfigurationName: String) {
if (extendingConfigurationName != extendsFromConfigurationName) {
if (project.configurations.findByName(extendingConfigurationName) != null)
project.dependencies.add(extendingConfigurationName, project.configurations.getByName(extendsFromConfigurationName))
}
}
override val compileDependencyConfigurationName: String
get() = lowerCamelCaseName(compilationName.takeIf { it != "main" }.orEmpty(), target.disambiguationClassifier, "compileClasspath")
override val compileKotlinTaskName: String
get() = lowerCamelCaseName("compile", compilationName.takeIf { it != "main" }.orEmpty(), "Kotlin", target.disambiguationClassifier)
override val compileAllTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName.takeIf { it != "main" }.orEmpty(), "classes")
override lateinit var compileDependencyFiles: FileCollection
override val apiConfigurationName: String
get() = disambiguateName("api")
override val implementationConfigurationName: String
get() = disambiguateName("implementation")
override val compileOnlyConfigurationName: String
get() = disambiguateName("compileOnly")
override val runtimeOnlyConfigurationName: String
get() = disambiguateName("runtimeOnly")
override fun dependencies(configure: KotlinDependencyHandler.() -> Unit): Unit =
DefaultKotlinDependencyHandler(this, target.project).run(configure)
override fun dependencies(configureClosure: Closure<Any?>) =
dependencies f@{ this@f.executeClosure(configureClosure) }
override fun toString(): String = "compilation '$compilationName' ($target)"
}
abstract class AbstractKotlinCompilationToRunnableFiles<T : KotlinTarget>(target: T, name: String)
: AbstractKotlinCompilation<T>(target, name), KotlinCompilationToRunnableFiles {
override val runtimeDependencyConfigurationName: String
get() = lowerCamelCaseName(compilationName, target.disambiguationClassifier, "runtimeClasspath")
override lateinit var runtimeDependencyFiles: FileCollection
}
internal fun KotlinCompilation.disambiguateName(simpleName: String): String {
return lowerCamelCaseName(
target.disambiguationClassifier,
compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
simpleName
)
}
open class KotlinJvmCompilation(
target: KotlinTarget,
name: String,
override val output: SourceSetOutput
) : AbstractKotlinCompilationToRunnableFiles<KotlinTarget>(target, name), KotlinCompilationWithResources {
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
}
class KotlinWithJavaCompilation(
target: KotlinWithJavaTarget,
name: String,
val javaSourceSet: SourceSet
) : AbstractKotlinCompilationToRunnableFiles<KotlinWithJavaTarget>(target, name), KotlinCompilationWithResources {
override val output: SourceSetOutput
get() = javaSourceSet.output
override val processResourcesTaskName: String
get() = javaSourceSet.processResourcesTaskName
override var runtimeDependencyFiles: FileCollection
get() = javaSourceSet.runtimeClasspath
set(value) { javaSourceSet.runtimeClasspath = value }
override val runtimeDependencyConfigurationName: String
get() = javaSourceSet.runtimeClasspathConfigurationName
override val compileDependencyConfigurationName: String
get() = javaSourceSet.compileClasspathConfigurationName
override val runtimeOnlyConfigurationName: String
get() = javaSourceSet.runtimeOnlyConfigurationName
override val implementationConfigurationName: String
get() = javaSourceSet.implementationConfigurationName
override val apiConfigurationName: String
get() = javaSourceSet.apiConfigurationName
override val compileOnlyConfigurationName: String
get() = javaSourceSet.compileOnlyConfigurationName
override val compileAllTaskName: String
get() = javaSourceSet.classesTaskName
override var compileDependencyFiles: FileCollection
get() = javaSourceSet.compileClasspath
set(value) { javaSourceSet.compileClasspath = value }
fun source(javaSourceSet: SourceSet) {
with(target.project) {
afterEvaluate {
(tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).source(javaSourceSet.java)
}
}
}
}
class KotlinJvmAndroidCompilation(
target: KotlinAndroidTarget,
name: String,
override val output: SourceSetOutput
): AbstractKotlinCompilation<KotlinAndroidTarget>(target, name)
class KotlinJsCompilation(
target: KotlinTarget,
name: String,
override val output: SourceSetOutput
) : AbstractKotlinCompilation<KotlinTarget>(target, name)
class KotlinCommonCompilation(
target: KotlinTarget,
name: String,
override val output: SourceSetOutput
) : AbstractKotlinCompilation<KotlinTarget>(target, name)
class KotlinNativeCompilation(
target: KotlinNativeTarget,
name: String,
override val output: SourceSetOutput
) : AbstractKotlinCompilationToRunnableFiles<KotlinNativeTarget>(target, name) {
val linkAllTaskName: String
get() = lowerCamelCaseName("link", compilationName.takeIf { it != "main" }.orEmpty(), target.disambiguationClassifier)
var isTestCompilation = false
// Native-specific DSL.
val extraOpts = mutableListOf<String>()
fun extraOpts(vararg values: Any) = extraOpts(values.toList())
fun extraOpts(values: List<Any>) {
extraOpts.addAll(values.map { it.toString() })
}
var buildTypes = mutableListOf<NativeBuildType>()
var outputKinds = mutableListOf<NativeOutputKind>()
// TODO: Integrate with Big Kotlin tasks and runners and remove this method.
override fun source(sourceSet: KotlinSourceSet) {
kotlinSourceSets += sourceSet
with(target.project) {
addExtendsFromRelation(apiConfigurationName, sourceSet.apiConfigurationName)
addExtendsFromRelation(implementationConfigurationName, sourceSet.implementationConfigurationName)
addExtendsFromRelation(compileOnlyConfigurationName, sourceSet.compileOnlyConfigurationName)
if (this is KotlinCompilationToRunnableFiles) {
addExtendsFromRelation(runtimeOnlyConfigurationName, sourceSet.runtimeOnlyConfigurationName)
}
}
}
// TODO: Can we do it better?
companion object {
val DEBUG = NativeBuildType.DEBUG
val RELEASE = NativeBuildType.RELEASE
val EXECUTABLE = NativeOutputKind.EXECUTABLE
val FRAMEWORK = NativeOutputKind.FRAMEWORK
}
}

View File

@@ -0,0 +1,158 @@
/*
* 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.gradle.plugin.mpp
import groovy.lang.Closure
import org.gradle.api.NamedDomainObjectCollection
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.internal.plugins.DslObject
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.internal.publication.MavenPublicationInternal
import org.gradle.internal.cleanup.BuildOutputCleanupRegistry
import org.gradle.internal.reflect.Instantiator
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
internal val Project.multiplatformExtension get(): KotlinMultiplatformExtension? =
project.extensions.getByName("kotlin") as KotlinMultiplatformExtension
internal class KotlinMultiplatformPlugin(
private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry,
private val fileResolver: FileResolver,
private val instantiator: Instantiator,
private val kotlinPluginVersion: String
) : Plugin<Project> {
private class TargetFromPresetExtension(val targetsContainer: NamedDomainObjectCollection<KotlinTarget>) {
fun <T : KotlinTarget> fromPreset(preset: KotlinTargetPreset<T>, name: String, configureClosure: Closure<*>): T =
fromPreset(preset, name, { executeClosure(configureClosure) })
@JvmOverloads
fun <T : KotlinTarget> fromPreset(preset: KotlinTargetPreset<T>, name: String, configureAction: T.() -> Unit = { }): T {
val target = preset.createTarget(name)
targetsContainer.add(target)
target.run(configureAction)
return target
}
}
override fun apply(project: Project) {
project.plugins.apply(BasePlugin::class.java)
val targetsContainer = project.container(KotlinTarget::class.java)
val targetsFromPreset = TargetFromPresetExtension(targetsContainer)
project.extensions.getByType(KotlinMultiplatformExtension::class.java).apply {
DslObject(targetsContainer).addConvention("fromPreset", targetsFromPreset)
targets = targetsContainer
addExtension("targets", targets)
presets = project.container(KotlinTargetPreset::class.java)
addExtension("presets", presets)
}
setupDefaultPresets(project)
configureDefaultVersionsResolutionStrategy(project, kotlinPluginVersion)
configureSourceSets(project)
setUpConfigurationAttributes(project)
configurePublishingWithMavenPublish(project)
}
fun setupDefaultPresets(project: Project) {
with((project.kotlinExtension as KotlinMultiplatformExtension).presets) {
add(KotlinUniversalTargetPreset(project, instantiator, fileResolver, buildOutputCleanupRegistry, kotlinPluginVersion))
add(KotlinJvmTargetPreset(project, instantiator, fileResolver, buildOutputCleanupRegistry, kotlinPluginVersion))
add(KotlinJsTargetPreset(project, instantiator, fileResolver, buildOutputCleanupRegistry, kotlinPluginVersion))
add(KotlinAndroidTargetPreset(project, kotlinPluginVersion, buildOutputCleanupRegistry))
add(KotlinJvmWithJavaTargetPreset(project, kotlinPluginVersion))
HostManager().targets.forEach { name, target ->
add(KotlinNativeTargetPreset(name, project, target.toKotlinPlatformType(), buildOutputCleanupRegistry))
}
}
}
private fun configurePublishingWithMavenPublish(project: Project) = project.pluginManager.withPlugin("maven-publish") {
val targets = project.multiplatformExtension!!.targets
val kotlinSoftwareComponent = KotlinSoftwareComponent(project, "kotlin", targets)
project.afterEvaluate { project -> // Use afterEvaluate because publications configuration is no more lazy since Gradle 4.9
project.extensions.configure(PublishingExtension::class.java) { publishing ->
publishing.publications.create("kotlinCompositeLibrary", MavenPublication::class.java) { publication ->
publication.from(kotlinSoftwareComponent)
(publication as MavenPublicationInternal).publishWithOriginalFileName()
publication.artifactId = project.name
publication.groupId = project.group.toString()
}
}
}
}
private fun configureSourceSets(project: Project) = with (project.kotlinExtension as KotlinMultiplatformExtension) {
sourceSets.all { defineSourceSetConfigurations(project, it) }
val production = sourceSets.create(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
val test = sourceSets.create(KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME)
targets.all {
it.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME)?.source(production)
it.compilations.findByName(KotlinCompilation.TEST_COMPILATION_NAME)?.source(test)
}
}
private fun setUpConfigurationAttributes(project: Project) {
val targets = (project.kotlinExtension as KotlinMultiplatformExtension).targets
project.afterEvaluate {
targets.all { target ->
val compilationAttributes = target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME)?.attributes
?: return@all
fun <T> copyAttribute(key: Attribute<T>, from: AttributeContainer, to: AttributeContainer) {
to.attribute(key, from.getAttribute(key)!!)
}
listOf(
target.apiElementsConfigurationName,
target.runtimeElementsConfigurationName,
target.defaultConfigurationName
)
.mapNotNull { configurationName -> target.project.configurations.findByName(configurationName) }
.forEach { configuration ->
compilationAttributes.keySet().forEach { key ->
copyAttribute(key, compilationAttributes, configuration.attributes)
}
}
target.compilations.all { compilation ->
compilation.relatedConfigurationNames
.mapNotNull { configurationName -> target.project.configurations.findByName(configurationName) }
.forEach { configuration ->
compilationAttributes.keySet().forEach { key ->
copyAttribute(key, compilationAttributes, configuration.attributes)
}
}
}
}
}
}
private fun defineSourceSetConfigurations(project: Project, sourceSet: KotlinSourceSet) = with (project.configurations) {
sourceSet.relatedConfigurationNames.forEach { configurationName ->
maybeCreate(configurationName)
}
}
}

View File

@@ -0,0 +1,380 @@
/*
* 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.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.plugins.JavaPlugin
import org.gradle.internal.cleanup.BuildOutputCleanupRegistry
import org.gradle.internal.reflect.Instantiator
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.gradle.nativeplatform.test.tasks.RunTestExecutable
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.dsl.sourceSetProvider
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.base.*
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.gradle.utils.setClassesDirCompatible
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.HostManager
import java.io.File
import java.util.*
abstract class KotlinOnlyTargetPreset<T : KotlinCompilation>(
protected val project: Project,
private val instantiator: Instantiator,
private val fileResolver: FileResolver,
private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry,
protected val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinOnlyTarget<T>> {
override fun createTarget(name: String): KotlinOnlyTarget<T> {
val result = KotlinOnlyTarget<T>(project, platformType).apply {
targetName = name
disambiguationClassifier = name
val compilationFactory = createCompilationFactory(this)
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
KotlinOnlyTargetConfigurator(buildOutputCleanupRegistry).configureTarget(project, result)
result.compilations.all { compilation ->
buildCompilationProcessor(compilation).run()
}
return result
}
protected abstract fun createCompilationFactory(forTarget: KotlinOnlyTarget<T>): KotlinCompilationFactory<T>
protected abstract val platformType: KotlinPlatformType
internal abstract fun buildCompilationProcessor(compilation: T): KotlinSourceSetProcessor<*>
}
class KotlinUniversalTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
buildOutputCleanupRegistry: BuildOutputCleanupRegistry,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinCommonCompilation>(
project,
instantiator,
fileResolver,
buildOutputCleanupRegistry,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinCommonCompilation>)
: KotlinCompilationFactory<KotlinCommonCompilation> =
KotlinCommonCompilationFactory(project, forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.COMMON
override fun buildCompilationProcessor(compilation: KotlinCommonCompilation): KotlinSourceSetProcessor<*> =
KotlinCommonSourceSetProcessor(
project,
compilation,
KotlinCommonTasksProvider(),
kotlinPluginVersion
)
companion object {
const val PRESET_NAME = "universal"
}
}
class KotlinJvmTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
buildOutputCleanupRegistry: BuildOutputCleanupRegistry,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJvmCompilation>(
project,
instantiator,
fileResolver,
buildOutputCleanupRegistry,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJvmCompilation>): KotlinCompilationFactory<KotlinJvmCompilation> =
KotlinJvmCompilationFactory(project, forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.JVM
override fun buildCompilationProcessor(compilation: KotlinJvmCompilation): KotlinSourceSetProcessor<*> =
Kotlin2JvmSourceSetProcessor(project, KotlinTasksProvider(), compilation, kotlinPluginVersion)
companion object {
const val PRESET_NAME = "jvm"
}
}
class KotlinJsTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
buildOutputCleanupRegistry: BuildOutputCleanupRegistry,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJsCompilation>(
project,
instantiator,
fileResolver,
buildOutputCleanupRegistry,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(forTarget: KotlinOnlyTarget<KotlinJsCompilation>) =
KotlinJsCompilationFactory(project, forTarget)
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.JS
override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> =
Kotlin2JsSourceSetProcessor(project, Kotlin2JsTasksProvider(), compilation, kotlinPluginVersion)
companion object {
const val PRESET_NAME = "js"
}
}
class KotlinAndroidTargetPreset(
private val project: Project,
private val kotlinPluginVersion: String,
private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry
) : KotlinTargetPreset<KotlinAndroidTarget> {
override fun getName(): String = PRESET_NAME
override fun createTarget(name: String): KotlinAndroidTarget {
val result = KotlinAndroidTarget(project).apply {
disambiguationClassifier = name
val targetConfigurator = KotlinOnlyTargetConfigurator(buildOutputCleanupRegistry)
compilations.all { compilation ->
targetConfigurator.defineConfigurationsForCompilation(compilation, this@apply, project.configurations)
}
}
KotlinAndroidPlugin.applyToTarget(
project, result, project.kotlinExtension.sourceSetProvider,
AndroidTasksProvider(), kotlinPluginVersion
)
return result
}
companion object {
const val PRESET_NAME = "android"
}
}
class KotlinJvmWithJavaTargetPreset(
private val project: Project,
private val kotlinPluginVersion: String
): KotlinTargetPreset<KotlinWithJavaTarget> {
override fun getName(): String = PRESET_NAME
override fun createTarget(name: String): KotlinWithJavaTarget {
project.plugins.apply(JavaPlugin::class.java)
val target = KotlinWithJavaTarget(project, KotlinPlatformType.JVM, name)
AbstractKotlinPlugin.configureTarget(target) { compilation ->
Kotlin2JvmSourceSetProcessor(
project,
KotlinTasksProvider(),
compilation as KotlinJvmCompilation,
kotlinPluginVersion
)
}
return target
}
companion object {
const val PRESET_NAME = "jvmWithJava"
}
}
class KotlinNativeTargetPreset(
private val name: String,
val project: Project,
val platformType: KotlinNativePlatformType,
private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry
) : KotlinTargetPreset<KotlinNativeTarget> {
override fun getName(): String = name
private val Collection<*>.isDimensionVisible: Boolean
get() = size > 1
private fun createDimensionSuffix(dimensionName: String, multivalueProperty: Collection<*>): String =
if (multivalueProperty.isDimensionVisible) {
dimensionName.toLowerCase().capitalize()
} else {
""
}
private fun Task.dependsOnCompilerDownloading() {
val checkCompilerTask = project.tasks.maybeCreate(
KonanCompilerDownloadTask.KONAN_DOWNLOAD_TASK_NAME,
KonanCompilerDownloadTask::class.java
)
dependsOn(checkCompilerTask)
}
private fun createKlibCompilationTask(compilation: KotlinNativeCompilation) {
val compileTask = project.tasks.create(
compilation.compileKotlinTaskName,
KotlinNativeCompile::class.java
).apply {
this.compilation = compilation
outputKind = CompilerOutputKind.LIBRARY
group = BasePlugin.BUILD_GROUP
description = "Compiles a klibrary from the '${compilation.name}' " +
"compilation for target '${compilation.platformType.name}'"
outputFile.set {
val targetSubDirectory = compilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty()
val compilationName = compilation.compilationName
val klibName = if (compilation.name == KotlinCompilation.MAIN_COMPILATION_NAME)
project.name
else
compilationName
File(project.buildDir, "classes/kotlin/$targetSubDirectory$compilationName/$klibName.klib")
}
dependsOnCompilerDownloading()
}
compilation.output.tryAddClassesDir { project.files(compileTask.outputFile.asFile.get()) }
project.tasks.getByName(compilation.compileAllTaskName).dependsOn(compileTask)
if (compilation.compilationName == KotlinCompilation.MAIN_COMPILATION_NAME) {
project.tasks.findByName(compilation.target.artifactsTaskName)?.dependsOn(compileTask)
val apiElements = project.configurations.getByName(compilation.target.apiElementsConfigurationName)
apiElements.artifacts.add(
DefaultPublishArtifact(
compilation.name,
"klib",
"klib",
"klib",
Date(),
compileTask.outputFile.asFile.get(),
compileTask
)
)
}
}
private fun createTestTask(compilation: KotlinNativeCompilation, testExecutableLinkTask: KotlinNativeCompile) {
val taskName = lowerCamelCaseName("run", compilation.name, compilation.platformType.name)
val testTask = project.tasks.create(taskName, RunTestExecutable::class.java).apply {
group = LifecycleBasePlugin.VERIFICATION_GROUP
description = "Executes Kotlin/Native unit tests from the '${compilation.name}' compilation " +
"for target '${compilation.platformType.name}'"
val testExecutableProperty = testExecutableLinkTask.outputFile
executable = testExecutableProperty.asFile.get().absolutePath
// TODO: Provide a normal test path!
outputDir = project.layout.buildDirectory.dir("test-results").get().asFile
onlyIf { testExecutableProperty.asFile.get().exists() }
inputs.file(testExecutableProperty)
dependsOn(testExecutableLinkTask)
dependsOnCompilerDownloading()
}
}
// Called in whenEvaluated block.
private fun createBinaryLinkTasks(compilation: KotlinNativeCompilation) {
val konanTarget = compilation.target.konanTarget
val buildTypes = compilation.buildTypes
val availableOutputKinds = compilation.outputKinds.filter { it.availableFor(konanTarget) }
// TODO: Consider using lockable set property.
// TODO: Provide outgoing configurations somehow.
for (buildType in compilation.buildTypes) {
for (kind in availableOutputKinds) {
val compilerOutputKind = kind.compilerOutputKind
val compilationSuffix = compilation.name.takeIf { it != "main" }.orEmpty()
val buildTypeSuffix = createDimensionSuffix(buildType.name, buildTypes)
val targetSuffix = compilation.target.name
val taskName = lowerCamelCaseName("link", compilationSuffix, buildTypeSuffix, kind.taskNameClassifier, targetSuffix)
val linkTask = project.tasks.create(
taskName,
KotlinNativeCompile::class.java
).apply {
this.compilation = compilation
outputKind = compilerOutputKind
group = BasePlugin.BUILD_GROUP
description = "Links ${kind.description} from the '${compilation.name}' " +
"compilation for target '${compilation.platformType.name}'"
optimized = buildType.optimized
debuggable = buildType.debuggable
outputFile.set {
val targetSubDirectory = compilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty()
val compilationName = compilation.compilationName
val prefix = compilerOutputKind.prefix(konanTarget)
val suffix = compilerOutputKind.suffix(konanTarget)
File(project.buildDir, "bin/$targetSubDirectory$compilationName/$prefix$compilationName$suffix")
}
dependsOnCompilerDownloading()
}
project.tasks.maybeCreate(compilation.linkAllTaskName).dependsOn(linkTask)
if (compilation.isTestCompilation &&
buildType == NativeBuildType.DEBUG &&
konanTarget == HostManager.host
) {
createTestTask(compilation, linkTask)
}
}
}
}
override fun createTarget(name: String): KotlinNativeTarget {
val result = KotlinNativeTarget(project, platformType).apply {
targetName = name
disambiguationClassifier = name
val compilationFactory = KotlinNativeCompilationFactory(project, this)
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
KotlinOnlyTargetConfigurator(buildOutputCleanupRegistry).configureTarget(project, result)
// TODO: Move into KotlinNativeTargetConfigurator
result.compilations.all { compilation ->
createKlibCompilationTask(compilation)
project.whenEvaluated {
createBinaryLinkTasks(compilation)
}
}
return result
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.DependencyConstraint
import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.artifacts.PublishArtifact
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.attributes.Usage
import org.gradle.api.capabilities.Capability
import org.gradle.api.internal.component.SoftwareComponentInternal
import org.gradle.api.internal.component.UsageContext
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.base.usageByName
class KotlinSoftwareComponent(
private val project: Project,
private val name: String,
private val kotlinTargets: Iterable<KotlinTarget>
) : SoftwareComponentInternal {
override fun getUsages(): Set<UsageContext> = kotlinTargets.flatMap { it.createUsageContexts() }.toSet()
override fun getName(): String = name
companion object {
fun kotlinApiUsage(project: Project) = project.usageByName(Usage.JAVA_API)
fun kotlinRuntimeUsage(project: Project) = project.usageByName(Usage.JAVA_RUNTIME)
}
}
internal class KotlinPlatformUsageContext(
val project: Project,
val kotlinTarget: KotlinTarget,
private val usage: Usage,
val dependencyConfigurationName: String
) : UsageContext {
override fun getUsage(): Usage = usage
override fun getName(): String = kotlinTarget.targetName + when (usage.name) {
Usage.JAVA_API -> "-api"
Usage.JAVA_RUNTIME -> "-runtime"
else -> error("unexpected usage")
}
private val configuration: Configuration
get() = project.configurations.getByName(dependencyConfigurationName)
override fun getDependencies(): MutableSet<out ModuleDependency> =
configuration.incoming.dependencies.withType(ModuleDependency::class.java)
override fun getDependencyConstraints(): MutableSet<out DependencyConstraint> =
configuration.incoming.dependencyConstraints
override fun getArtifacts(): MutableSet<out PublishArtifact> =
// TODO Gradle Java plugin does that in a different way; check whether we can improve this
configuration.artifacts
override fun getAttributes(): AttributeContainer =
configuration.outgoing.attributes
override fun getCapabilities(): Set<Capability> = emptySet()
// FIXME this is a stub for a function that is not present in the Gradle API that we compile against
fun getGlobalExcludes(): Set<Any> = emptySet()
}

View File

@@ -0,0 +1,66 @@
/*
* 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.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.file.FileCollectionInternal
import org.gradle.api.internal.file.FileCollectionVisitor
import org.gradle.api.internal.file.FileSystemSubset
import org.gradle.api.tasks.SourceSetOutput
import java.io.File
import java.util.concurrent.Callable
private class MutableReference<T>(var item: T)
class KotlinSourceSetOutput constructor(
private val project: Project,
private var resourcesDir: Any,
private var backingFileCollection: ConfigurableFileCollection = project.files() // must be a parameter since it is used in delegation
) : SourceSetOutput, ConfigurableFileCollection by backingFileCollection, FileCollectionInternal {
/* TODO report Gradle bug: cannot implement SourceSetOutput without also implementing FileCollectionInternal, there's a cast to the latter
inside the Gradle logic for resolving file collections */
override fun registerWatchPoints(p0: FileSystemSubset.Builder) = (backingFileCollection as FileCollectionInternal).registerWatchPoints(p0)
override fun visitRootElements(p0: FileCollectionVisitor) = (backingFileCollection as FileCollectionInternal).visitRootElements(p0)
private val classesDirs: FileCollection = project.files()
private val otherDirs: ConfigurableFileCollection = project.files()
init {
this.from(classesDirs)
this.from(Callable { resourcesDir })
}
override fun setResourcesDir(dirNotation: Any) {
resourcesDir = dirNotation
}
override fun setResourcesDir(dir: File): Unit = setResourcesDir(dir as Any)
override fun getDirs(): FileCollection = otherDirs
override fun isLegacyLayout(): Boolean = false
override fun getResourcesDir(): File = project.file(resourcesDir)
override fun setClassesDir(file: File?) = @Suppress("DEPRECATION") setClassesDir(file as Any)
override fun setClassesDir(fileNotation: Any?) = throw UnsupportedOperationException("Setting classesDir is not supported, use classesDirs.")
override fun getClassesDir(): File = classesDirs.singleFile
override fun getClassesDirs(): FileCollection = classesDirs
override fun dir(args: Map<String, Any>, dir: Any?) {
val dirFileCollection = project.files(dir)
args["builtBy"]?.let { dirFileCollection.builtBy(it) }
otherDirs.from(dirFileCollection)
}
override fun dir(dirNotation: Any?): Unit = dir(emptyMap(), dirNotation)
}

View File

@@ -0,0 +1,120 @@
/*
* 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.gradle.plugin.mpp
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.internal.component.UsageContext
import org.gradle.api.plugins.JavaPlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinNativePlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.base.KotlinJvmAndroidCompilationFactory
import org.jetbrains.kotlin.gradle.plugin.base.KotlinWithJavaCompilationFactory
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.KonanTarget
abstract class AbstractKotlinTarget (
override val project: Project
) : KotlinTarget {
private val attributeContainer = HierarchyAttributeContainer(parent = null)
override fun getAttributes(): AttributeContainer = attributeContainer
override val defaultConfigurationName: String
get() = disambiguateName("default")
override val apiElementsConfigurationName: String
get() = disambiguateName("apiElements")
override val runtimeElementsConfigurationName: String
get() = disambiguateName("runtimeElements")
override val artifactsTaskName: String
get() = disambiguateName("jar")
override fun toString(): String = "target $name ($platformType)"
override fun createUsageContexts(): Set<UsageContext> = setOf(
KotlinPlatformUsageContext(project, this, KotlinSoftwareComponent.kotlinApiUsage(project), apiElementsConfigurationName),
KotlinPlatformUsageContext(project, this, KotlinSoftwareComponent.kotlinRuntimeUsage(project), runtimeElementsConfigurationName)
)
}
internal fun KotlinTarget.disambiguateName(simpleName: String) =
lowerCamelCaseName(targetName, simpleName)
open class KotlinAndroidTarget(project: Project) : AbstractKotlinTarget(project) {
override val targetName: String
get() = "android"
override var disambiguationClassifier: String? = null
internal set
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.JVM
private val compilationFactory = KotlinJvmAndroidCompilationFactory(project, this)
override val compilations: NamedDomainObjectContainer<out KotlinJvmAndroidCompilation> =
project.container(compilationFactory.itemClass, compilationFactory)
override fun createUsageContexts(): Set<UsageContext> {
//TODO setup Android libraries publishing. This will likely require new API in the Android Gradle plugin
return emptySet()
}
}
open class KotlinWithJavaTarget(
project: Project,
override val platformType: KotlinPlatformType,
override val targetName: String
) : AbstractKotlinTarget(project) {
override var disambiguationClassifier: String? = null
internal set
override val defaultConfigurationName: String
get() = Dependency.DEFAULT_CONFIGURATION
override val apiElementsConfigurationName: String
get() = JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME
override val runtimeElementsConfigurationName: String
get() = JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME
override val artifactsTaskName: String
get() = JavaPlugin.JAR_TASK_NAME
override val compilations: NamedDomainObjectContainer<KotlinWithJavaCompilation> =
project.container(KotlinWithJavaCompilation::class.java, KotlinWithJavaCompilationFactory(project, this))
}
open class KotlinOnlyTarget<T : KotlinCompilation>(
project: Project,
override val platformType: KotlinPlatformType
) : AbstractKotlinTarget(project) {
override lateinit var compilations: NamedDomainObjectContainer<T>
internal set
override lateinit var targetName: String
internal set
override var disambiguationClassifier: String? = null
internal set
}
class KotlinNativeTarget(
project: Project,
override val platformType: KotlinNativePlatformType
) : KotlinOnlyTarget<KotlinNativeCompilation>(project, platformType) {
val konanTarget: KonanTarget
get() = platformType.konanTarget
}

View File

@@ -0,0 +1,8 @@
/*
* 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.gradle.plugin.mpp
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget

View File

@@ -0,0 +1,48 @@
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Named
import org.gradle.api.attributes.Usage
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.Family
import org.jetbrains.kotlin.konan.target.KonanTarget
object KotlinNativeUsage {
const val KLIB = "kotlin-native-klib"
const val FRAMEWORK = "kotlin-native-framework"
}
enum class NativeBuildType(val optimized: Boolean, val debuggable: Boolean): Named {
RELEASE(true, false),
DEBUG(false, true);
override fun getName(): String = name.toLowerCase()
}
enum class NativeOutputKind(
val compilerOutputKind: CompilerOutputKind,
val taskNameClassifier: String,
val description: String = taskNameClassifier,
val additionalCompilerFlags: List<String> = emptyList(),
val runtimeUsageName: String? = null,
val linkUsageName: String? = null,
val publishable: Boolean = true
) {
EXECUTABLE(
CompilerOutputKind.PROGRAM,
"executable",
description = "an executable",
runtimeUsageName = Usage.NATIVE_RUNTIME
),
FRAMEWORK(
CompilerOutputKind.FRAMEWORK,
"framework",
description = "an Objective C framework",
linkUsageName = KotlinNativeUsage.FRAMEWORK,
publishable = false
) {
override fun availableFor(target: KonanTarget) =
target.family == Family.OSX || target.family == Family.IOS
};
open fun availableFor(target: KonanTarget) = true
}

View File

@@ -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.kotlin.gradle.plugin.sources
import org.gradle.api.NamedDomainObjectFactory
import org.gradle.api.Project
import org.gradle.api.internal.file.FileResolver
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
import java.io.File
internal abstract class KotlinSourceSetFactory<T : KotlinSourceSet> internal constructor(
protected val fileResolver: FileResolver,
protected val project: Project
) : NamedDomainObjectFactory<T> {
abstract val itemClass: Class<T>
override fun create(name: String): T {
val result = doCreateSourceSet(name)
setUpSourceSetDefaults(result)
return result
}
protected open fun defaultSourceLocation(sourceSetName: String): File =
project.file("src/$sourceSetName")
protected open fun setUpSourceSetDefaults(sourceSet: T) {
with(sourceSet) {
sourceSet.kotlin.srcDir(File(defaultSourceLocation(sourceSet.name), "kotlin"))
}
}
protected abstract fun doCreateSourceSet(name: String): T
}
internal class DefaultKotlinSourceSetFactory(
project: Project,
fileResolver: FileResolver
) : KotlinSourceSetFactory<DefaultKotlinSourceSet>(fileResolver, project) {
override val itemClass: Class<DefaultKotlinSourceSet>
get() = DefaultKotlinSourceSet::class.java
override fun setUpSourceSetDefaults(sourceSet: DefaultKotlinSourceSet) {
super.setUpSourceSetDefaults(sourceSet)
sourceSet.resources.srcDir(File(defaultSourceLocation(sourceSet.name), "resources"))
}
override fun doCreateSourceSet(name: String): DefaultKotlinSourceSet {
return DefaultKotlinSourceSet(project, name, fileResolver)
}
}

View File

@@ -0,0 +1,112 @@
/*
* 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.gradle.plugin.sources
import groovy.lang.Closure
import org.gradle.api.Project
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.internal.file.DefaultSourceDirectorySet
import org.gradle.api.internal.file.FileResolver
import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
import org.jetbrains.kotlin.gradle.plugin.executeClosure
import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultKotlinDependencyHandler
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSetWithResources
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import java.lang.reflect.Constructor
abstract class AbstractKotlinSourceSet(
val displayName: String,
fileResolver: FileResolver
) : KotlinSourceSet {
override fun getName(): String = displayName
final override val kotlin: SourceDirectorySet =
createDefaultSourceDirectorySet(name + " Kotlin source", fileResolver).apply {
filter.include("**/*.java", "**/*.kt", "**/*.kts")
}
final override fun kotlin(configureClosure: Closure<Any?>): SourceDirectorySet {
ConfigureUtil.configure(configureClosure, kotlin)
return kotlin
}
}
internal fun KotlinSourceSet.composeName(prefix: String? = null, suffix: String? = null): String {
val sourceSetName = (if (name == "main") "" else name).let {
if (prefix.isNullOrEmpty()) it else it.capitalize()
}
val resultPrefix = (prefix ?: "") + sourceSetName
val resultSuffix = (if (resultPrefix.isEmpty()) suffix else suffix?.capitalize()) ?: ""
return resultPrefix + resultSuffix
}
class DefaultKotlinSourceSet(
private val project: Project,
val displayName: String,
fileResolver: FileResolver
) : KotlinSourceSetWithResources {
override val apiConfigurationName: String
get() = disambiguateName("api")
override val implementationConfigurationName: String
get() = disambiguateName("implementation")
override val compileOnlyConfigurationName: String
get() = disambiguateName("compileOnly")
override val runtimeOnlyConfigurationName: String
get() = disambiguateName("runtimeOnly")
override val kotlin: SourceDirectorySet = createDefaultSourceDirectorySet(displayName + ".kotlin", fileResolver)
override val resources: SourceDirectorySet = createDefaultSourceDirectorySet(displayName + ".resources", fileResolver)
override fun kotlin(configureClosure: Closure<Any?>): SourceDirectorySet = kotlin.apply { executeClosure(configureClosure) }
override fun getName(): String = displayName
override fun dependencies(configure: KotlinDependencyHandler.() -> Unit): Unit =
DefaultKotlinDependencyHandler(this, project).run(configure)
override fun dependencies(configureClosure: Closure<Any?>) =
dependencies f@{ this@f.executeClosure(configureClosure) }
override fun toString(): String = "source set $name"
}
class AndroidSourceSet
internal fun KotlinSourceSet.disambiguateName(simpleName: String): String {
val nameParts = listOfNotNull(this.name.takeIf { it != "main" }, simpleName)
return lowerCamelCaseName(*nameParts.toTypedArray())
}
private val createDefaultSourceDirectorySet: (name: String?, resolver: FileResolver?) -> SourceDirectorySet = run {
val klass = DefaultSourceDirectorySet::class.java
val defaultConstructor = klass.constructorOrNull(String::class.java, FileResolver::class.java)
if (defaultConstructor != null && defaultConstructor.getAnnotation(java.lang.Deprecated::class.java) == null) {
// TODO: drop when gradle < 2.12 are obsolete
{ name, resolver -> defaultConstructor.newInstance(name, resolver) }
} else {
val directoryFileTreeFactoryClass = Class.forName("org.gradle.api.internal.file.collections.DirectoryFileTreeFactory")
val alternativeConstructor = klass.getConstructor(String::class.java, FileResolver::class.java, directoryFileTreeFactoryClass)
val defaultFileTreeFactoryClass = Class.forName("org.gradle.api.internal.file.collections.DefaultDirectoryFileTreeFactory")
val defaultFileTreeFactory = defaultFileTreeFactoryClass.getConstructor().newInstance()
return@run { name, resolver -> alternativeConstructor.newInstance(name, resolver, defaultFileTreeFactory) }
}
}
private fun <T> Class<T>.constructorOrNull(vararg parameterTypes: Class<*>): Constructor<T>? =
try {
getConstructor(*parameterTypes)
} catch (e: NoSuchMethodException) {
null
}

View File

@@ -7,12 +7,9 @@ package org.jetbrains.kotlin.gradle.scripting.internal
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.gradle.plugin.JetBrainsSubpluginArtifact
import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin
import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
import org.jetbrains.kotlin.gradle.scripting.ScriptingExtension
class ScriptingGradleSubplugin : Plugin<Project> {
@@ -39,10 +36,10 @@ class ScriptingKotlinGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
override fun apply(
project: Project,
kotlinCompile: AbstractCompile,
javaCompile: AbstractCompile,
javaCompile: AbstractCompile?,
variantData: Any?,
androidProjectHandler: Any?,
javaSourceSet: SourceSet?
kotlinCompilation: KotlinCompilation?
): List<SubpluginOption> {
if (!ScriptingGradleSubplugin.isEnabled(project)) return emptyList()

View File

@@ -0,0 +1,183 @@
package org.jetbrains.kotlin.gradle.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.GradleScriptException
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.*
import org.jetbrains.kotlin.compilerRunner.KonanCompilerRunner
import org.jetbrains.kotlin.compilerRunner.KotlinNativeProjectProperty
import org.jetbrains.kotlin.compilerRunner.getProperty
import org.jetbrains.kotlin.compilerRunner.hasProperty
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
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.kotlin.konan.util.DependencyProcessor
import org.jetbrains.kotlin.konan.util.DependencySource
import java.io.IOException
// TODO: It's just a temporary task used while KN isn't integrated with Big Kotlin compilation infrastructure.
open class KotlinNativeCompile: DefaultTask() {
init {
super.dependsOn(KonanCompilerDownloadTask.KONAN_DOWNLOAD_TASK_NAME)
}
@Internal
lateinit var compilation: KotlinNativeCompilation
// region inputs/outputs
@Input
lateinit var outputKind: CompilerOutputKind
// Inputs and outputs
val sources: Collection<FileCollection>
@InputFiles get() = compilation.kotlinSourceSets.map { it.kotlin }
val libraries: FileCollection
@InputFiles get() = compilation.compileDependencyFiles.filter {
it.extension == "klib"
}
@Input var optimized = false
@Input var debuggable = true
val processTests
@Input get() = compilation.isTestCompilation
val target: String
@Input get() = compilation.target.konanTarget.name
val additionalCompilerOptions: Collection<String>
@Input get() = compilation.extraOpts
@OutputFile val outputFile: RegularFileProperty = newOutputFile()
// endregion
// region Useful extensions
internal fun MutableList<String>.addArg(parameter: String, value: String) {
add(parameter)
add(value)
}
internal fun MutableList<String>.addArgs(parameter: String, values: Iterable<String>) {
values.forEach {
addArg(parameter, it)
}
}
internal fun MutableList<String>.addArgIfNotNull(parameter: String, value: String?) {
if (value != null) {
addArg(parameter, value)
}
}
internal fun MutableList<String>.addKey(key: String, enabled: Boolean) {
if (enabled) {
add(key)
}
}
internal fun MutableList<String>.addFileArgs(parameter: String, values: FileCollection) {
values.files.forEach {
addArg(parameter, it.canonicalPath)
}
}
internal fun MutableList<String>.addFileArgs(parameter: String, values: Collection<FileCollection>) {
values.forEach {
addFileArgs(parameter, it)
}
}
internal fun MutableList<String>.addListArg(parameter: String, values: List<String>) {
if (values.isNotEmpty()) {
addArg(parameter, values.joinToString(separator = " "))
}
}
// endregion
@TaskAction
fun compile() {
val output = outputFile.asFile.get()
output.parentFile.mkdirs()
val args = mutableListOf<String>().apply {
addArg("-o", outputFile.get().asFile.absolutePath)
addKey("-opt", optimized)
addKey("-g", debuggable)
addKey("-ea", debuggable)
addKey("-tr", processTests)
addArg("-target", target)
addArg("-p", outputKind.name.toLowerCase())
add("-Xmulti-platform")
addAll(additionalCompilerOptions)
libraries.files.forEach {library ->
library.parent?.let { addArg("-r", it) }
addArg("-l", library.nameWithoutExtension)
}
// TODO: Filter only kt files?
addAll(sources.flatMap { it.files }.map { it.absolutePath })
}
KonanCompilerRunner(project).run(args)
}
}
open class KonanCompilerDownloadTask : DefaultTask() {
internal companion object {
internal const val BASE_DOWNLOAD_URL = "https://download.jetbrains.com/kotlin/native/builds"
const val KONAN_DOWNLOAD_TASK_NAME = "checkKonanCompiler"
}
private val simpleOsName: String = HostManager.simpleOsName()
private val konanCompilerName: String =
"kotlin-native-$simpleOsName-${KonanVersion.CURRENT}"
@TaskAction
fun downloadAndExtract() {
if (!project.hasProperty(KotlinNativeProjectProperty.DOWNLOAD_COMPILER)) {
val konanHome = project.getProperty(KotlinNativeProjectProperty.KONAN_HOME)
logger.info("Use a user-defined compiler path: $konanHome")
} else {
try {
val downloadUrlDirectory = buildString {
append("$BASE_DOWNLOAD_URL/")
val version = KonanVersion.CURRENT
when (version.meta) {
MetaVersion.DEV -> append("dev/")
else -> append("releases/")
}
append("$version/")
append(simpleOsName)
}
val konanCompiler = konanCompilerName
val parentDir = DependencyProcessor.localKonanDir
logger.info("Downloading Kotlin/Native compiler from $downloadUrlDirectory/$konanCompiler into $parentDir")
DependencyProcessor(
parentDir,
downloadUrlDirectory,
mapOf(konanCompiler to listOf(DependencySource.Remote.Public))
).run()
} catch (e: IOException) {
throw GradleScriptException("Cannot download Kotlin/Native compiler", e)
}
}
}
}

View File

@@ -272,7 +272,7 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
args.verbose = true
}
args.multiPlatform = project.plugins.any { it is KotlinPlatformPluginBase }
args.multiPlatform = project.plugins.any { it is KotlinPlatformPluginBase || it is KotlinMultiplatformPluginWrapper }
setupPlugins(args)
}

View File

@@ -0,0 +1,35 @@
/*
* 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.gradle.utils
import org.gradle.api.NamedDomainObjectCollection
/**
* Applies [whenMatched] to pairs of items with the same name in [containerA] and [containerB],
* regardless of the order in which they are added to the containers.
*/
internal fun <A, B> matchSymmetricallyByNames(
containerA: NamedDomainObjectCollection<out A>,
containerB: NamedDomainObjectCollection<out B>,
whenMatched: (A, B) -> Unit
) {
val matchedNames = mutableSetOf<String>()
fun <T, R> NamedDomainObjectCollection<T>.matchAllWith(other: NamedDomainObjectCollection<R>, match: (T, R) -> Unit) {
this@matchAllWith.all { item ->
val itemName = this@matchAllWith.namer.determineName(item)
if (itemName !in matchedNames) {
val otherItem = other.findByName(itemName)
if (otherItem != null) {
matchedNames += itemName
match(item, otherItem)
}
}
}
}
containerA.matchAllWith(containerB) { a, b -> whenMatched(a, b) }
containerB.matchAllWith(containerA) { b, a -> whenMatched(a, b) }
}

View File

@@ -0,0 +1,15 @@
/*
* 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.gradle.utils
internal fun lowerCamelCaseName(vararg nameParts: String?): String {
val nonEmptyParts = nameParts.mapNotNull { it?.takeIf(String::isNotEmpty) }
return nonEmptyParts.drop(1).joinToString(
separator = "",
prefix = nonEmptyParts.firstOrNull().orEmpty(),
transform = String::capitalize
)
}

View File

@@ -0,0 +1 @@
implementation-class=org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper

View File

@@ -18,15 +18,11 @@ package org.jetbrains.kotlin.noarg.gradle
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.internal.AbstractTask
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.internal.ConventionTask
import org.gradle.api.tasks.SourceSet
import org.gradle.api.internal.AbstractTask
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.gradle.plugin.JetBrainsSubpluginArtifact
import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin
import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
class NoArgGradleSubplugin : Plugin<Project> {
companion object {
@@ -54,12 +50,12 @@ class NoArgKotlinGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
override fun isApplicable(project: Project, task: AbstractCompile) = NoArgGradleSubplugin.isEnabled(project)
override fun apply(
project: Project,
kotlinCompile: AbstractCompile,
javaCompile: AbstractCompile,
variantData: Any?,
androidProjectHandler: Any?,
javaSourceSet: SourceSet?
project: Project,
kotlinCompile: AbstractCompile,
javaCompile: AbstractCompile?,
variantData: Any?,
androidProjectHandler: Any?,
kotlinCompilation: KotlinCompilation?
): List<SubpluginOption> {
if (!NoArgGradleSubplugin.isEnabled(project)) return emptyList()

View File

@@ -18,15 +18,11 @@ package org.jetbrains.kotlin.samWithReceiver.gradle
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.internal.AbstractTask
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.internal.ConventionTask
import org.gradle.api.tasks.SourceSet
import org.gradle.api.internal.AbstractTask
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.gradle.plugin.JetBrainsSubpluginArtifact
import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin
import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
class SamWithReceiverGradleSubplugin : Plugin<Project> {
companion object {
@@ -49,12 +45,12 @@ class SamWithReceiverKotlinGradleSubplugin : KotlinGradleSubplugin<AbstractCompi
override fun isApplicable(project: Project, task: AbstractCompile) = SamWithReceiverGradleSubplugin.isEnabled(project)
override fun apply(
project: Project,
kotlinCompile: AbstractCompile,
javaCompile: AbstractCompile,
variantData: Any?,
androidProjectHandler: Any?,
javaSourceSet: SourceSet?
project: Project,
kotlinCompile: AbstractCompile,
javaCompile: AbstractCompile?,
variantData: Any?,
androidProjectHandler: Any?,
kotlinCompilation: KotlinCompilation?
): List<SubpluginOption> {
if (!SamWithReceiverGradleSubplugin.isEnabled(project)) return emptyList()

View File

@@ -1,12 +1,27 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.gradle.jvm.tasks.Jar
import com.github.jk1.tcdeps.KotlinScriptDslAdapter.teamcityServer
import com.github.jk1.tcdeps.KotlinScriptDslAdapter.tc
description = "Kotlin IDEA plugin"
plugins {
`java-base`
id("com.github.jk1.tcdeps") version "0.17"
}
repositories {
teamcityServer {
setUrl("http://buildserver.labs.intellij.net")
credentials {
username = "guest"
password = "guest"
}
}
}
val kotlinNativeVersion = "0.9-dev-2798"
// Do not rename, used in JPS importer
val projectsToShadow by extra(listOf(
":plugins:annotation-based-compiler-plugins-ide-support",
@@ -29,8 +44,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",
@@ -54,6 +71,9 @@ val sideJars by configurations.creating
dependencies {
packedJars(protobufFull())
packedJars(project(":core:builtins", configuration = "builtins"))
sideJars(tc("Kotlin_KotlinNative_Master_KotlinNativeLinuxDist:$kotlinNativeVersion:shared.jar"))
sideJars(tc("Kotlin_KotlinNative_Master_KotlinNativeLinuxDist:$kotlinNativeVersion:backend.native.jar"))
sideJars("org.jetbrains.kotlin:kotlin-native-gradle-plugin:0.9-dev-2809") { isTransitive = false }
sideJars(projectDist(":kotlin-script-runtime"))
sideJars(projectDist(":kotlin-stdlib"))
sideJars(projectDist(":kotlin-reflect"))

View File

@@ -178,6 +178,12 @@ include ":kotlin-build-common",
':kotlin-noarg:plugin-marker',
":test-instrumenter"
def isIncludeNative = true //todo: add property
if (isIncludeNative) {
include ":idea:idea-native",
":idea:idea-gradle:native"
}
def isTeamcityBuild = hasProperty("teamcity") || System.getenv("TEAMCITY_VERSION") != null
def includeUltimate = hasProperty("intellijUltimateEnabled") && intellijUltimateEnabled != 'false'
if (isTeamcityBuild || includeUltimate) {