Introduce Gradle multiplatform plugin
This commit is contained in:
+22
@@ -291,4 +291,26 @@ class KotlinGradleIT: BaseGradleIT() {
|
||||
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(
|
||||
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(
|
||||
tasksProvider: KotlinTasksProvider,
|
||||
|
||||
+5
@@ -48,6 +48,11 @@ open class KotlinPluginWrapper @Inject constructor(fileResolver: FileResolver):
|
||||
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) {
|
||||
override fun getPlugin(kotlinGradleBuildServices: KotlinGradleBuildServices) =
|
||||
KotlinAndroidPlugin(AndroidTasksProvider(), KotlinSourceSetProviderImpl(fileResolver), kotlinPluginVersion, kotlinGradleBuildServices)
|
||||
|
||||
+1
@@ -37,6 +37,7 @@ sealed internal class RegexTaskToFriendTaskMapper(
|
||||
) : TaskToFriendTaskMapper() {
|
||||
class Default : RegexTaskToFriendTaskMapper("compile", "TestKotlin", "Kotlin")
|
||||
class JavaScript : RegexTaskToFriendTaskMapper("compile", "TestKotlin2Js", "Kotlin2Js")
|
||||
class Common : RegexTaskToFriendTaskMapper("compile", "TestKotlinCommon", "KotlinCommon")
|
||||
class Android : RegexTaskToFriendTaskMapper("compile", "(Unit|Android)TestKotlin", "Kotlin")
|
||||
|
||||
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]
|
||||
}
|
||||
|
||||
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 =
|
||||
RegexTaskToFriendTaskMapper.Default()
|
||||
}
|
||||
|
||||
internal class KotlinCommonTasksProvider : KotlinTasksProvider() {
|
||||
override val taskToFriendTaskMapper: TaskToFriendTaskMapper =
|
||||
RegexTaskToFriendTaskMapper.Common()
|
||||
}
|
||||
|
||||
internal class Kotlin2JsTasksProvider : KotlinTasksProvider() {
|
||||
override val taskToFriendTaskMapper: TaskToFriendTaskMapper =
|
||||
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