diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/HasKotlinDependencies.kt b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/HasKotlinDependencies.kt index 1a98edd8fe4..b9f62e93513 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/HasKotlinDependencies.kt +++ b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/HasKotlinDependencies.kt @@ -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 api(dependency: T, configure: T.() -> Unit): T + fun api(dependencyNotation: String, configure: Closure<*>) = api(dependencyNotation) { ConfigureUtil.configure(configure, this) } + fun 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 implementation(dependency: T, configure: T.() -> Unit): T + fun implementation(dependencyNotation: String, configure: Closure<*>) = implementation(dependencyNotation) { ConfigureUtil.configure(configure, this) } + fun 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 compileOnly(dependency: T, configure: T.() -> Unit): T + fun compileOnly(dependencyNotation: String, configure: Closure<*>) = compileOnly(dependencyNotation) { ConfigureUtil.configure(configure, this) } + fun 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 runtimeOnly(dependency: T, configure: T.() -> Unit): T + fun runtimeOnly(dependencyNotation: String, configure: Closure<*>) = runtimeOnly(dependencyNotation) { ConfigureUtil.configure(configure, this) } + fun 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): ProjectDependency } interface HasKotlinDependencies { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt index 65649696f11..08766d7fa75 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt @@ -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() diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/util/testResolveAllConfigurations.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/util/testResolveAllConfigurations.kt index 50332536d8f..4028da96473 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/util/testResolveAllConfigurations.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/util/testResolveAllConfigurations.kt @@ -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() \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-lib-and-app/sample-lib-gradle-kotlin-dsl/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-lib-and-app/sample-lib-gradle-kotlin-dsl/build.gradle.kts index 6e5a246819e..3215c96d7b4 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-lib-and-app/sample-lib-gradle-kotlin-dsl/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-lib-and-app/sample-lib-gradle-kotlin-dsl/build.gradle.kts @@ -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") } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/newMppDependenciesDsl/app/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/newMppDependenciesDsl/app/build.gradle new file mode 100644 index 00000000000..c0f0d2c20af --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/newMppDependenciesDsl/app/build.gradle @@ -0,0 +1,39 @@ +plugins { + id("org.jetbrains.kotlin.multiplatform").version("") +} + +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")) + } + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/newMppDependenciesDsl/lib/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/newMppDependenciesDsl/lib/build.gradle new file mode 100644 index 00000000000..ed771e07eb2 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/newMppDependenciesDsl/lib/build.gradle @@ -0,0 +1,16 @@ +plugins { + id 'java' +} + +repositories { + mavenLocal() + jcenter() +} + +configurations { + outputConfiguration +} + +artifacts { + outputConfiguration file('output.txt') +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/newMppDependenciesDsl/settings.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/newMppDependenciesDsl/settings.gradle new file mode 100644 index 00000000000..d0a0e31da1c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/newMppDependenciesDsl/settings.gradle @@ -0,0 +1,8 @@ +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + } +} + +include ':lib', ':app' \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/DefaultKotlinDependencyHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/DefaultKotlinDependencyHandler.kt new file mode 100644 index 00000000000..bb4eedfca09 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/DefaultKotlinDependencyHandler.kt @@ -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 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 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 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 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): 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 addDependency( + configurationName: String, + dependency: T, + configure: T.() -> Unit + ): T = + dependency.also { + configure(it) + project.dependencies.add(configurationName, it) + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt index 565c9ac1bbe..380a57f24c6 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt @@ -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( target: KotlinTarget, override val compilationName: String