Implement new MPP plugin

This commit is contained in:
Sergey Igushkin
2018-08-02 02:25:51 +03:00
parent dc7d488deb
commit cb99f43033
10 changed files with 842 additions and 1 deletions
@@ -130,6 +130,10 @@ pluginBundle {
id = 'org.jetbrains.kotlin.jvm'
description = displayName = 'Kotlin JVM plugin'
}
kotlinMultiplatformPlugin {
id = 'org.jetbrains.kotlin.multiplatform'
description = displayName = 'Kotlin Multiplatform plugin'
}
kotlinAndroidPlugin {
id = 'org.jetbrains.kotlin.android'
description = displayName = 'Kotlin Android plugin'
@@ -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
}
@@ -121,6 +121,21 @@ open class Kotlin2JsPluginWrapper @Inject constructor(
get() = KotlinSingleJavaTargetExtension::class
}
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
}
fun Project.getKotlinPluginVersion(): String? =
plugins.asSequence().mapNotNull { (it as? KotlinBasePluginWrapper)?.kotlinPluginVersion }.firstOrNull()
@@ -0,0 +1,366 @@
/*
* 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.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.Dependency
import org.gradle.api.artifacts.PublishArtifact
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.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.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 KotlinTargetConfigurator(
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 }
// Workaround: adding the artifact during configuration seems to interfere with the Java plugin, which results into missing
// task dependency 'assemble -> jar' if the Java plugin is applied after this steps
project.afterEvaluate {
project.artifacts.add(Dependency.ARCHIVES_CONFIGURATION, jar) { jarArtifact ->
jarArtifact.builtBy(jar)
jarArtifact.type = ArtifactTypeDefinition.JAR_TYPE
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 -- do we need that too?
}
}
}
}
private fun addJar(configuration: Configuration, jarArtifact: PublishArtifact) {
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 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 = ""
}
}
companion object {
const val buildNeededTaskName = "buildAllNeeded"
const val buildDependentTaskName = "buildAllDependents"
fun defineConfigurationsForCompilation(
compilation: KotlinCompilation,
target: KotlinTarget,
configurations: ConfigurationContainer
) {
val compileConfiguration = configurations.maybeCreate(compilation.deprecatedCompileConfigurationName).apply {
usesPlatformOf(target)
isVisible = false
isCanBeConsumed = false
isCanBeResolved = 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
isCanBeConsumed = false
isCanBeResolved = 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
isCanBeConsumed = false
isCanBeResolved = 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 val KotlinCompilation.deprecatedCompileConfigurationName: String
get() = disambiguateName("compile")
private val KotlinCompilationToRunnableFiles.deprecatedRuntimeConfigurationName: String
get() = disambiguateName("runtime")
}
}
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.Usages")
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
}
@@ -0,0 +1,160 @@
/*
* 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.plugins.ReportingBasePlugin
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.gradle.util.ConfigureUtil
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
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, { ConfigureUtil.configure(configureClosure, this) })
@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)
project.plugins.apply(ReportingBasePlugin::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))
add(KotlinJvmWithJavaTargetPreset(project, kotlinPluginVersion))
}
}
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("kotlin", MavenPublication::class.java) { publication ->
publication.from(kotlinSoftwareComponent)
(publication as MavenPublicationInternal).publishWithOriginalFileName()
publication.artifactId = project.name
publication.groupId = project.group.toString()
}
}
}
project.components.add(kotlinSoftwareComponent)
}
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 mainCompilationAttributes = 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 ->
mainCompilationAttributes.keySet().forEach { key ->
copyAttribute(key, mainCompilationAttributes, configuration.attributes)
}
}
target.compilations.all { compilation ->
val compilationAttributes = compilation.attributes
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)
}
}
}
@@ -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.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()
}
@@ -0,0 +1,206 @@
/*
* 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.internal.file.FileResolver
import org.gradle.api.plugins.JavaPlugin
import org.gradle.internal.cleanup.BuildOutputCleanupRegistry
import org.gradle.internal.reflect.Instantiator
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.tasks.AndroidTasksProvider
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsTasksProvider
import org.jetbrains.kotlin.gradle.tasks.KotlinCommonTasksProvider
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
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)
}
KotlinTargetConfigurator(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(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(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
) : KotlinTargetPreset<KotlinAndroidTarget> {
override fun getName(): String = PRESET_NAME
override fun createTarget(name: String): KotlinAndroidTarget {
val result = KotlinAndroidTarget(name, project).apply {
disambiguationClassifier = name
}
KotlinAndroidPlugin.applyToTarget(
project, result, 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).apply {
disambiguationClassifier = name
}
AbstractKotlinPlugin.configureTarget(target) { compilation ->
Kotlin2JvmSourceSetProcessor(project, KotlinTasksProvider(), compilation, kotlinPluginVersion)
}
target.compilations.all { compilation ->
// Set up dependency resolution using platforms:
KotlinTargetConfigurator.defineConfigurationsForCompilation(compilation, target, project.configurations)
}
target.compilations.getByName("test").run {
val main = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
compileDependencyFiles = project.files(main.output, project.configurations.maybeCreate(compileDependencyConfigurationName))
runtimeDependencyFiles = project.files(output, main.output, project.configurations.maybeCreate(runtimeDependencyConfigurationName))
}
return target
}
companion object {
const val PRESET_NAME = "jvmWithJava"
}
}
@@ -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)
}
@@ -0,0 +1 @@
implementation-class=org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
@@ -0,0 +1 @@
implementation-class=org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper