From 3eaf46ae60309adfb09d5fef4ca81fcd490bb964 Mon Sep 17 00:00:00 2001 From: Ilya Matveev Date: Fri, 2 Feb 2018 17:26:50 +0700 Subject: [PATCH] [gradle-plugin] Support multiplatform projects in current DSL. --- .../kotlin-native-gradle-plugin/build.gradle | 11 +- .../gradle/plugin/KonanCompileConfig.kt | 9 ++ .../gradle/plugin/KonanInteropLibrary.kt | 2 +- .../kotlin/gradle/plugin/KonanSpecs.kt | 8 ++ .../gradle/plugin/tasks/KonanCompileTask.kt | 109 +++++++++++++++++- .../plugin/tasks/KonanCompilerDownloadTask.kt | 4 +- 6 files changed, 134 insertions(+), 9 deletions(-) diff --git a/tools/kotlin-native-gradle-plugin/build.gradle b/tools/kotlin-native-gradle-plugin/build.gradle index 88dd9c9cea4..312e17ec7e9 100644 --- a/tools/kotlin-native-gradle-plugin/build.gradle +++ b/tools/kotlin-native-gradle-plugin/build.gradle @@ -76,6 +76,7 @@ configurations { dependencies { compile "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion" compile "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion" + compile "org.jetbrains.kotlin:kotlin-gradle-plugin:$buildKotlinVersion" compile gradleApi() bundleDependencies "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion" @@ -119,15 +120,23 @@ processResources { from(file("$rootBuildDirectory/utilities/env_blacklist")) } +// TODO: Get rid of manual pom generation publishing { publications { gradlePlugin(MavenPublication) { artifact shadowJar pom.withXml { XmlProvider xml -> - def stdlibDep = xml.asNode().appendNode("dependencies").appendNode("dependency") + def deps = xml.asNode().appendNode("dependencies") + + def stdlibDep = deps.appendNode("dependency") stdlibDep.appendNode("groupId", "org.jetbrains.kotlin") stdlibDep.appendNode("artifactId", "kotlin-stdlib") stdlibDep.appendNode("version", "$buildKotlinVersion") + + def kotlinPluginDep = deps.appendNode("dependency") + kotlinPluginDep.appendNode("groupId", "org.jetbrains.kotlin") + kotlinPluginDep.appendNode("artifactId", "kotlin-gradle-plugin") + kotlinPluginDep.appendNode("version", "$buildKotlinVersion") } } } diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanCompileConfig.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanCompileConfig.kt index 912ec617b96..ea209aa2f8f 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanCompileConfig.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanCompileConfig.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.gradle.plugin +import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.file.FileCollection import org.gradle.api.internal.project.ProjectInternal @@ -51,6 +52,14 @@ abstract class KonanCompileConfig(name: String, override fun nativeLibraries(vararg libs: Any) = forEach { it.nativeLibraries(*libs) } override fun nativeLibraries(libs: FileCollection) = forEach { it.nativeLibraries(libs) } + override fun expectedBy(parameters: Map) = forEach { it.expectedBy(parameters) } + override fun expectedBy(commonProject: String) = forEach { it.expectedBy(commonProject) } + override fun expectedBy(commonProject: Project) = forEach { it.expectedBy(commonProject) } + override fun expectedBy(commonProject: String, sourceSetName: String) = + forEach { it.expectedBy(commonProject, sourceSetName) } + override fun expectedBy(commonProject: Project, sourceSetName: String) = + forEach { it.expectedBy(commonProject, sourceSetName) } + override fun linkerOpts(values: List) = forEach { it.linkerOpts(values) } override fun linkerOpts(vararg values: String) = forEach { it.linkerOpts(*values) } diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanInteropLibrary.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanInteropLibrary.kt index b09559c1074..3deaf4fa7dc 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanInteropLibrary.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanInteropLibrary.kt @@ -23,8 +23,8 @@ import org.gradle.api.file.FileCollection import org.gradle.api.internal.project.ProjectInternal import org.gradle.internal.reflect.Instantiator import org.gradle.util.ConfigureUtil -import org.jetbrains.kotlin.gradle.plugin.tasks.KonanInteropTask import org.jetbrains.kotlin.gradle.plugin.KonanInteropSpec.IncludeDirectoriesSpec +import org.jetbrains.kotlin.gradle.plugin.tasks.KonanInteropTask import org.jetbrains.kotlin.konan.target.KonanTarget import java.io.File diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanSpecs.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanSpecs.kt index 5f47f8fe39a..74b24bd3bf1 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanSpecs.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanSpecs.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.gradle.plugin import groovy.lang.Closure import org.gradle.api.Action +import org.gradle.api.Project import org.gradle.api.file.FileCollection interface KonanArtifactSpec { @@ -51,6 +52,13 @@ interface KonanCompileSpec: KonanBuildingSpec { fun nativeLibraries(vararg libs: Any) fun nativeLibraries(libs: FileCollection) + // DSL. Mutliplatform projects + fun expectedBy(parameters: Map) + fun expectedBy(commonProject: String, sourceSetName: String) + fun expectedBy(commonProject: String) + fun expectedBy(commonProject: Project) + fun expectedBy(commonProject: Project, sourceSetName: String) + // DSL. Other parameters. fun linkerOpts(vararg values: String) diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompileTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompileTask.kt index c59111db423..3e45f2e4ba8 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompileTask.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompileTask.kt @@ -16,7 +16,12 @@ package org.jetbrains.kotlin.gradle.plugin.tasks +import org.gradle.api.GradleException +import org.gradle.api.Project import org.gradle.api.file.FileCollection +import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.internal.HasConvention +import org.gradle.api.plugins.JavaPluginConvention import org.gradle.api.tasks.* import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.konan.target.CompilerOutputKind @@ -36,7 +41,6 @@ enum class Produce(val cliOption: String, val kind: CompilerOutputKind) { */ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec { - // TODO: Support custom runner options (java options) @Internal override val toolRunner = KonanCompilerRunner(project) abstract val produce: Produce @@ -50,6 +54,16 @@ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec { override val artifactPrefix: String @Internal get() = produce.kind.prefix(konanTarget) + // Mutliplatform support -------------------------------------------------- + + internal var commonProject: Project? = null + internal set + @Internal get + + @InputFiles val commonSrcFiles = mutableSetOf() + + @Internal protected val defaultSourceSetName = "main" + // Other compilation parameters ------------------------------------------- protected val srcFiles_ = mutableSetOf() @@ -78,6 +92,11 @@ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec { val apiVersion : String? @Optional @Input get() = project.konanExtension.apiVersion + protected fun directoryToKt(dir: Any) = project.fileTree(dir).apply { + include("**/*.kt") + exclude { it.file.startsWith(project.buildDir) } + } + // Command line ------------------------------------------------------------ override fun buildArgs() = mutableListOf().apply { @@ -106,10 +125,13 @@ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec { addKey("-ea", enableAssertions) addKey("--time", measureTime) addKey("-nodefaultlibs", noDefaultLibs) + addKey("-Xmulti-platform", commonProject != null) addAll(extraOpts) - srcFiles.flatMap { it.files }.filter { it.name.endsWith(".kt") }.mapTo(this) { it.canonicalPath } + listOf(srcFiles, commonSrcFiles).forEach { + it.flatMap { it.files }.filter { it.name.endsWith(".kt") }.mapTo(this) { it.canonicalPath } + } } // region DSL. @@ -117,10 +139,7 @@ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec { // DSL. Input/output files. override fun srcDir(dir: Any) { - srcFiles_.add(project.fileTree(dir).apply { - include("**/*.kt") - exclude { it.file.startsWith(project.buildDir) } - }) + srcFiles_.add(directoryToKt(dir)) } override fun srcFiles(vararg files: Any) { srcFiles_.add(project.files(files)) @@ -137,6 +156,53 @@ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec { nativeLibraries.add(libs) } + // DSL. Multiplatform projects. + + override fun expectedBy(parameters: Map) { + val project = parameters[PROJECT_KEY] ?: + throw GradleException("A project should be passed to expectedBy method") + val sourceSetName = parameters[SOURCE_SET_KEY] ?: defaultSourceSetName + + if (sourceSetName !is String) { + throw GradleException("sourceSet parameter of expectedBy should be String") + } + + when (project) { + is Project -> expectedBy(project, sourceSetName) + is String -> expectedBy(project, sourceSetName) + else -> throw GradleException("project parameter of expectedBy should be String") + } + } + + override fun expectedBy(commonProject: String, sourceSetName: String) = expectedBy(project.project(commonProject), sourceSetName) + override fun expectedBy(commonProject: String) = expectedBy(commonProject, defaultSourceSetName) + override fun expectedBy(commonProject: Project) = expectedBy(commonProject, defaultSourceSetName) + override fun expectedBy(commonProject: Project, sourceSetName: String) { + if (this.commonProject != null) { + throw GradleException("Artifact ${artifactName} has more then one expectedBy dependency") + } + + this.commonProject = commonProject + + commonProject.whenEvaluated { + if (!commonProject.pluginManager.hasPlugin("kotlin-platform-common")) { + throw GradleException("Artifact ${artifactName} of project ${this@KonanCompileTask.project.path} " + + "has an expectedBy dependency to a non-common project ${this.path}") + } + + val commonSourceSet = commonProject.sourceSets.let { + it.findByName(sourceSetName) ?: run { + logger.warn("Cannot find a source set ${sourceSetName} in project ${this.path}. " + + "Use the default one: ${defaultSourceSetName}") + it.getByName(defaultSourceSetName) + } + } + commonSourceSet.kotlin!!.srcDirs.forEach { + commonSrcFiles.add(directoryToKt(it)) + } + } + } + // DSL. Other parameters. override fun linkerOpts(values: List) = linkerOpts(*values.toTypedArray()) @@ -172,6 +238,37 @@ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec { measureTime = flag } // endregion + + // Some functions from Kotlin Gradle plugin. + protected fun Project.whenEvaluated(fn: Project.() -> Unit) { + if (state.executed) { + fn() + } else { + afterEvaluate { it.fn() } + } + } + + protected val Project.sourceSets: SourceSetContainer + get() = convention.getPlugin(JavaPluginConvention::class.java).sourceSets + + 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 + } + + protected fun Any.getConvention(name: String): Any? = + (this as HasConvention).convention.plugins[name] + + companion object { + const val PROJECT_KEY = "project" + const val SOURCE_SET_KEY = "sourceSet" + } + } open class KonanCompileProgramTask: KonanCompileTask() { diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompilerDownloadTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompilerDownloadTask.kt index 0dd4eb26018..788a31b3a85 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompilerDownloadTask.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompilerDownloadTask.kt @@ -21,7 +21,9 @@ import org.gradle.api.GradleScriptException import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction import org.jetbrains.kotlin.gradle.plugin.* -import org.jetbrains.kotlin.konan.util.* +import org.jetbrains.kotlin.konan.util.DependencyProcessor +import org.jetbrains.kotlin.konan.util.DependencySource +import org.jetbrains.kotlin.konan.util.visibleName import java.io.File import java.io.IOException