KT-38221: Stdlib by default; KT-40225 Single dependency on kotlin-test
* Add a DSL property `kotlin.coreLibrariesVersion` that is used as the default version for all org.jetbrains.kotlin dependencies instead of the plugin version used previously – the pluging version is the default now. * Support omitting the `kotlin-stdlib-*` dependencies in all kinds of projects. For the JVM source sets, choose the stdlib module by checking the jvmTarget in the compilations – if all compilations target 8+ then use `kotlin-stdlib-jdk8`. * Support adding a single dependency on the fake module `kotlin-test-multiplatform` that is later expanded into the appropriate dependencies on `kotlin-test-*` modules. For JVM source sets, detect the test framework by inspecting the test tasks. * (minor) Fix _executionSource not updated in KotlinJvmTestRun * (minor) Lazily query `kotlinUsageContext`'s dependency-related properties to avoid freezing the configurations Issue #KT-38221 Fixed Issue #KT-40225 Fixed
This commit is contained in:
+1
-1
@@ -28,7 +28,7 @@ abstract class BaseGradleIT {
|
||||
|
||||
protected var workingDir = File(".")
|
||||
|
||||
protected open fun defaultBuildOptions(): BuildOptions = BuildOptions(withDaemon = true)
|
||||
internal open fun defaultBuildOptions(): BuildOptions = BuildOptions(withDaemon = true)
|
||||
|
||||
open val defaultGradleVersion: GradleVersionRequired
|
||||
get() = GradleVersionRequired.None
|
||||
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle
|
||||
|
||||
import org.jetbrains.kotlin.gradle.internals.KOTLIN_TEST_MULTIPLATFORM_MODULE_NAME
|
||||
import org.jetbrains.kotlin.gradle.util.AGPVersion
|
||||
import org.jetbrains.kotlin.gradle.util.modify
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class KotlinSpecificDependenciesIT : BaseGradleIT() {
|
||||
|
||||
override val defaultGradleVersion: GradleVersionRequired
|
||||
get() = GradleVersionRequired.FOR_MPP_SUPPORT
|
||||
|
||||
override fun defaultBuildOptions(): BuildOptions =
|
||||
super.defaultBuildOptions().copy(androidGradlePluginVersion = AGPVersion.v3_6_0, androidHome = KotlinTestUtils.findAndroidSdk())
|
||||
|
||||
private var testBuildRunId = 0
|
||||
|
||||
private fun Project.prepare() { // call this when reusing a project after a test, too, in order to remove any added dependencies
|
||||
setupWorkingDir()
|
||||
gradleSettingsScript().takeIf { it.exists() }?.modify(::transformBuildScriptWithPluginsDsl)
|
||||
gradleBuildScript().modify {
|
||||
transformBuildScriptWithPluginsDsl(it).lines().filter { line ->
|
||||
"stdlib" !in line && "kotlin(\"test" !in line && "kotlin-test" !in line
|
||||
}.joinToString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
private fun jsProject() = Project("kotlin-js-plugin-project").apply {
|
||||
setupWorkingDir()
|
||||
gradleBuildScript().modify { it.lines().filter { "html" !in it }.joinToString("\n") }
|
||||
projectFile("Main.kt").modify { "fun f() = listOf(1, 2, 3).joinToString()" }
|
||||
prepare()
|
||||
}
|
||||
|
||||
private fun androidProject() = Project("AndroidLibraryKotlinProject").apply { prepare() }
|
||||
|
||||
private fun mppProject() = Project("jvm-and-js-hmpp").apply {
|
||||
setupWorkingDir()
|
||||
prepare()
|
||||
}
|
||||
|
||||
private fun jvmProject() = Project("simpleProject").apply {
|
||||
prepare()
|
||||
gradleBuildScript().modify { it.lines().filter { "testng" !in it }.joinToString("\n") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStdlibByDefault() {
|
||||
listOf( // projects and tasks:
|
||||
androidProject() to listOf("compileDebugKotlin"),
|
||||
jvmProject() to listOf("compileKotlin"),
|
||||
jsProject() to listOf("compileKotlinJs"),
|
||||
mppProject() to listOf(
|
||||
"compileKotlinJvm",
|
||||
"compileKotlinJs",
|
||||
"compileCommonMainKotlinMetadata",
|
||||
"compileJvmAndJsMainKotlinMetadata"
|
||||
)
|
||||
).forEach { (project, tasks) ->
|
||||
with(project) {
|
||||
prepare()
|
||||
tasks.forEach { task ->
|
||||
project.checkTaskCompileClasspath(task, listOf("kotlin-stdlib" /*any of them*/))
|
||||
}
|
||||
projectDir.resolve("gradle.properties").appendText(
|
||||
"\nkotlin.stdlib.default.dependency=false"
|
||||
)
|
||||
tasks.forEach { task ->
|
||||
project.checkTaskCompileClasspath(task, listOf(), checkModulesNotInClasspath = listOf("kotlin-stdlib" /*any of them*/))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStdlibBasedOnJdk() = with(jvmProject()) {
|
||||
prepare()
|
||||
gradleBuildScript().modify { "$it\nkotlin.target.compilations[\"main\"].kotlinOptions { jvmTarget = \"1.6\" }" }
|
||||
val version = defaultBuildOptions().kotlinVersion
|
||||
checkTaskCompileClasspath(
|
||||
"compileKotlin",
|
||||
listOf("kotlin-stdlib-$version"),
|
||||
listOf("kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8")
|
||||
)
|
||||
gradleBuildScript().modify { "$it\nkotlin.target.compilations[\"main\"].kotlinOptions { jvmTarget = \"11\" }" }
|
||||
checkTaskCompileClasspath(
|
||||
"compileKotlin",
|
||||
listOf("kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8"),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOverrideStdlib() = with(jvmProject().apply { prepare() }) {
|
||||
gradleBuildScript().appendText(
|
||||
"\n" + """
|
||||
kotlin.target.compilations["main"].kotlinOptions.jvmTarget = "1.8"
|
||||
dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib") }
|
||||
""".trimIndent()
|
||||
)
|
||||
// Check that the explicit stdlib overrides the plugin's choice of stdlib-jdk8
|
||||
checkTaskCompileClasspath(
|
||||
"compileKotlin",
|
||||
listOf("kotlin-stdlib-${defaultBuildOptions().kotlinVersion}"),
|
||||
listOf("kotlin-stdlib-jdk8")
|
||||
)
|
||||
}
|
||||
|
||||
private val kotlinTestMultiplatformDependency = "org.jetbrains.kotlin:$KOTLIN_TEST_MULTIPLATFORM_MODULE_NAME"
|
||||
|
||||
@Test
|
||||
fun testKotlinTestSingleDependency() {
|
||||
data class TestCase(
|
||||
val project: Project,
|
||||
val configurationsToAddDependency: List<String>,
|
||||
val classpathElementsExpectedByTask: Map<String, List<String>>,
|
||||
val filesExpectedByConfiguration: Map<String, List<String>> = emptyMap()
|
||||
)
|
||||
|
||||
listOf(
|
||||
TestCase(
|
||||
androidProject(),
|
||||
listOf("testImplementation"),
|
||||
mapOf(
|
||||
"compileDebugUnitTestKotlin" to listOf("kotlin-test-junit"),
|
||||
"compileReleaseUnitTestKotlin" to listOf("kotlin-test-junit")
|
||||
)
|
||||
),
|
||||
TestCase(
|
||||
androidProject(),
|
||||
listOf("androidTestImplementation"),
|
||||
mapOf("compileDebugAndroidTestKotlin" to listOf("kotlin-test-junit"))
|
||||
),
|
||||
TestCase(jvmProject(), listOf("testImplementation"), mapOf("compileTestKotlin" to listOf("kotlin-test-testng"))),
|
||||
TestCase(jsProject(), listOf("testImplementation"), mapOf("compileTestKotlinJs" to listOf("kotlin-test-js"))),
|
||||
TestCase(
|
||||
mppProject(),
|
||||
listOf("commonTestImplementation"),
|
||||
mapOf(
|
||||
"compileTestKotlinJvm" to listOf("kotlin-test-junit"),
|
||||
"compileTestKotlinJs" to listOf("kotlin-test-js")
|
||||
),
|
||||
mapOf(
|
||||
"commonTestImplementationDependenciesMetadata" to listOf("kotlin-test-common", "kotlin-test-annotations-common"),
|
||||
"commonTestApiDependenciesMetadata" to listOf("!kotlin-test-common", "!kotlin-test-annotations-common"),
|
||||
)
|
||||
),
|
||||
TestCase(
|
||||
mppProject(),
|
||||
listOf("jvmAndJsTestApi", "jvmAndJsTestCompileOnly"), // add to the intermediate source set, and to two scopes
|
||||
mapOf(
|
||||
"compileTestKotlinJvm" to listOf("kotlin-test-junit"),
|
||||
"compileTestKotlinJs" to listOf("kotlin-test-js")
|
||||
),
|
||||
mapOf(
|
||||
"commonTestApiDependenciesMetadata" to listOf("!kotlin-test-common"),
|
||||
"commonTestCompileOnlyDependenciesMetadata" to listOf("!kotlin-test-common"),
|
||||
"jvmAndJsTestApiDependenciesMetadata" to listOf("kotlin-test-common"),
|
||||
"jvmAndJsTestCompileOnlyDependenciesMetadata" to listOf("kotlin-test-common"),
|
||||
"jvmAndJsTestImplementationDependenciesMetadata" to listOf("!kotlin-test-common"),
|
||||
)
|
||||
)
|
||||
).forEach { testCase ->
|
||||
with(testCase) {
|
||||
project.prepare()
|
||||
project.gradleBuildScript().appendText(
|
||||
configurationsToAddDependency.joinToString("\n", "\n") { configuration ->
|
||||
"\ndependencies { \"$configuration\"(\"$kotlinTestMultiplatformDependency\") }"
|
||||
}
|
||||
)
|
||||
classpathElementsExpectedByTask.forEach { (task, expected) ->
|
||||
val (notInClasspath, inClasspath) = expected.partition { it.startsWith("!") }
|
||||
project.checkTaskCompileClasspath(task, inClasspath, notInClasspath.map { it.removePrefix("!") })
|
||||
}
|
||||
filesExpectedByConfiguration.forEach { (configuration, expected) ->
|
||||
val (notInItems, inItems) = expected.partition { it.startsWith("!") }
|
||||
project.checkConfigurationContent(configuration, subproject = null, inItems, notInItems.map { it.removePrefix("!") })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFrameworkSelection() {
|
||||
data class TestCase(
|
||||
val project: Project,
|
||||
val testTaskName: String,
|
||||
val compileTaskName: String,
|
||||
val configurationToAddDependency: String
|
||||
)
|
||||
|
||||
val frameworks = listOf("useJUnit()" to "junit", "useTestNG()" to "testng", "useJUnitPlatform()" to "junit5")
|
||||
listOf(
|
||||
TestCase(jvmProject(), "test", "compileTestKotlin", "testImplementation"),
|
||||
TestCase(mppProject(), "jvmTest", "compileTestKotlinJvm", "commonTestImplementation"),
|
||||
TestCase(mppProject(), "jvmTest", "compileTestKotlinJvm", "jvmAndJsTestImplementation")
|
||||
).forEach { (project, testTaskName, compileTaskName, configuration) ->
|
||||
project.prepare()
|
||||
project.gradleBuildScript().appendText("""${'\n'}dependencies { "$configuration"("$kotlinTestMultiplatformDependency") }""")
|
||||
|
||||
frameworks.forEach { (setup, frameworkName) ->
|
||||
with(project) {
|
||||
gradleBuildScript().appendText("\n(tasks.getByName(\"$testTaskName\") as Test).$setup")
|
||||
val expectedModule = "kotlin-test-$frameworkName-"
|
||||
checkTaskCompileClasspath(
|
||||
compileTaskName,
|
||||
listOf(expectedModule),
|
||||
frameworks.map { "kotlin-test-" + it.second + "-" } - expectedModule
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemoveKotlinTestDependency() = with(jvmProject().apply { prepare() }) {
|
||||
gradleBuildScript().appendText(
|
||||
"\n" + """
|
||||
dependencies { testImplementation("$kotlinTestMultiplatformDependency") }
|
||||
configurations.getByName("testImplementation").dependencies.removeAll { it.name == "$KOTLIN_TEST_MULTIPLATFORM_MODULE_NAME" }
|
||||
""".trimIndent()
|
||||
)
|
||||
checkTaskCompileClasspath("compileTestKotlin", checkModulesNotInClasspath = listOf("kotlin-test"))
|
||||
|
||||
// Add it back after removal:
|
||||
gradleBuildScript().appendText(
|
||||
"\n" + """
|
||||
dependencies { testImplementation("$kotlinTestMultiplatformDependency") }
|
||||
"""
|
||||
)
|
||||
checkTaskCompileClasspath("compileTestKotlin", checkModulesInClasspath = listOf("kotlin-test"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCoreLibraryVersionsDsl() = with(jvmProject().apply { prepare() }) {
|
||||
val customVersion = "1.3.70"
|
||||
gradleBuildScript().appendText(
|
||||
"\n" + """
|
||||
kotlin.coreLibrariesVersion = "$customVersion"
|
||||
dependencies {
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test-multiplatform")
|
||||
}
|
||||
test.useJUnit()
|
||||
""".trimIndent()
|
||||
)
|
||||
checkTaskCompileClasspath(
|
||||
"compileTestKotlin",
|
||||
listOf("kotlin-stdlib-", "kotlin-reflect-", "kotlin-test-junit-").map { it + customVersion }
|
||||
)
|
||||
}
|
||||
|
||||
private fun Project.checkConfigurationContent(
|
||||
configurationName: String,
|
||||
subproject: String? = null,
|
||||
checkModulesInResolutionResult: List<String> = emptyList(),
|
||||
checkModulesNotInResolutionResult: List<String> = emptyList()
|
||||
) {
|
||||
val expression = """configurations["$configurationName"].toList()"""
|
||||
checkPrintedItems(subproject, expression, checkModulesInResolutionResult, checkModulesNotInResolutionResult)
|
||||
}
|
||||
|
||||
private fun Project.checkTaskCompileClasspath(
|
||||
taskPath: String,
|
||||
checkModulesInClasspath: List<String> = emptyList(),
|
||||
checkModulesNotInClasspath: List<String> = emptyList()
|
||||
) {
|
||||
val subproject = taskPath.substringBeforeLast(":").takeIf { it.isNotEmpty() && it != taskPath }
|
||||
val taskName = taskPath.removePrefix(subproject.orEmpty())
|
||||
val expression = """(tasks.getByName("$taskName") as AbstractCompile).classpath.toList()"""
|
||||
checkPrintedItems(subproject, expression, checkModulesInClasspath, checkModulesNotInClasspath)
|
||||
}
|
||||
|
||||
private fun Project.checkPrintedItems(
|
||||
subproject: String?,
|
||||
itemsExpression: String,
|
||||
checkAnyItemsContains: List<String>,
|
||||
checkNoItemContains: List<String>
|
||||
) {
|
||||
setupWorkingDir()
|
||||
val printingTaskName = "printItems${testBuildRunId++}"
|
||||
gradleBuildScript(subproject).appendText(
|
||||
"""
|
||||
${'\n'}
|
||||
tasks.create("$printingTaskName") {
|
||||
doLast {
|
||||
println("###$printingTaskName" + $itemsExpression)
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
build("${subproject?.prependIndent(":").orEmpty()}:$printingTaskName") {
|
||||
assertSuccessful()
|
||||
val itemsLine = output.lines().single { "###$printingTaskName" in it }.substringAfter(printingTaskName)
|
||||
val items = itemsLine.removeSurrounding("[", "]").split(", ").toSet()
|
||||
checkAnyItemsContains.forEach { pattern -> assertTrue { items.any { pattern in it } } }
|
||||
checkNoItemContains.forEach { pattern -> assertFalse { items.any { pattern in it } } }
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -2107,13 +2107,13 @@ class NewMultiplatformIT : BaseGradleIT() {
|
||||
fun testDependencies() = testResolveAllConfigurations("app") {
|
||||
assertContains(">> :app:testNonTransitiveStringNotationApiDependenciesMetadata --> junit-4.12.jar")
|
||||
assertEquals(
|
||||
1,
|
||||
2, // the dependency above and stdlib
|
||||
(Regex.escape(">> :app:testNonTransitiveStringNotationApiDependenciesMetadata") + " .*").toRegex().findAll(output).count()
|
||||
)
|
||||
|
||||
assertContains(">> :app:testNonTransitiveDependencyNotationApiDependenciesMetadata --> kotlin-reflect-${defaultBuildOptions().kotlinVersion}.jar")
|
||||
assertEquals(
|
||||
1,
|
||||
2, // the dependency above and stdlib
|
||||
(Regex.escape(">> :app:testNonTransitiveStringNotationApiDependenciesMetadata") + " .*").toRegex().findAll(output).count()
|
||||
)
|
||||
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
plugins {
|
||||
kotlin("multiplatform").version("<pluginMarkerVersion>")
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
group = "com.example.thirdparty"
|
||||
version = "1.0"
|
||||
|
||||
kotlin {
|
||||
jvm()
|
||||
js()
|
||||
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-common"))
|
||||
}
|
||||
}
|
||||
val commonTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("test-common"))
|
||||
implementation(kotlin("test-annotations-common"))
|
||||
}
|
||||
}
|
||||
val jvmAndJsMain by creating {
|
||||
dependsOn(commonMain)
|
||||
}
|
||||
val jvmAndJsTest by creating {
|
||||
dependsOn(commonTest)
|
||||
}
|
||||
jvm().compilations["main"].defaultSourceSet {
|
||||
dependsOn(jvmAndJsMain)
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
}
|
||||
}
|
||||
jvm().compilations["test"].defaultSourceSet {
|
||||
dependsOn(jvmAndJsTest)
|
||||
dependencies {
|
||||
implementation(kotlin("test-junit"))
|
||||
}
|
||||
}
|
||||
js().compilations["main"].defaultSourceSet {
|
||||
dependsOn(jvmAndJsMain)
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-js"))
|
||||
}
|
||||
}
|
||||
js().compilations["test"].defaultSourceSet {
|
||||
dependsOn(jvmAndJsTest)
|
||||
dependencies {
|
||||
implementation(kotlin("test-js"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven("../repo")
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
kotlin.mpp.enableGranularSourceSetsMetadata=true
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "third-party-lib"
|
||||
|
||||
enableFeaturePreview("GRADLE_METADATA")
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.example.thirdparty
|
||||
|
||||
expect fun thirdPartyFun(): String
|
||||
|
||||
private fun useStdlibInCommonMain() = listOf(1, 2, 3).joinToString()
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.example.thirdparty
|
||||
|
||||
actual fun thirdPartyFun(): String = "thirdPartyFun @ js"
|
||||
|
||||
fun thirdPartyJsFun() { }
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package com.example.thirdparty
|
||||
|
||||
private fun useStdlibInJvmAndJsMain() = listOf(1, 2, 3).joinToString()
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.example.thirdparty
|
||||
|
||||
actual fun thirdPartyFun(): String = "thirdPartyFun @ jvm"
|
||||
|
||||
fun thirdPartyJvmFun() { }
|
||||
+2
@@ -47,6 +47,8 @@ open class KotlinProjectExtension : KotlinSourceSetContainer {
|
||||
val experimental: ExperimentalExtension
|
||||
get() = DslObject(this).extensions.getByType(ExperimentalExtension::class.java)
|
||||
|
||||
lateinit var coreLibrariesVersion: String
|
||||
|
||||
var explicitApi: ExplicitApiMode? = null
|
||||
|
||||
fun explicitApi() {
|
||||
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.internal
|
||||
|
||||
import com.android.build.gradle.api.TestVariant
|
||||
import com.android.build.gradle.api.UnitTestVariant
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.artifacts.Dependency
|
||||
import org.gradle.api.artifacts.ExternalDependency
|
||||
import org.gradle.api.tasks.testing.AbstractTestTask
|
||||
import org.gradle.api.tasks.testing.Test
|
||||
import org.gradle.api.tasks.testing.junit.JUnitOptions
|
||||
import org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions
|
||||
import org.gradle.api.tasks.testing.testng.TestNGOptions
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
|
||||
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
||||
import org.jetbrains.kotlin.gradle.execution.KotlinAggregateExecutionSource
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.CompilationSourceSetUtil
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
|
||||
import org.jetbrains.kotlin.gradle.targets.jvm.JvmCompilationsTestRunSource
|
||||
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_MODULE_GROUP
|
||||
import org.jetbrains.kotlin.gradle.tasks.locateTask
|
||||
import org.jetbrains.kotlin.gradle.testing.KotlinTaskTestRun
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
|
||||
internal fun customizeKotlinDependencies(project: Project) {
|
||||
configureStdlibDefaultDependency(project)
|
||||
configureKotlinTestDependencies(project)
|
||||
configureDefaultVersionsResolutionStrategy(project)
|
||||
}
|
||||
|
||||
private fun configureDefaultVersionsResolutionStrategy(project: Project) {
|
||||
project.configurations.all { configuration ->
|
||||
// Use the API introduced in Gradle 4.4 to modify the dependencies directly before they are resolved:
|
||||
configuration.withDependencies { dependencySet ->
|
||||
val coreLibrariesVersion = project.kotlinExtension.coreLibrariesVersion
|
||||
dependencySet.filterIsInstance<ExternalDependency>()
|
||||
.filter { it.group == KOTLIN_MODULE_GROUP && it.version.isNullOrEmpty() }
|
||||
.forEach { it.version { constraint -> constraint.require(coreLibrariesVersion) } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//region stdlib
|
||||
internal fun configureStdlibDefaultDependency(project: Project) {
|
||||
if (!PropertiesProvider(project).stdlibDefaultDependency)
|
||||
return
|
||||
|
||||
project.kotlinExtension.sourceSets.all { kotlinSourceSet ->
|
||||
val apiConfiguration = project.sourceSetDependencyConfigurationByScope(kotlinSourceSet, KotlinDependencyScope.API_SCOPE)
|
||||
|
||||
apiConfiguration.withDependencies { dependencies ->
|
||||
val sourceSetDependencyConfigurations =
|
||||
KotlinDependencyScope.values().map { project.sourceSetDependencyConfigurationByScope(kotlinSourceSet, it) }
|
||||
|
||||
val hierarchySourceSetsDependencyConfigurations = kotlinSourceSet.getSourceSetHierarchy().flatMap { hierarchySourceSet ->
|
||||
if (hierarchySourceSet === kotlinSourceSet) emptyList() else
|
||||
KotlinDependencyScope.values().map { scope ->
|
||||
project.sourceSetDependencyConfigurationByScope(hierarchySourceSet, scope)
|
||||
}
|
||||
}
|
||||
|
||||
val compilations = CompilationSourceSetUtil.compilationsBySourceSets(project).getValue(kotlinSourceSet)
|
||||
.filter { it.target !is KotlinMetadataTarget }
|
||||
val platformTypes = compilations.mapTo(mutableSetOf()) { it.platformType }
|
||||
|
||||
val sourceSetStdlibPlatformType = when {
|
||||
platformTypes.isEmpty() -> KotlinPlatformType.common
|
||||
setOf(KotlinPlatformType.jvm, KotlinPlatformType.androidJvm).containsAll(platformTypes) -> KotlinPlatformType.jvm
|
||||
platformTypes.size == 1 -> platformTypes.single()
|
||||
else -> KotlinPlatformType.common
|
||||
}
|
||||
|
||||
val isStdlibAddedByUser = sourceSetDependencyConfigurations
|
||||
.flatMap { it.allDependencies }
|
||||
.any { dependency -> dependency.group == KOTLIN_MODULE_GROUP && dependency.name in stdlibModules }
|
||||
|
||||
if (!isStdlibAddedByUser) {
|
||||
val stdlibModuleName = when (sourceSetStdlibPlatformType) {
|
||||
KotlinPlatformType.jvm, KotlinPlatformType.androidJvm -> jvmStdlibModuleForJvmCompilations(compilations)
|
||||
KotlinPlatformType.js -> "kotlin-stdlib-js"
|
||||
KotlinPlatformType.native -> null
|
||||
KotlinPlatformType.common -> // there's no platform compilation that the source set is default for
|
||||
"kotlin-stdlib-common"
|
||||
}
|
||||
|
||||
// If the exact same module is added in the source sets hierarchy, possibly even with a different scope, we don't add it
|
||||
val moduleAddedInHierarchy = hierarchySourceSetsDependencyConfigurations.any {
|
||||
it.allDependencies.any { dependency -> dependency.group == KOTLIN_MODULE_GROUP && dependency.name == stdlibModuleName }
|
||||
}
|
||||
|
||||
if (stdlibModuleName != null && !moduleAddedInHierarchy)
|
||||
dependencies.add(project.kotlinDependency(stdlibModuleName, project.kotlinExtension.coreLibrariesVersion))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val stdlibModules = setOf("kotlin-stdlib-common", "kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8", "kotlin-stdlib-js")
|
||||
|
||||
private fun jvmStdlibModuleForJvmCompilations(compilations: Iterable<KotlinCompilation<*>>): String {
|
||||
val jvmTargets = compilations.map { (it.kotlinOptions as KotlinJvmOptions).jvmTarget }
|
||||
return if ("1.6" in jvmTargets) "kotlin-stdlib" else "kotlin-stdlib-jdk8"
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region kotlin-test
|
||||
internal fun configureKotlinTestDependencies(project: Project) {
|
||||
fun isKotlinTestMultiplatformDependency(dependency: Dependency) =
|
||||
dependency.group == KOTLIN_MODULE_GROUP && dependency.name == KOTLIN_TEST_MULTIPLATFORM_MODULE_NAME
|
||||
|
||||
KotlinDependencyScope.values().forEach { scope ->
|
||||
val versionOrNullBySourceSet = mutableMapOf<KotlinSourceSet, String?>()
|
||||
|
||||
project.kotlinExtension.sourceSets.all { kotlinSourceSet ->
|
||||
val configuration = project.sourceSetDependencyConfigurationByScope(kotlinSourceSet, scope)
|
||||
var finalizingDependencies = false
|
||||
|
||||
configuration.dependencies.matching(::isKotlinTestMultiplatformDependency).apply {
|
||||
firstOrNull()?.let { versionOrNullBySourceSet[kotlinSourceSet] = it.version }
|
||||
whenObjectRemoved {
|
||||
if (!finalizingDependencies && !any())
|
||||
versionOrNullBySourceSet.remove(kotlinSourceSet)
|
||||
}
|
||||
whenObjectAdded { item ->
|
||||
versionOrNullBySourceSet[kotlinSourceSet] = item.version
|
||||
}
|
||||
}
|
||||
|
||||
configuration.withDependencies { dependencies ->
|
||||
val parentOrOwnVersions: List<String?> =
|
||||
kotlinSourceSet.getSourceSetHierarchy().filter(versionOrNullBySourceSet::contains).map(versionOrNullBySourceSet::get)
|
||||
|
||||
finalizingDependencies = true
|
||||
|
||||
parentOrOwnVersions.distinct().forEach { version -> // add dependencies with each version and let Gradle disambiguate them
|
||||
val dependenciesToAdd = kotlinTestDependenciesForSourceSet(project, kotlinSourceSet, version)
|
||||
dependenciesToAdd.filterIsInstance<ExternalDependency>().forEach {
|
||||
if (it.version == null) {
|
||||
it.version { constraint -> constraint.require(project.kotlinExtension.coreLibrariesVersion) }
|
||||
}
|
||||
}
|
||||
dependencies.addAll(dependenciesToAdd)
|
||||
dependencies.removeIf(::isKotlinTestMultiplatformDependency)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun kotlinTestDependenciesForSourceSet(project: Project, kotlinSourceSet: KotlinSourceSet, version: String?): List<Dependency> {
|
||||
val compilations = CompilationSourceSetUtil.compilationsBySourceSets(project).getValue(kotlinSourceSet)
|
||||
.filter { it.target !is KotlinMetadataTarget }
|
||||
|
||||
val platformTypes = compilations.mapTo(mutableSetOf()) { it.platformType }
|
||||
|
||||
return when {
|
||||
platformTypes.isEmpty() -> emptyList()
|
||||
setOf(KotlinPlatformType.jvm, KotlinPlatformType.androidJvm).containsAll(platformTypes) -> listOf(
|
||||
kotlinTestDependenciesForJvm(project, compilations, version)
|
||||
)
|
||||
platformTypes.singleOrNull() == KotlinPlatformType.js -> listOf(
|
||||
project.kotlinDependency("kotlin-test-js", version)
|
||||
)
|
||||
platformTypes.singleOrNull() == KotlinPlatformType.native -> emptyList()
|
||||
else -> listOf(
|
||||
project.kotlinDependency("kotlin-test-common", version),
|
||||
project.kotlinDependency("kotlin-test-annotations-common", version)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal const val KOTLIN_TEST_MULTIPLATFORM_MODULE_NAME = "kotlin-test-multiplatform"
|
||||
|
||||
private fun Project.kotlinDependency(moduleName: String, versionOrNull: String?) =
|
||||
project.dependencies.create("$KOTLIN_MODULE_GROUP:$moduleName${versionOrNull?.prependIndent(":").orEmpty()}")
|
||||
|
||||
private fun kotlinTestDependenciesForJvm(project: Project, compilations: Iterable<KotlinCompilation<*>>, version: String?): Dependency {
|
||||
val testTaskLists: List<List<Task>?> = compilations.map { compilation ->
|
||||
val target = compilation.target
|
||||
when {
|
||||
target is KotlinTargetWithTests<*, *> ->
|
||||
target.findTestRunsByCompilation(compilation)?.filterIsInstance<KotlinTaskTestRun<*, *>>()?.map { it.executionTask.get() }
|
||||
target is KotlinWithJavaTarget<*> ->
|
||||
if (compilation.name == KotlinCompilation.TEST_COMPILATION_NAME)
|
||||
project.locateTask<AbstractTestTask>(target.testTaskName)?.get()?.let(::listOf)
|
||||
else null
|
||||
compilation is KotlinJvmAndroidCompilation -> when (compilation.androidVariant) {
|
||||
is UnitTestVariant ->
|
||||
project.locateTask<AbstractTestTask>(lowerCamelCaseName("test", compilation.androidVariant.name))?.get()?.let(::listOf)
|
||||
is TestVariant -> (compilation.androidVariant as TestVariant).connectedInstrumentTest?.let(::listOf)
|
||||
else -> null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
if (null in testTaskLists) {
|
||||
return project.kotlinDependency("kotlin-test", version)
|
||||
}
|
||||
val testTasks = testTaskLists.flatMap { checkNotNull(it) }
|
||||
val frameworks = testTasks.mapTo(mutableSetOf()) { testTask ->
|
||||
when (testTask) {
|
||||
is Test -> testFrameworkOf(testTask)
|
||||
else -> // Android connected test tasks don't inherit from Test, but we use JUnit for them
|
||||
KotlinTestJvmFramework.junit
|
||||
}
|
||||
}
|
||||
return when {
|
||||
frameworks.size > 1 -> project.kotlinDependency("kotlin-test", version)
|
||||
else -> project.kotlinDependency("kotlin-test-${frameworks.single().name}", version)
|
||||
}
|
||||
}
|
||||
|
||||
private enum class KotlinTestJvmFramework {
|
||||
junit, testng, junit5
|
||||
}
|
||||
|
||||
private fun testFrameworkOf(testTask: Test): KotlinTestJvmFramework = when (testTask.options) {
|
||||
is JUnitOptions -> KotlinTestJvmFramework.junit
|
||||
is JUnitPlatformOptions -> KotlinTestJvmFramework.junit5
|
||||
is TestNGOptions -> KotlinTestJvmFramework.testng
|
||||
else -> // failed to detect, fallback to junit
|
||||
KotlinTestJvmFramework.junit
|
||||
}
|
||||
|
||||
private fun KotlinTargetWithTests<*, *>.findTestRunsByCompilation(byCompilation: KotlinCompilation<*>): List<KotlinTargetTestRun<*>>? {
|
||||
fun KotlinExecution.ExecutionSource.isProducedFromTheCompilation(): Boolean = when (this) {
|
||||
is CompilationExecutionSource<*> -> compilation == byCompilation
|
||||
is JvmCompilationsTestRunSource -> byCompilation in testCompilations
|
||||
is KotlinAggregateExecutionSource<*> -> this.executionSources.any { it.isProducedFromTheCompilation() }
|
||||
else -> false
|
||||
}
|
||||
return testRuns.filter { it.executionSource.isProducedFromTheCompilation() }.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
//endregion
|
||||
+7
-20
@@ -6,11 +6,8 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin
|
||||
|
||||
import com.android.build.gradle.*
|
||||
import com.android.build.gradle.api.AndroidSourceSet
|
||||
import com.android.build.gradle.api.BaseVariant
|
||||
import com.android.build.gradle.api.SourceKind
|
||||
import com.android.build.gradle.api.*
|
||||
import org.gradle.api.*
|
||||
import org.gradle.api.artifacts.ExternalDependency
|
||||
import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer
|
||||
import org.gradle.api.artifacts.maven.MavenResolver
|
||||
import org.gradle.api.attributes.Usage
|
||||
@@ -32,8 +29,9 @@ import org.gradle.api.tasks.compile.AbstractCompile
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
|
||||
import org.jetbrains.kotlin.gradle.dsl.*
|
||||
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
|
||||
import org.jetbrains.kotlin.gradle.internal.*
|
||||
import org.jetbrains.kotlin.gradle.internal.checkAndroidAnnotationProcessorDependencyUsage
|
||||
import org.jetbrains.kotlin.gradle.internal.customizeKotlinDependencies
|
||||
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
||||
import org.jetbrains.kotlin.gradle.model.builder.KotlinModelBuilder
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
@@ -412,7 +410,7 @@ internal abstract class AbstractKotlinPlugin(
|
||||
|
||||
rewriteMppDependenciesInPom(target)
|
||||
|
||||
configureProjectGlobalSettings(project, kotlinPluginVersion)
|
||||
configureProjectGlobalSettings(project)
|
||||
registry.register(KotlinModelBuilder(kotlinPluginVersion, null))
|
||||
|
||||
project.components.addAll(target.components)
|
||||
@@ -452,8 +450,8 @@ internal abstract class AbstractKotlinPlugin(
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun configureProjectGlobalSettings(project: Project, kotlinPluginVersion: String) {
|
||||
configureDefaultVersionsResolutionStrategy(project, kotlinPluginVersion)
|
||||
fun configureProjectGlobalSettings(project: Project) {
|
||||
customizeKotlinDependencies(project)
|
||||
configureClassInspectionForIC(project)
|
||||
project.setupGeneralKotlinExtensionParameters()
|
||||
}
|
||||
@@ -590,17 +588,6 @@ internal abstract class AbstractKotlinPlugin(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun configureDefaultVersionsResolutionStrategy(project: Project, kotlinPluginVersion: String) {
|
||||
project.configurations.all { configuration ->
|
||||
// Use the API introduced in Gradle 4.4 to modify the dependencies directly before they are resolved:
|
||||
configuration.withDependencies { dependencySet ->
|
||||
dependencySet.filterIsInstance<ExternalDependency>()
|
||||
.filter { it.group == "org.jetbrains.kotlin" && it.version.isNullOrEmpty() }
|
||||
.forEach { it.version { constraint -> constraint.require(kotlinPluginVersion) } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal open class KotlinPlugin(
|
||||
kotlinPluginVersion: String,
|
||||
registry: ToolingModelBuilderRegistry
|
||||
@@ -703,7 +690,7 @@ internal open class KotlinAndroidPlugin(
|
||||
|
||||
applyUserDefinedAttributes(androidTarget)
|
||||
|
||||
configureDefaultVersionsResolutionStrategy(project, kotlinPluginVersion)
|
||||
customizeKotlinDependencies(project)
|
||||
|
||||
registry.register(KotlinModelBuilder(kotlinPluginVersion, androidTarget))
|
||||
|
||||
|
||||
+2
@@ -78,6 +78,8 @@ abstract class KotlinBasePluginWrapper(
|
||||
kotlinGradleBuildServices.detectKotlinPluginLoadedInMultipleProjects(project, kotlinPluginVersion)
|
||||
|
||||
project.createKotlinExtension(projectExtensionClass).apply {
|
||||
coreLibrariesVersion = kotlinPluginVersion
|
||||
|
||||
fun kotlinSourceSetContainer(factory: NamedDomainObjectFactory<KotlinSourceSet>) =
|
||||
project.container(KotlinSourceSet::class.java, factory)
|
||||
|
||||
|
||||
+3
@@ -202,6 +202,9 @@ internal class PropertiesProvider private constructor(private val project: Proje
|
||||
val jsCompiler: KotlinJsCompilerType
|
||||
get() = property(jsCompilerProperty)?.let { KotlinJsCompilerType.byArgumentOrNull(it) } ?: KotlinJsCompilerType.LEGACY
|
||||
|
||||
val stdlibDefaultDependency: Boolean
|
||||
get() = booleanProperty("kotlin.stdlib.default.dependency") ?: true
|
||||
|
||||
private fun propertyWithDeprecatedVariant(propName: String, deprecatedPropName: String): String? {
|
||||
val deprecatedProperty = property(deprecatedPropName)
|
||||
if (deprecatedProperty != null) {
|
||||
|
||||
+2
-1
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.configureOrCreate
|
||||
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.internal.customizeKotlinDependencies
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin.Companion.sourceSetFreeCompilerArgsPropertyName
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
|
||||
@@ -84,7 +85,7 @@ class KotlinMultiplatformPlugin(
|
||||
}
|
||||
|
||||
setupDefaultPresets(project)
|
||||
configureDefaultVersionsResolutionStrategy(project, kotlinPluginVersion)
|
||||
customizeKotlinDependencies(project)
|
||||
configureSourceSets(project)
|
||||
|
||||
// set up metadata publishing
|
||||
|
||||
+5
-1
@@ -21,6 +21,7 @@ import org.gradle.api.component.SoftwareComponentFactory
|
||||
import org.gradle.api.internal.component.SoftwareComponentInternal
|
||||
import org.gradle.api.internal.component.UsageContext
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.gradle.util.ConfigureUtil
|
||||
import org.gradle.util.WrapUtil
|
||||
@@ -98,7 +99,10 @@ abstract class AbstractKotlinTarget(
|
||||
?: project.configurations.create(kotlinUsageContext.name).also { configuration ->
|
||||
configuration.isCanBeConsumed = false
|
||||
configuration.isCanBeResolved = false
|
||||
configuration.dependencies.addAll(kotlinUsageContext.dependencies)
|
||||
/** Add dependencies lazily to avoid freezing the content of the configuration held by [kotlinUsageContext] */
|
||||
configuration.withDependencies { dependencies ->
|
||||
dependencies.addAll(kotlinUsageContext.dependencies)
|
||||
}
|
||||
configuration.dependencyConstraints.addAll(kotlinUsageContext.dependencyConstraints)
|
||||
configuration.artifacts.addAll(kotlinUsageContext.artifacts)
|
||||
|
||||
|
||||
+2
-2
@@ -10,10 +10,10 @@ import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaBasePlugin
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJsProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
||||
import org.jetbrains.kotlin.gradle.internal.customizeKotlinDependencies
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.TEST_COMPILATION_NAME
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.configureDefaultVersionsResolutionStrategy
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsSingleTargetPreset
|
||||
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
|
||||
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrSingleTargetPreset
|
||||
@@ -36,7 +36,7 @@ open class KotlinJsPlugin(
|
||||
checkGradleCompatibility()
|
||||
|
||||
val kotlinExtension = project.kotlinExtension as KotlinJsProjectExtension
|
||||
configureDefaultVersionsResolutionStrategy(project, kotlinPluginVersion)
|
||||
customizeKotlinDependencies(project)
|
||||
|
||||
kotlinExtension.apply {
|
||||
irPreset = KotlinJsIrSingleTargetPreset(project, kotlinPluginVersion)
|
||||
|
||||
+1
@@ -64,6 +64,7 @@ open class KotlinJvmTestRun(testRunName: String, override val target: KotlinJvmT
|
||||
get() = _executionSource
|
||||
private set(value) {
|
||||
setTestTaskClasspathAndClassesDirs(value.classpath, value.testClassesDirs)
|
||||
_executionSource = value
|
||||
}
|
||||
|
||||
private fun setTestTaskClasspathAndClassesDirs(classpath: FileCollection, testClassesDirs: FileCollection) {
|
||||
|
||||
+2
@@ -26,3 +26,5 @@ const val NO_NATIVE_STDLIB_WARNING = NO_NATIVE_STDLIB_WARNING
|
||||
const val NO_NATIVE_STDLIB_PROPERTY_WARNING = NO_NATIVE_STDLIB_PROPERTY_WARNING
|
||||
|
||||
val KOTLIN_12X_MPP_DEPRECATION_WARNING = KOTLIN_12X_MPP_DEPRECATION_WARNING
|
||||
|
||||
const val KOTLIN_TEST_MULTIPLATFORM_MODULE_NAME = org.jetbrains.kotlin.gradle.internal.KOTLIN_TEST_MULTIPLATFORM_MODULE_NAME
|
||||
Reference in New Issue
Block a user