Introduce Gradle multiplatform plugin
This commit is contained in:
+22
@@ -291,4 +291,26 @@ class KotlinGradleIT: BaseGradleIT() {
|
|||||||
assertSuccessful()
|
assertSuccessful()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testMultiplatformCompile() {
|
||||||
|
val project = Project("multiplatformProject", GRADLE_VERSION)
|
||||||
|
|
||||||
|
project.build("build") {
|
||||||
|
assertSuccessful()
|
||||||
|
assertContains(":lib:compileKotlinCommon",
|
||||||
|
":lib:compileTestKotlinCommon",
|
||||||
|
":libJvm:compileKotlin",
|
||||||
|
":libJvm:compileTestKotlin",
|
||||||
|
":libJs:compileKotlin2Js",
|
||||||
|
":libJs:compileTestKotlin2Js")
|
||||||
|
assertFileExists("lib/build/classes/main/foo/PlatformClass.kotlin_metadata")
|
||||||
|
assertFileExists("lib/build/classes/test/foo/PlatformTest.kotlin_metadata")
|
||||||
|
assertFileExists("libJvm/build/classes/main/foo/PlatformClass.class")
|
||||||
|
assertFileExists("libJvm/build/classes/test/foo/PlatformTest.class")
|
||||||
|
assertFileExists("libJs/build/classes/main/libJs_main.js")
|
||||||
|
assertFileExists("libJs/build/classes/test/libJs_test.js")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
apply plugin: 'kotlin-platform-common'
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
header class PlatformClass {
|
||||||
|
val value: String
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
header class PlatformTest {
|
||||||
|
//val value: PlatformClass
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
apply plugin: 'kotlin-platform-js'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implement project(":lib")
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
impl class PlatformClass {
|
||||||
|
impl val value: String = "JS"
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
impl class PlatformTest {
|
||||||
|
//impl val value: PlatformClass = PlatformClass()
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
apply plugin: 'kotlin-platform-jvm'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implement project(":lib")
|
||||||
|
compile 'com.google.guava:guava:20.0'
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
impl class PlatformClass {
|
||||||
|
impl val value: String = "JVM"
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val colors = ImmutableSet.of("red", "orange", "yellow")
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
impl class PlatformTest {
|
||||||
|
//impl val value: PlatformClass = PlatformClass()
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
include ':lib', ':libJvm', ':libJs'
|
||||||
+113
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.gradle.api.GradleException
|
||||||
|
import org.gradle.api.Plugin
|
||||||
|
import org.gradle.api.Project
|
||||||
|
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.KotlinCompile
|
||||||
|
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
|
||||||
|
|
||||||
|
abstract class KotlinPlatformPluginBase(protected val platformName: String) : Plugin<Project>
|
||||||
|
|
||||||
|
open class KotlinPlatformCommonPlugin : KotlinPlatformPluginBase("common") {
|
||||||
|
override fun apply(project: Project) {
|
||||||
|
project.applyPlugin<KotlinCommonPluginWrapper>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class KotlinPlatformImplementationPluginBase(platformName: String) : KotlinPlatformPluginBase(platformName) {
|
||||||
|
private val commonProjects = arrayListOf<Project>()
|
||||||
|
private val platformSourceSets = hashMapOf<String, SourceSet>()
|
||||||
|
|
||||||
|
override fun apply(project: Project) {
|
||||||
|
project.tasks.withType(AbstractKotlinCompile::class.java).all {
|
||||||
|
(it as KotlinCompile<*>).kotlinOptions.freeCompilerArgs += listOf("-Xmulti-platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
project.sourceSets.associateByTo(platformSourceSets, SourceSet::getName)
|
||||||
|
|
||||||
|
val implementConfig = project.configurations.create("implement")
|
||||||
|
implementConfig.isTransitive = false
|
||||||
|
implementConfig.dependencies.whenObjectAdded { dep ->
|
||||||
|
if (dep is ProjectDependency) {
|
||||||
|
addCommonProject(dep.dependencyProject, project)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw GradleException("$project `implement` dependency is not a project: $dep")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addCommonProject(commonProject: Project, platformProject: Project) {
|
||||||
|
commonProjects.add(commonProject)
|
||||||
|
if (commonProjects.size > 1) {
|
||||||
|
throw GradleException("Platform project $platformProject implements more than one common project: ${commonProjects.joinToString()}")
|
||||||
|
}
|
||||||
|
|
||||||
|
commonProject.whenEvaluated {
|
||||||
|
if ((!commonProject.plugins.hasPlugin(KotlinPlatformCommonPlugin::class.java))) {
|
||||||
|
throw GradleException("Platform project $platformProject implements non-common project $commonProject (`apply plugin 'kotlin-platform-kotlin'`)")
|
||||||
|
}
|
||||||
|
|
||||||
|
commonProject.sourceSets.all { commonSourceSet ->
|
||||||
|
// todo: warn if not found
|
||||||
|
val platformSourceSet = platformSourceSets[commonSourceSet.name]
|
||||||
|
val platformKotlinSourceDirectorySet = platformSourceSet?.kotlin
|
||||||
|
commonSourceSet.kotlin?.srcDirs?.forEach { platformKotlinSourceDirectorySet?.srcDir(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val SourceSet.kotlin: SourceDirectorySet?
|
||||||
|
get() = ((getConvention("kotlin") ?: getConvention("kotlin2js")) as? KotlinSourceSet)?.kotlin
|
||||||
|
}
|
||||||
|
|
||||||
|
open class KotlinPlatformJvmPlugin : KotlinPlatformImplementationPluginBase("jvm") {
|
||||||
|
override fun apply(project: Project) {
|
||||||
|
project.applyPlugin<KotlinPluginWrapper>()
|
||||||
|
super.apply(project)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class KotlinPlatformJsPlugin : KotlinPlatformImplementationPluginBase("js") {
|
||||||
|
override fun apply(project: Project) {
|
||||||
|
project.applyPlugin<Kotlin2JsPluginWrapper>()
|
||||||
|
super.apply(project)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val Project.sourceSets: SourceSetContainer
|
||||||
|
get() = convention.getPlugin(JavaPluginConvention::class.java).sourceSets
|
||||||
|
|
||||||
|
private fun <T> Project.whenEvaluated(fn: Project.()->T) {
|
||||||
|
if (state.executed) {
|
||||||
|
fn()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
afterEvaluate { it.fn() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <reified T : Plugin<*>> Project.applyPlugin() {
|
||||||
|
pluginManager.apply(T::class.java)
|
||||||
|
}
|
||||||
+37
@@ -209,6 +209,35 @@ internal class Kotlin2JsSourceSetProcessor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class KotlinCommonSourceSetProcessor(
|
||||||
|
project: Project,
|
||||||
|
javaBasePlugin: JavaBasePlugin,
|
||||||
|
sourceSet: SourceSet,
|
||||||
|
tasksProvider: KotlinTasksProvider,
|
||||||
|
kotlinSourceSetProvider: KotlinSourceSetProvider
|
||||||
|
) : KotlinSourceSetProcessor<KotlinCompileCommon>(
|
||||||
|
project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider,
|
||||||
|
dslExtensionName = KOTLIN_DSL_NAME,
|
||||||
|
taskDescription = "Compiles the kotlin sources in $sourceSet to Metadata.",
|
||||||
|
compileTaskNameSuffix = "kotlinCommon"
|
||||||
|
) {
|
||||||
|
override fun doTargetSpecificProcessing() {
|
||||||
|
project.afterEvaluate { project ->
|
||||||
|
kotlinTask.source(kotlinSourceSet.kotlin)
|
||||||
|
project.tasks.findByName(sourceSet.classesTaskName).dependsOn(kotlinTask)
|
||||||
|
// can be missing (e.g. in case of tests)
|
||||||
|
project.tasks.findByName(sourceSet.jarTaskName)?.dependsOn(kotlinTask)
|
||||||
|
val javaTask = project.tasks.findByName(sourceSet.compileJavaTaskName)
|
||||||
|
project.tasks.remove(javaTask)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val defaultKotlinDestinationDir: File
|
||||||
|
get() = sourceSet.output.classesDir
|
||||||
|
|
||||||
|
override fun doCreateTask(project: Project, taskName: String): KotlinCompileCommon =
|
||||||
|
tasksProvider.createKotlinCommonTask(project, taskName, sourceSet.name)
|
||||||
|
}
|
||||||
|
|
||||||
internal abstract class AbstractKotlinPlugin(
|
internal abstract class AbstractKotlinPlugin(
|
||||||
val tasksProvider: KotlinTasksProvider,
|
val tasksProvider: KotlinTasksProvider,
|
||||||
@@ -252,6 +281,14 @@ internal open class KotlinPlugin(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal open class KotlinCommonPlugin(
|
||||||
|
tasksProvider: KotlinTasksProvider,
|
||||||
|
kotlinSourceSetProvider: KotlinSourceSetProvider,
|
||||||
|
kotlinPluginVersion: String
|
||||||
|
) : AbstractKotlinPlugin(tasksProvider, kotlinSourceSetProvider, kotlinPluginVersion) {
|
||||||
|
override fun buildSourceSetProcessor(project: Project, javaBasePlugin: JavaBasePlugin, sourceSet: SourceSet, kotlinPluginVersion: String) =
|
||||||
|
KotlinCommonSourceSetProcessor(project, javaBasePlugin, sourceSet, tasksProvider, kotlinSourceSetProvider)
|
||||||
|
}
|
||||||
|
|
||||||
internal open class Kotlin2JsPlugin(
|
internal open class Kotlin2JsPlugin(
|
||||||
tasksProvider: KotlinTasksProvider,
|
tasksProvider: KotlinTasksProvider,
|
||||||
|
|||||||
+5
@@ -48,6 +48,11 @@ open class KotlinPluginWrapper @Inject constructor(fileResolver: FileResolver):
|
|||||||
KotlinPlugin(KotlinTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion, kotlinGradleBuildServices)
|
KotlinPlugin(KotlinTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion, kotlinGradleBuildServices)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open class KotlinCommonPluginWrapper @Inject constructor(fileResolver: FileResolver): KotlinBasePluginWrapper(fileResolver) {
|
||||||
|
override fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices) =
|
||||||
|
KotlinCommonPlugin(KotlinCommonTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion)
|
||||||
|
}
|
||||||
|
|
||||||
open class KotlinAndroidPluginWrapper @Inject constructor(fileResolver: FileResolver): KotlinBasePluginWrapper(fileResolver) {
|
open class KotlinAndroidPluginWrapper @Inject constructor(fileResolver: FileResolver): KotlinBasePluginWrapper(fileResolver) {
|
||||||
override fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices) =
|
override fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices) =
|
||||||
KotlinAndroidPlugin(AndroidTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion, kotlinGradleBuildServices)
|
KotlinAndroidPlugin(AndroidTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion, kotlinGradleBuildServices)
|
||||||
|
|||||||
+1
@@ -37,6 +37,7 @@ sealed internal class RegexTaskToFriendTaskMapper(
|
|||||||
) : TaskToFriendTaskMapper() {
|
) : TaskToFriendTaskMapper() {
|
||||||
class Default : RegexTaskToFriendTaskMapper("compile", "TestKotlin", "Kotlin")
|
class Default : RegexTaskToFriendTaskMapper("compile", "TestKotlin", "Kotlin")
|
||||||
class JavaScript : RegexTaskToFriendTaskMapper("compile", "TestKotlin2Js", "Kotlin2Js")
|
class JavaScript : RegexTaskToFriendTaskMapper("compile", "TestKotlin2Js", "Kotlin2Js")
|
||||||
|
class Common : RegexTaskToFriendTaskMapper("compile", "TestKotlinCommon", "KotlinCommon")
|
||||||
class Android : RegexTaskToFriendTaskMapper("compile", "(Unit|Android)TestKotlin", "Kotlin")
|
class Android : RegexTaskToFriendTaskMapper("compile", "(Unit|Android)TestKotlin", "Kotlin")
|
||||||
|
|
||||||
private val regex = "$prefix(.*)$suffix".toRegex()
|
private val regex = "$prefix(.*)$suffix".toRegex()
|
||||||
|
|||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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.tasks
|
||||||
|
|
||||||
|
import org.gradle.api.tasks.compile.AbstractCompile
|
||||||
|
import org.jetbrains.kotlin.cli.common.arguments.K2MetadataCompilerArguments
|
||||||
|
import org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler
|
||||||
|
import org.jetbrains.kotlin.config.Services
|
||||||
|
import org.jetbrains.kotlin.incremental.ChangedFiles
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
internal open class KotlinCompileCommon : AbstractKotlinCompile<K2MetadataCompilerArguments>() {
|
||||||
|
override val compiler = K2MetadataCompiler()
|
||||||
|
|
||||||
|
override fun populateCompilerArguments(): K2MetadataCompilerArguments =
|
||||||
|
K2MetadataCompilerArguments()
|
||||||
|
|
||||||
|
override fun getSourceRoots(): SourceRoots =
|
||||||
|
SourceRoots.KotlinOnly.create(getSource())
|
||||||
|
|
||||||
|
override fun callCompiler(args: K2MetadataCompilerArguments, sourceRoots: SourceRoots, changedFiles: ChangedFiles) {
|
||||||
|
val classpathList = classpath.files.toMutableList()
|
||||||
|
val friendTask = friendTaskName?.let { project.tasks.findByName(it) } as? AbstractCompile
|
||||||
|
friendTask?.let { classpathList.add(it.destinationDir) }
|
||||||
|
|
||||||
|
with(args) {
|
||||||
|
classpath = classpathList.joinToString(File.pathSeparator)
|
||||||
|
destination = destinationDir.canonicalPath
|
||||||
|
freeArgs = sourceRoots.kotlinSourceFiles.map { it.canonicalPath }
|
||||||
|
}
|
||||||
|
|
||||||
|
val messageCollector = GradleMessageCollector(project.logger)
|
||||||
|
val exitCode = compiler.exec(messageCollector, Services.EMPTY, args)
|
||||||
|
throwGradleExceptionIfError(exitCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -37,10 +37,21 @@ internal open class KotlinTasksProvider {
|
|||||||
friendTaskName = taskToFriendTaskMapper[this]
|
friendTaskName = taskToFriendTaskMapper[this]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createKotlinCommonTask(project: Project, name: String, sourceSetName: String): KotlinCompileCommon =
|
||||||
|
project.tasks.create(name, KotlinCompileCommon::class.java).apply {
|
||||||
|
this.sourceSetName = sourceSetName
|
||||||
|
friendTaskName = taskToFriendTaskMapper[this]
|
||||||
|
}
|
||||||
|
|
||||||
protected open val taskToFriendTaskMapper: TaskToFriendTaskMapper =
|
protected open val taskToFriendTaskMapper: TaskToFriendTaskMapper =
|
||||||
RegexTaskToFriendTaskMapper.Default()
|
RegexTaskToFriendTaskMapper.Default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class KotlinCommonTasksProvider : KotlinTasksProvider() {
|
||||||
|
override val taskToFriendTaskMapper: TaskToFriendTaskMapper =
|
||||||
|
RegexTaskToFriendTaskMapper.Common()
|
||||||
|
}
|
||||||
|
|
||||||
internal class Kotlin2JsTasksProvider : KotlinTasksProvider() {
|
internal class Kotlin2JsTasksProvider : KotlinTasksProvider() {
|
||||||
override val taskToFriendTaskMapper: TaskToFriendTaskMapper =
|
override val taskToFriendTaskMapper: TaskToFriendTaskMapper =
|
||||||
RegexTaskToFriendTaskMapper.JavaScript()
|
RegexTaskToFriendTaskMapper.JavaScript()
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
implementation-class=org.jetbrains.kotlin.gradle.plugin.KotlinPlatformCommonPlugin
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
implementation-class=org.jetbrains.kotlin.gradle.plugin.KotlinPlatformJsPlugin
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
implementation-class=org.jetbrains.kotlin.gradle.plugin.KotlinPlatformJvmPlugin
|
||||||
Reference in New Issue
Block a user