Add missing DSL features to the MPP dependencies DSL

* Add DSL for project(...) with path and configuration
* Add DSL for e.g. api(someDependency) { /* ... */ }
* Add DSL for e.g. api(kotlin("stdlib"))

Issue #KT-26663 Fixed
This commit is contained in:
Sergey Igushkin
2018-11-21 20:30:47 +03:00
parent cee52c095f
commit 4dd95e331f
9 changed files with 247 additions and 26 deletions
@@ -6,12 +6,43 @@
package org.jetbrains.kotlin.gradle.plugin
import groovy.lang.Closure
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ExternalModuleDependency
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.util.ConfigureUtil
interface KotlinDependencyHandler {
fun api(dependencyNotation: Any)
fun implementation(dependencyNotation: Any)
fun compileOnly(dependencyNotation: Any)
fun runtimeOnly(dependencyNotation: Any)
fun api(dependencyNotation: Any): Dependency?
fun api(dependencyNotation: String, configure: ExternalModuleDependency.() -> Unit): ExternalModuleDependency
fun <T : Dependency> api(dependency: T, configure: T.() -> Unit): T
fun api(dependencyNotation: String, configure: Closure<*>) = api(dependencyNotation) { ConfigureUtil.configure(configure, this) }
fun <T : Dependency> api(dependency: T, configure: Closure<*>) = api(dependency) { ConfigureUtil.configure(configure, this) }
fun implementation(dependencyNotation: Any): Dependency?
fun implementation(dependencyNotation: String, configure: ExternalModuleDependency.() -> Unit): ExternalModuleDependency
fun <T : Dependency> implementation(dependency: T, configure: T.() -> Unit): T
fun implementation(dependencyNotation: String, configure: Closure<*>) = implementation(dependencyNotation) { ConfigureUtil.configure(configure, this) }
fun <T : Dependency> implementation(dependency: T, configure: Closure<*>) = implementation(dependency) { ConfigureUtil.configure(configure, this) }
fun compileOnly(dependencyNotation: Any): Dependency?
fun compileOnly(dependencyNotation: String, configure: ExternalModuleDependency.() -> Unit): ExternalModuleDependency
fun <T : Dependency> compileOnly(dependency: T, configure: T.() -> Unit): T
fun compileOnly(dependencyNotation: String, configure: Closure<*>) = compileOnly(dependencyNotation) { ConfigureUtil.configure(configure, this) }
fun <T : Dependency> compileOnly(dependency: T, configure: Closure<*>) = compileOnly(dependency) { ConfigureUtil.configure(configure, this) }
fun runtimeOnly(dependencyNotation: Any): Dependency?
fun runtimeOnly(dependencyNotation: String, configure: ExternalModuleDependency.() -> Unit): ExternalModuleDependency
fun <T : Dependency> runtimeOnly(dependency: T, configure: T.() -> Unit): T
fun runtimeOnly(dependencyNotation: String, configure: Closure<*>) = runtimeOnly(dependencyNotation) { ConfigureUtil.configure(configure, this) }
fun <T : Dependency> runtimeOnly(dependency: T, configure: Closure<*>) = runtimeOnly(dependency) { ConfigureUtil.configure(configure, this) }
fun kotlin(simpleModuleName: String): ExternalModuleDependency = kotlin(simpleModuleName, null)
fun kotlin(simpleModuleName: String, version: String?): ExternalModuleDependency
fun project(path: String, configuration: String? = null): ProjectDependency =
project(listOf("path", "configuration").zip(listOfNotNull(path, configuration)).toMap())
fun project(notation: Map<String, Any?>): ProjectDependency
}
interface HasKotlinDependencies {
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.gradle.plugin.sources.SourceSetConsistencyChecks
import org.jetbrains.kotlin.gradle.util.checkBytecodeContains
import org.jetbrains.kotlin.gradle.util.isWindows
import org.jetbrains.kotlin.gradle.util.modify
import org.jetbrains.kotlin.gradle.util.testResolveAllConfigurations
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.HostManager
import org.junit.Assert
@@ -1171,6 +1172,36 @@ class NewMultiplatformIT : BaseGradleIT() {
}
}
@Test
fun testDependenciesDsl() = with(transformProjectWithPluginsDsl("newMppDependenciesDsl", GradleVersionRequired.AtLeast("4.10"))) {
val originalBuildscriptContent = gradleBuildScript("app").readText()
fun testDependencies() = testResolveAllConfigurations("app") {
assertContains(">> :app:testNonTransitiveStringNotationApi --> junit-4.12.jar")
assertEquals(1, (Regex.escape(">> :app:testNonTransitiveStringNotationApi") + " .*").toRegex().findAll(output).count())
assertContains(">> :app:testNonTransitiveDependencyNotationApi --> kotlin-reflect-${defaultBuildOptions().kotlinVersion}.jar")
assertEquals(1, (Regex.escape(">> :app:testNonTransitiveStringNotationApi") + " .*").toRegex().findAll(output).count())
assertContains(">> :app:testExplicitKotlinVersionApi --> kotlin-reflect-1.3.0.jar")
assertContains(">> :app:testExplicitKotlinVersionImplementation --> kotlin-reflect-1.2.71.jar")
assertContains(">> :app:testExplicitKotlinVersionCompileOnly --> kotlin-reflect-1.2.70.jar")
assertContains(">> :app:testExplicitKotlinVersionRuntimeOnly --> kotlin-reflect-1.2.60.jar")
assertContains(">> :app:testProjectWithConfigurationApi --> output.txt")
}
testDependencies()
// Then run with Gradle Kotlin DSL; the build script needs only one correction to be a valid GK DSL script:
gradleBuildScript("app").run {
modify { originalBuildscriptContent.replace(": ", " = ") }
renameTo(projectDir.resolve("app/build.gradle.kts"))
}
testDependencies()
}
@Test
fun testUnusedSourceSetsReport() = with(Project("sample-lib", gradleVersion, "new-mpp-lib-and-app")) {
setupWorkingDir()
@@ -19,7 +19,14 @@ fun BaseGradleIT.Project.testResolveAllConfigurations(
) = with(testCase) {
setupWorkingDir()
gradleBuildScript(subproject).appendText("\n" + generateResolveAllConfigurationsTask(excludePredicate))
gradleBuildScript(subproject).run {
val taskCode = when (extension) {
"gradle" -> generateResolveAllConfigurationsTask(excludePredicate)
"kts" -> generateResolveAllConfigurationsTaskKts(excludePredicate)
else -> error("Unexpected build script extension $extension")
}
appendText("\n" + taskCode)
}
build(RESOLVE_ALL_CONFIGURATIONS_TASK_NAME) {
assertSuccessful()
@@ -52,4 +59,30 @@ private fun generateResolveAllConfigurationsTask(exclude: String) =
}
}
}
""".trimIndent()
private fun generateResolveAllConfigurationsTaskKts(exclude: String) =
"""
tasks.create("$RESOLVE_ALL_CONFIGURATIONS_TASK_NAME") {
doFirst {
project.configurations
.filter { it.isCanBeResolved }
.filterNot { $exclude }
.forEach { configuration ->
val path = (configuration as org.gradle.api.internal.artifacts.configurations.ConfigurationInternal).path
try {
println("Resolving ${'$'}path")
configuration.files.forEach { println(">> ${'$'}path --> ${'$'}{it.name}") }
println("OK, resolved ${'$'}path\n")
} catch (e: Throwable) {
var ex = e as Throwable?
while (ex != null) {
println(ex.message)
ex = ex.cause
}
println("$UNRESOLVED_MARKER ${'$'}{configuration.name}\n")
}
}
}
}
""".trimIndent()
@@ -31,18 +31,18 @@ kotlin {
sourceSets {
val commonMain by getting {
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib-common")
api(kotlin("stdlib-common"))
}
}
jvm.compilations["main"].defaultSourceSet {
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib")
api(kotlin("stdlib"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4")
}
}
js.compilations["main"].defaultSourceSet {
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib-js")
api(kotlin("stdlib-js"))
implementation("org.jetbrains.kotlinx:kotlinx-html-js:0.6.11")
}
}
@@ -0,0 +1,39 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
}
repositories {
mavenLocal()
jcenter()
}
kotlin {
jvm("jvm6")
js { }
sourceSets {
create("testNonTransitiveStringNotation") {
dependencies {
api("junit:junit:4.12") { setTransitive(false) }
}
}
create("testNonTransitiveDependencyNotation") {
dependencies {
api(kotlin("reflect")) { setTransitive(false) }
}
}
create("testExplicitKotlinVersion") {
dependencies {
api(kotlin("reflect", "1.3.0"))
implementation(kotlin("reflect", "1.2.71"))
compileOnly(kotlin("reflect", "1.2.70"))
runtimeOnly(kotlin("reflect", "1.2.60"))
}
}
create("testProjectWithConfiguration") {
dependencies {
api(project(path: ":lib", configuration: "outputConfiguration"))
}
}
}
}
@@ -0,0 +1,16 @@
plugins {
id 'java'
}
repositories {
mavenLocal()
jcenter()
}
configurations {
outputConfiguration
}
artifacts {
outputConfiguration file('output.txt')
}
@@ -0,0 +1,8 @@
pluginManagement {
repositories {
mavenLocal()
gradlePluginPortal()
}
}
include ':lib', ':app'
@@ -0,0 +1,80 @@
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ExternalModuleDependency
import org.gradle.api.artifacts.ProjectDependency
import org.jetbrains.kotlin.gradle.plugin.HasKotlinDependencies
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
class DefaultKotlinDependencyHandler(
val parent: HasKotlinDependencies,
val project: Project
) : KotlinDependencyHandler {
override fun api(dependencyNotation: Any): Dependency? =
addDependencyByAnyNotation(parent.apiConfigurationName, dependencyNotation)
override fun api(dependencyNotation: String, configure: ExternalModuleDependency.() -> Unit): ExternalModuleDependency =
addDependencyByStringNotation(parent.apiConfigurationName, dependencyNotation, configure)
override fun <T : Dependency> api(dependency: T, configure: T.() -> Unit): T =
addDependency(parent.apiConfigurationName, dependency, configure)
override fun implementation(dependencyNotation: Any): Dependency? =
addDependencyByAnyNotation(parent.implementationConfigurationName, dependencyNotation)
override fun implementation(dependencyNotation: String, configure: ExternalModuleDependency.() -> Unit): ExternalModuleDependency =
addDependencyByStringNotation(parent.implementationConfigurationName, dependencyNotation, configure)
override fun <T : Dependency> implementation(dependency: T, configure: T.() -> Unit): T =
addDependency(parent.implementationConfigurationName, dependency, configure)
override fun compileOnly(dependencyNotation: Any): Dependency? =
addDependencyByAnyNotation(parent.compileOnlyConfigurationName, dependencyNotation)
override fun compileOnly(dependencyNotation: String, configure: ExternalModuleDependency.() -> Unit): ExternalModuleDependency =
addDependencyByStringNotation(parent.compileOnlyConfigurationName, dependencyNotation, configure)
override fun <T : Dependency> compileOnly(dependency: T, configure: T.() -> Unit): T =
addDependency(parent.compileOnlyConfigurationName, dependency, configure)
override fun runtimeOnly(dependencyNotation: Any): Dependency? =
addDependencyByAnyNotation(parent.runtimeOnlyConfigurationName, dependencyNotation)
override fun runtimeOnly(dependencyNotation: String, configure: ExternalModuleDependency.() -> Unit): ExternalModuleDependency =
addDependencyByStringNotation(parent.runtimeOnlyConfigurationName, dependencyNotation, configure)
override fun <T : Dependency> runtimeOnly(dependency: T, configure: T.() -> Unit): T =
addDependency(parent.runtimeOnlyConfigurationName, dependency, configure)
override fun kotlin(simpleModuleName: String, version: String?): ExternalModuleDependency =
project.dependencies.create(
"org.jetbrains.kotlin:kotlin-$simpleModuleName" + version?.let { ":$it" }.orEmpty()
) as ExternalModuleDependency
override fun project(notation: Map<String, Any?>): ProjectDependency =
project.dependencies.project(notation) as ProjectDependency
private fun addDependencyByAnyNotation(
configurationName: String,
dependencyNotation: Any
): Dependency? =
project.dependencies.add(configurationName, dependencyNotation)
private fun addDependencyByStringNotation(
configurationName: String,
dependencyNotation: Any,
configure: ExternalModuleDependency.() -> Unit = { }
): ExternalModuleDependency =
addDependency(configurationName, project.dependencies.create(dependencyNotation) as ExternalModuleDependency, configure)
private fun <T : Dependency> addDependency(
configurationName: String,
dependency: T,
configure: T.() -> Unit
): T =
dependency.also {
configure(it)
project.dependencies.add(configurationName, it)
}
}
@@ -1,4 +1,4 @@
/*
/*
* 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.
*/
@@ -38,23 +38,6 @@ internal fun KotlinCompilation<*>.composeName(prefix: String? = null, suffix: St
internal val KotlinCompilation<*>.defaultSourceSetName: 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 : KotlinCommonOptions>(
target: KotlinTarget,
override val compilationName: String