KT-49189: fix project(...) dependencies on MPP from pure-Java consumers
* Don't set the localToProject attribute anymore, as it prevents proper disambiguation if not set properly on all configurations, while all the intentionally-public configurations should already have the same value. * Disambiguate o.j.k.platform.type Android vs JVM preferring JVM, as Android should have other attributes which make pure-Android consumers match the Android MPP variants. * With Gradle 7.0+, set the attribute `org.gradle.api.attributes.java.TargetJvmEnvironment` on the JVM & Android elements configurations, which helps pure-Android consumers to match the Android, not JVM variants. This fixes KT-30961 for Gradle 7.0+ and AGP 7.0+ Issues: KT-49189, KT-30961
This commit is contained in:
+16
-2
@@ -29,14 +29,28 @@ enum class KotlinPlatformType: Named, Serializable {
|
|||||||
|
|
||||||
class DisambiguationRule : AttributeDisambiguationRule<KotlinPlatformType> {
|
class DisambiguationRule : AttributeDisambiguationRule<KotlinPlatformType> {
|
||||||
override fun execute(details: MultipleCandidatesDetails<KotlinPlatformType?>) = with(details) {
|
override fun execute(details: MultipleCandidatesDetails<KotlinPlatformType?>) = with(details) {
|
||||||
if (candidateValues == setOf(androidJvm, jvm))
|
if (consumerValue in candidateValues) {
|
||||||
closestMatch(androidJvm)
|
closestMatch(checkNotNull(consumerValue))
|
||||||
|
return@with
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the consumer doesn't request anything specific and matches both JVM and Android,
|
||||||
|
* then assume that it's an ordinary pure-Java consumer. If it's a pure-Android consumer, it will have
|
||||||
|
* other means of disambiguation that will take precedence
|
||||||
|
* (the buildType attribute, the target JVM environment = android)
|
||||||
|
*/
|
||||||
|
if (consumerValue == null && androidJvm in candidateValues && jvm in candidateValues) {
|
||||||
|
closestMatch(jvm)
|
||||||
|
return@with
|
||||||
|
}
|
||||||
|
|
||||||
if (common in candidateValues && jvm !in candidateValues && androidJvm !in candidateValues) {
|
if (common in candidateValues && jvm !in candidateValues && androidJvm !in candidateValues) {
|
||||||
// then the consumer requests common or requests no platform-specific artifacts,
|
// then the consumer requests common or requests no platform-specific artifacts,
|
||||||
// so common is the best match, KT-26834; apply this rule only when no JVM variant is available,
|
// so common is the best match, KT-26834; apply this rule only when no JVM variant is available,
|
||||||
// as doing otherwise would conflict with Gradle java's disambiguation rules and lead to KT-32239
|
// as doing otherwise would conflict with Gradle java's disambiguation rules and lead to KT-32239
|
||||||
closestMatch(common)
|
closestMatch(common)
|
||||||
|
return@with
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+129
-36
@@ -8,29 +8,63 @@ package org.jetbrains.kotlin.gradle
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.gradle.util.AGPVersion
|
import org.jetbrains.kotlin.gradle.util.AGPVersion
|
||||||
import org.jetbrains.kotlin.gradle.util.checkedReplace
|
import org.jetbrains.kotlin.gradle.util.checkedReplace
|
||||||
import org.jetbrains.kotlin.gradle.util.isWindows
|
|
||||||
import org.jetbrains.kotlin.gradle.util.modify
|
import org.jetbrains.kotlin.gradle.util.modify
|
||||||
import org.jetbrains.kotlin.test.util.KtTestUtil
|
import org.jetbrains.kotlin.test.util.KtTestUtil
|
||||||
import org.junit.Assume
|
import org.junit.Assume
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.junit.rules.ErrorCollector
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.junit.runners.Parameterized
|
import org.junit.runners.Parameterized
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
|
||||||
import java.lang.Boolean as RefBoolean
|
import java.lang.Boolean as RefBoolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It is important to run both pre-7.0 and post-7.0 tests, as Gradle 7.0 + AGP 7 introduces a new attribute to distinguish JVM vs Android
|
||||||
|
*/
|
||||||
|
class PureAndroidAndJavaConsumeMppLibPreGradle7IT : PureAndroidAndJavaConsumeMppLibIT() {
|
||||||
|
override val agpVersion: AGPVersion
|
||||||
|
get() = AGPVersion.v4_2_0
|
||||||
|
|
||||||
|
override val gradleVersion: GradleVersionRequired
|
||||||
|
get() = GradleVersionRequired.Exact("6.9")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It is important to run both pre-7.0 and post-7.0 tests, as Gradle 7.0 + AGP 7 introduces a new attribute to distinguish JVM vs Android
|
||||||
|
*/
|
||||||
|
class PureAndroidAndJavaConsumeMppLibGradle7PlusIT : PureAndroidAndJavaConsumeMppLibIT() {
|
||||||
|
override val agpVersion: AGPVersion
|
||||||
|
get() = AGPVersion.v7_0_0
|
||||||
|
|
||||||
|
override val gradleVersion: GradleVersionRequired
|
||||||
|
get() = GradleVersionRequired.AtLeast("7.0")
|
||||||
|
}
|
||||||
|
|
||||||
@RunWith(Parameterized::class)
|
@RunWith(Parameterized::class)
|
||||||
class PureAndroidAndJavaConsumeMppLibIT : BaseGradleIT() {
|
abstract class PureAndroidAndJavaConsumeMppLibIT : BaseGradleIT() {
|
||||||
|
abstract val agpVersion: AGPVersion
|
||||||
|
abstract val gradleVersion: GradleVersionRequired
|
||||||
|
|
||||||
|
@field:Rule
|
||||||
|
@JvmField
|
||||||
|
var collector: ErrorCollector = ErrorCollector()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Parameterized.Parameters(name = "useFlavors: {0}, isAndroidDebugOnly: {1}")
|
@Parameterized.Parameters(name = "useFlavors: {0}, isAndroidPublishDebugOnly: {1}, isPublishedLibrary: {2}")
|
||||||
fun testCases(): List<Array<Boolean>> =
|
fun testCases(): List<Array<Boolean>> =
|
||||||
listOf( /* useFlavors, isAndroidDebugOnly */
|
listOf(
|
||||||
arrayOf(false, false),
|
/* useFlavors, isAndroidPublishDebugOnly, isPublishedLibrary */
|
||||||
arrayOf(false, true),
|
arrayOf(false, false, false),
|
||||||
arrayOf(true, false),
|
arrayOf(false, true, false),
|
||||||
arrayOf(true, true)
|
arrayOf(true, false, false),
|
||||||
|
arrayOf(true, true, false),
|
||||||
|
arrayOf(false, false, true),
|
||||||
|
arrayOf(false, true, true),
|
||||||
|
arrayOf(true, false, true),
|
||||||
|
arrayOf(true, true, true),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +72,7 @@ class PureAndroidAndJavaConsumeMppLibIT : BaseGradleIT() {
|
|||||||
lateinit var useFlavorsParameter: RefBoolean
|
lateinit var useFlavorsParameter: RefBoolean
|
||||||
|
|
||||||
@Parameterized.Parameter(1)
|
@Parameterized.Parameter(1)
|
||||||
lateinit var isAndroidDebugOnlyParameter: RefBoolean
|
lateinit var isAndroidPublishDebugOnlyParameter: RefBoolean
|
||||||
|
|
||||||
lateinit var buildOptions: BuildOptions
|
lateinit var buildOptions: BuildOptions
|
||||||
|
|
||||||
@@ -50,28 +84,30 @@ class PureAndroidAndJavaConsumeMppLibIT : BaseGradleIT() {
|
|||||||
buildOptions = defaultBuildOptions().copy(
|
buildOptions = defaultBuildOptions().copy(
|
||||||
javaHome = jdk11Home,
|
javaHome = jdk11Home,
|
||||||
androidHome = KtTestUtil.findAndroidSdk(),
|
androidHome = KtTestUtil.findAndroidSdk(),
|
||||||
androidGradlePluginVersion = AGPVersion.v7_0_0
|
androidGradlePluginVersion = agpVersion
|
||||||
)
|
)
|
||||||
buildOptions.androidHome?.let { acceptAndroidSdkLicenses(it) }
|
buildOptions.androidHome?.let { acceptAndroidSdkLicenses(it) }
|
||||||
|
|
||||||
super.setUp()
|
super.setUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Parameterized.Parameter(2)
|
||||||
|
lateinit var isPublishedLibraryParameter: RefBoolean
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test() {
|
fun test() {
|
||||||
val gradleVersionRequirement = GradleVersionRequired.AtLeast("7.0")
|
val isAndroidPublishDebugOnly = isAndroidPublishDebugOnlyParameter.booleanValue()
|
||||||
|
|
||||||
val isAndroidDebugOnly = isAndroidDebugOnlyParameter.booleanValue()
|
|
||||||
val useFlavors = useFlavorsParameter.booleanValue()
|
val useFlavors = useFlavorsParameter.booleanValue()
|
||||||
|
val isPublishedLibrary = isPublishedLibraryParameter.booleanValue()
|
||||||
|
|
||||||
val publishedProject = Project("new-mpp-android", gradleVersionRequirement).apply {
|
val dependencyProject = Project("new-mpp-android", gradleVersion).apply {
|
||||||
projectDir.deleteRecursively()
|
projectDir.deleteRecursively()
|
||||||
setupWorkingDir()
|
setupWorkingDir()
|
||||||
// Don't need custom attributes here
|
// Don't need custom attributes here
|
||||||
gradleBuildScript("lib").modify { text ->
|
gradleBuildScript("lib").modify { text ->
|
||||||
text.lines().filterNot { it.trimStart().startsWith("attribute(") }.joinToString("\n")
|
text.lines().filterNot { it.trimStart().startsWith("attribute(") }.joinToString("\n")
|
||||||
.let {
|
.let {
|
||||||
if (isAndroidDebugOnly) it.checkedReplace(
|
if (isAndroidPublishDebugOnly) it.checkedReplace(
|
||||||
"publishAllLibraryVariants()",
|
"publishAllLibraryVariants()",
|
||||||
"publishLibraryVariants(\"${if (useFlavors) "flavor1Debug" else "debug"}\")"
|
"publishLibraryVariants(\"${if (useFlavors) "flavor1Debug" else "debug"}\")"
|
||||||
) else it
|
) else it
|
||||||
@@ -87,18 +123,52 @@ class PureAndroidAndJavaConsumeMppLibIT : BaseGradleIT() {
|
|||||||
}
|
}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
} else it
|
} else it
|
||||||
|
}.let {
|
||||||
|
// Simulate the behavior with user-defined consumable configuration added with no proper attributes:
|
||||||
|
it + "\n" + """
|
||||||
|
configurations.create("legacyConfiguration") {
|
||||||
|
def bundlingAttribute = Attribute.of("org.gradle.dependency.bundling", String)
|
||||||
|
attributes.attribute(bundlingAttribute, "external")
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publishedProject.build(":lib:publish", options = buildOptions) {
|
if (isPublishedLibrary) {
|
||||||
assertSuccessful()
|
dependencyProject.build(":lib:publish", options = buildOptions) {
|
||||||
|
assertSuccessful()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val repoDir = publishedProject.projectDir.resolve("lib/build/repo")
|
|
||||||
|
|
||||||
val consumerProject = Project("AndroidProject", gradleVersionRequirement).apply {
|
val repositoryLinesIfNeeded = if (isPublishedLibrary) """
|
||||||
|
repositories {
|
||||||
|
maven { setUrl("${dependencyProject.projectDir.resolve("lib/build/repo").toURI()}") }
|
||||||
|
}
|
||||||
|
""".trimIndent() else ""
|
||||||
|
|
||||||
|
val dependencyNotation =
|
||||||
|
if (isPublishedLibrary)
|
||||||
|
""""com.example:lib:1.0""""
|
||||||
|
else "project(\":${dependencyProject.projectName}:lib\")"
|
||||||
|
|
||||||
|
val variantNamePublishedSuffix = if (isPublishedLibrary) "-published" else ""
|
||||||
|
|
||||||
|
val dependencyInsightModuleName =
|
||||||
|
if (isPublishedLibrary)
|
||||||
|
"com.example:lib"
|
||||||
|
else ":${dependencyProject.projectName}:lib"
|
||||||
|
|
||||||
|
val consumerProject = Project("AndroidProject", gradleVersion).apply {
|
||||||
projectDir.deleteRecursively()
|
projectDir.deleteRecursively()
|
||||||
|
if (!isPublishedLibrary) {
|
||||||
|
embedProject(dependencyProject)
|
||||||
|
gradleSettingsScript().appendText(
|
||||||
|
"\ninclude(\":${dependencyProject.projectName}:lib\")"
|
||||||
|
)
|
||||||
|
}
|
||||||
setupWorkingDir()
|
setupWorkingDir()
|
||||||
|
|
||||||
gradleBuildScript("Lib").apply {
|
gradleBuildScript("Lib").apply {
|
||||||
writeText(
|
writeText(
|
||||||
// Remove the Kotlin plugin from the consumer project to check how pure-AGP Kotlin-less consumers resolve the dependency
|
// Remove the Kotlin plugin from the consumer project to check how pure-AGP Kotlin-less consumers resolve the dependency
|
||||||
@@ -112,35 +182,45 @@ class PureAndroidAndJavaConsumeMppLibIT : BaseGradleIT() {
|
|||||||
create("staging") { initWith(getByName("debug")) }
|
create("staging") { initWith(getByName("debug")) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
repositories {
|
$repositoryLinesIfNeeded
|
||||||
maven { setUrl("${repoDir.absolutePath}") }
|
|
||||||
}
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.example:lib:1.0")
|
implementation($dependencyNotation)
|
||||||
}
|
}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val variantForReleaseAndStaging =
|
val variantForReleaseAndStaging = if (isAndroidPublishDebugOnly && isPublishedLibrary)
|
||||||
if (isAndroidDebugOnly) "debugApiElements-published" else "releaseApiElements-published"
|
"debugApiElements$variantNamePublishedSuffix"
|
||||||
|
else "releaseApiElements$variantNamePublishedSuffix"
|
||||||
|
|
||||||
fun nameWithFlavorIfNeeded(name: String) = if (useFlavors) "flavor1${name.capitalize()}" else name
|
fun nameWithFlavorIfNeeded(name: String) = if (useFlavors) "flavor1${name.capitalize()}" else name
|
||||||
|
|
||||||
val configurationToExpectedVariant = listOf(
|
val configurationToExpectedVariant = listOf(
|
||||||
nameWithFlavorIfNeeded("debugCompileClasspath") to nameWithFlavorIfNeeded("debugApiElements-published"),
|
nameWithFlavorIfNeeded("debugCompileClasspath") to nameWithFlavorIfNeeded("debugApiElements$variantNamePublishedSuffix"),
|
||||||
nameWithFlavorIfNeeded("releaseCompileClasspath") to nameWithFlavorIfNeeded(variantForReleaseAndStaging),
|
nameWithFlavorIfNeeded("releaseCompileClasspath") to nameWithFlavorIfNeeded(variantForReleaseAndStaging),
|
||||||
nameWithFlavorIfNeeded("stagingCompileClasspath") to nameWithFlavorIfNeeded(variantForReleaseAndStaging)
|
nameWithFlavorIfNeeded("stagingCompileClasspath") to
|
||||||
|
if (isPublishedLibrary)
|
||||||
|
nameWithFlavorIfNeeded(variantForReleaseAndStaging)
|
||||||
|
// NB: unlike published library, both the release and debug variants provide the build type attribute
|
||||||
|
// and therefore are not compatible with the "staging" consumer. So it can only use the JVM variant
|
||||||
|
else "jvmLibApiElements"
|
||||||
)
|
)
|
||||||
configurationToExpectedVariant.forEach { (configuration, expected) ->
|
configurationToExpectedVariant.forEach { (configuration, expected) ->
|
||||||
consumerProject.build(
|
consumerProject.build(
|
||||||
":Lib:dependencyInsight",
|
":Lib:dependencyInsight",
|
||||||
"--configuration", configuration,
|
"--configuration", configuration,
|
||||||
"--dependency", "com.example:lib",
|
"--dependency", dependencyInsightModuleName,
|
||||||
options = buildOptions
|
options = buildOptions
|
||||||
) {
|
) {
|
||||||
assertSuccessful()
|
assertSuccessful()
|
||||||
assertContains("variant \"$expected\" [")
|
if (project.testGradleVersionBelow("7.0") && !isPublishedLibrary) {
|
||||||
|
/* TODO: the issue KT-30961 is only fixed for Gradle 7.0+ and AGP 7+. Older versions still reproduce the issue;
|
||||||
|
* This test asserts the existing incorrect behavior for older Gradle versions in order to detect unintentional changes
|
||||||
|
*/
|
||||||
|
assertVariantInDependencyInsight("jvmLibApiElements")
|
||||||
|
} else
|
||||||
|
assertVariantInDependencyInsight(expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,11 +232,9 @@ class PureAndroidAndJavaConsumeMppLibIT : BaseGradleIT() {
|
|||||||
plugins {
|
plugins {
|
||||||
java
|
java
|
||||||
}
|
}
|
||||||
repositories {
|
$repositoryLinesIfNeeded
|
||||||
maven { setUrl("${repoDir.absolutePath}") }
|
|
||||||
}
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.example:lib:1.0")
|
implementation($dependencyNotation)
|
||||||
}
|
}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
@@ -164,12 +242,27 @@ class PureAndroidAndJavaConsumeMppLibIT : BaseGradleIT() {
|
|||||||
build(
|
build(
|
||||||
":pure-java:dependencyInsight",
|
":pure-java:dependencyInsight",
|
||||||
"--configuration", "compileClasspath",
|
"--configuration", "compileClasspath",
|
||||||
"--dependency", "com.example:lib",
|
"--dependency", dependencyInsightModuleName,
|
||||||
options = buildOptions
|
options = buildOptions
|
||||||
) {
|
) {
|
||||||
assertSuccessful()
|
assertSuccessful()
|
||||||
assertContains("variant \"jvmLibApiElements-published\" [")
|
assertVariantInDependencyInsight("jvmLibApiElements$variantNamePublishedSuffix")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun CompiledProject.assertVariantInDependencyInsight(variantName: String) {
|
||||||
|
try {
|
||||||
|
assertContains("variant \"$variantName\" [")
|
||||||
|
} catch (originalError: AssertionError) {
|
||||||
|
val matchedVariants = Regex("variant \"(.*?)\" \\[").findAll(output).toList()
|
||||||
|
val failure = AssertionError(
|
||||||
|
"Expected variant $variantName. " + if (matchedVariants.isNotEmpty()) "Matched instead: " + matchedVariants
|
||||||
|
.joinToString { it.groupValues[1] } else "No match.",
|
||||||
|
originalError
|
||||||
|
)
|
||||||
|
this.output
|
||||||
|
collector.addError(failure)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+1
-2
@@ -88,8 +88,7 @@ class VariantAwareDependenciesIT : BaseGradleIT() {
|
|||||||
val innerProject = Project("simpleProject").apply {
|
val innerProject = Project("simpleProject").apply {
|
||||||
setupWorkingDir(false)
|
setupWorkingDir(false)
|
||||||
gradleBuildScript().modify {
|
gradleBuildScript().modify {
|
||||||
it.replace("apply plugin: \"kotlin\"", "")
|
it.checkedReplace("id \"org.jetbrains.kotlin.jvm\"", "")
|
||||||
.replace("\"org.jetbrains.kotlin:kotlin-stdlib\"", "\"org.jetbrains.kotlin:kotlin-stdlib:\$kotlin_version\"")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gradleBuildScript().appendText(
|
gradleBuildScript().appendText(
|
||||||
|
|||||||
+1
-2
@@ -12,6 +12,7 @@ import com.android.build.gradle.api.BaseVariant
|
|||||||
import com.android.build.gradle.api.SourceKind
|
import com.android.build.gradle.api.SourceKind
|
||||||
import org.gradle.api.*
|
import org.gradle.api.*
|
||||||
import org.gradle.api.artifacts.repositories.ArtifactRepository
|
import org.gradle.api.artifacts.repositories.ArtifactRepository
|
||||||
|
import org.gradle.api.attributes.Bundling
|
||||||
import org.gradle.api.attributes.Category
|
import org.gradle.api.attributes.Category
|
||||||
import org.gradle.api.attributes.Usage
|
import org.gradle.api.attributes.Usage
|
||||||
import org.gradle.api.file.ConfigurableFileTree
|
import org.gradle.api.file.ConfigurableFileTree
|
||||||
@@ -613,14 +614,12 @@ internal abstract class AbstractKotlinPlugin(
|
|||||||
project.configurations.getByName(kotlinTarget.apiElementsConfigurationName).run {
|
project.configurations.getByName(kotlinTarget.apiElementsConfigurationName).run {
|
||||||
attributes.attribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerApiUsage(kotlinTarget))
|
attributes.attribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerApiUsage(kotlinTarget))
|
||||||
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
|
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
|
||||||
setupAsPublicConfigurationIfSupported(kotlinTarget)
|
|
||||||
usesPlatformOf(kotlinTarget)
|
usesPlatformOf(kotlinTarget)
|
||||||
}
|
}
|
||||||
|
|
||||||
project.configurations.getByName(kotlinTarget.runtimeElementsConfigurationName).run {
|
project.configurations.getByName(kotlinTarget.runtimeElementsConfigurationName).run {
|
||||||
attributes.attribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerRuntimeUsage(kotlinTarget))
|
attributes.attribute(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerRuntimeUsage(kotlinTarget))
|
||||||
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
|
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
|
||||||
setupAsPublicConfigurationIfSupported(kotlinTarget)
|
|
||||||
usesPlatformOf(kotlinTarget)
|
usesPlatformOf(kotlinTarget)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-2
@@ -6,12 +6,14 @@
|
|||||||
package org.jetbrains.kotlin.gradle.plugin
|
package org.jetbrains.kotlin.gradle.plugin
|
||||||
|
|
||||||
import org.gradle.api.DefaultTask
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.Named
|
||||||
import org.gradle.api.NamedDomainObjectContainer
|
import org.gradle.api.NamedDomainObjectContainer
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.artifacts.Configuration
|
import org.gradle.api.artifacts.Configuration
|
||||||
import org.gradle.api.artifacts.Dependency
|
import org.gradle.api.artifacts.Dependency
|
||||||
import org.gradle.api.artifacts.PublishArtifact
|
import org.gradle.api.artifacts.PublishArtifact
|
||||||
import org.gradle.api.artifacts.type.ArtifactTypeDefinition
|
import org.gradle.api.artifacts.type.ArtifactTypeDefinition
|
||||||
|
import org.gradle.api.attributes.Attribute
|
||||||
import org.gradle.api.attributes.Category
|
import org.gradle.api.attributes.Category
|
||||||
import org.gradle.api.attributes.Usage
|
import org.gradle.api.attributes.Usage
|
||||||
import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE
|
import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE
|
||||||
@@ -38,6 +40,7 @@ import org.jetbrains.kotlin.gradle.tasks.registerTask
|
|||||||
import org.jetbrains.kotlin.gradle.tasks.withType
|
import org.jetbrains.kotlin.gradle.tasks.withType
|
||||||
import org.jetbrains.kotlin.gradle.utils.COMPILE
|
import org.jetbrains.kotlin.gradle.utils.COMPILE
|
||||||
import org.jetbrains.kotlin.gradle.utils.RUNTIME
|
import org.jetbrains.kotlin.gradle.utils.RUNTIME
|
||||||
|
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
|
||||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
import kotlin.reflect.KMutableProperty1
|
import kotlin.reflect.KMutableProperty1
|
||||||
@@ -182,7 +185,6 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
|
|||||||
runtimeConfiguration?.let { extendsFrom(it) }
|
runtimeConfiguration?.let { extendsFrom(it) }
|
||||||
}
|
}
|
||||||
usesPlatformOf(target)
|
usesPlatformOf(target)
|
||||||
setupAsPublicConfigurationIfSupported(target)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mainCompilation is KotlinCompilationToRunnableFiles<*>) {
|
if (mainCompilation is KotlinCompilationToRunnableFiles<*>) {
|
||||||
@@ -199,7 +201,6 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
|
|||||||
extendsFrom(runtimeOnlyConfiguration)
|
extendsFrom(runtimeOnlyConfiguration)
|
||||||
runtimeConfiguration?.let { extendsFrom(it) }
|
runtimeConfiguration?.let { extendsFrom(it) }
|
||||||
usesPlatformOf(target)
|
usesPlatformOf(target)
|
||||||
setupAsPublicConfigurationIfSupported(target)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,6 +501,12 @@ internal fun Project.categoryByName(categoryName: String): Category =
|
|||||||
fun Configuration.usesPlatformOf(target: KotlinTarget): Configuration {
|
fun Configuration.usesPlatformOf(target: KotlinTarget): Configuration {
|
||||||
attributes.attribute(KotlinPlatformType.attribute, target.platformType)
|
attributes.attribute(KotlinPlatformType.attribute, target.platformType)
|
||||||
|
|
||||||
|
when (target.platformType) {
|
||||||
|
KotlinPlatformType.jvm -> setJavaTargetEnvironmentAttributeIfSupported(target.project, "standard-jvm")
|
||||||
|
KotlinPlatformType.androidJvm -> setJavaTargetEnvironmentAttributeIfSupported(target.project, "android")
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
|
||||||
if (target is KotlinJsTarget) {
|
if (target is KotlinJsTarget) {
|
||||||
attributes.attribute(KotlinJsCompilerAttribute.jsCompilerAttribute, KotlinJsCompilerAttribute.legacy)
|
attributes.attribute(KotlinJsCompilerAttribute.jsCompilerAttribute, KotlinJsCompilerAttribute.legacy)
|
||||||
}
|
}
|
||||||
@@ -515,6 +522,19 @@ fun Configuration.usesPlatformOf(target: KotlinTarget): Configuration {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Configuration.setJavaTargetEnvironmentAttributeIfSupported(project: Project, value: String) {
|
||||||
|
if (isGradleVersionAtLeast(7, 0)) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val attributeClass = Class.forName("org.gradle.api.attributes.java.TargetJvmEnvironment") as Class<out Named>
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val attributeKey = attributeClass.getField("TARGET_JVM_ENVIRONMENT_ATTRIBUTE").get(null) as Attribute<Named>
|
||||||
|
|
||||||
|
val attributeValue = project.objects.named(attributeClass, value)
|
||||||
|
attributes.attribute(attributeKey, attributeValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal val Project.commonKotlinPluginClasspath get() = configurations.getByName(PLUGIN_CLASSPATH_CONFIGURATION_NAME)
|
internal val Project.commonKotlinPluginClasspath get() = configurations.getByName(PLUGIN_CLASSPATH_CONFIGURATION_NAME)
|
||||||
internal val KotlinCompilation<*>.pluginConfigurationName
|
internal val KotlinCompilation<*>.pluginConfigurationName
|
||||||
get() = lowerCamelCaseName(PLUGIN_CLASSPATH_CONFIGURATION_NAME, target.disambiguationClassifier, compilationName)
|
get() = lowerCamelCaseName(PLUGIN_CLASSPATH_CONFIGURATION_NAME, target.disambiguationClassifier, compilationName)
|
||||||
+5
-8
@@ -9,11 +9,15 @@ import org.gradle.api.artifacts.Configuration
|
|||||||
import org.gradle.api.attributes.*
|
import org.gradle.api.attributes.*
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This attribute is registered in the schema only for the case if some 3rd-party consumer or published
|
||||||
|
* module still has it or relies on it. The Kotlin Gradle plugin should not add this attribute to any variant.
|
||||||
|
* TODO: examine and remove
|
||||||
|
*/
|
||||||
object ProjectLocalConfigurations {
|
object ProjectLocalConfigurations {
|
||||||
val ATTRIBUTE: Attribute<String> = Attribute.of("org.jetbrains.kotlin.localToProject", String::class.java)
|
val ATTRIBUTE: Attribute<String> = Attribute.of("org.jetbrains.kotlin.localToProject", String::class.java)
|
||||||
|
|
||||||
const val PUBLIC_VALUE = "public"
|
const val PUBLIC_VALUE = "public"
|
||||||
const val LOCAL_TO_PROJECT_PREFIX = "local to "
|
|
||||||
|
|
||||||
fun setupAttributesMatchingStrategy(attributesSchema: AttributesSchema) = with(attributesSchema) {
|
fun setupAttributesMatchingStrategy(attributesSchema: AttributesSchema) = with(attributesSchema) {
|
||||||
attribute(ATTRIBUTE) {
|
attribute(ATTRIBUTE) {
|
||||||
@@ -43,12 +47,5 @@ internal fun Configuration.setupAsLocalTargetSpecificConfigurationIfSupported(ta
|
|||||||
// `api/RuntimeElements` with the KotlinPlatformType
|
// `api/RuntimeElements` with the KotlinPlatformType
|
||||||
if ((target !is KotlinWithJavaTarget<*> || target.platformType != KotlinPlatformType.common)) {
|
if ((target !is KotlinWithJavaTarget<*> || target.platformType != KotlinPlatformType.common)) {
|
||||||
usesPlatformOf(target)
|
usesPlatformOf(target)
|
||||||
attributes.attribute(ProjectLocalConfigurations.ATTRIBUTE, ProjectLocalConfigurations.LOCAL_TO_PROJECT_PREFIX + target.project.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Configuration.setupAsPublicConfigurationIfSupported(target: KotlinTarget) {
|
|
||||||
if ((target !is KotlinWithJavaTarget<*> || target.platformType != KotlinPlatformType.common)) {
|
|
||||||
attributes.attribute(ProjectLocalConfigurations.ATTRIBUTE, ProjectLocalConfigurations.PUBLIC_VALUE)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -14,6 +14,7 @@ import org.gradle.api.Project
|
|||||||
import org.gradle.api.artifacts.ArtifactCollection
|
import org.gradle.api.artifacts.ArtifactCollection
|
||||||
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
||||||
import org.gradle.api.attributes.Attribute
|
import org.gradle.api.attributes.Attribute
|
||||||
|
import org.gradle.api.attributes.Category
|
||||||
import org.gradle.api.file.FileCollection
|
import org.gradle.api.file.FileCollection
|
||||||
import org.gradle.api.specs.Spec
|
import org.gradle.api.specs.Spec
|
||||||
import org.gradle.api.tasks.PathSensitivity
|
import org.gradle.api.tasks.PathSensitivity
|
||||||
@@ -143,7 +144,10 @@ class Android25ProjectHandler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
listOf(apiElementsConfigurationName, runtimeElementsConfigurationName).forEach { outputConfigurationName ->
|
listOf(apiElementsConfigurationName, runtimeElementsConfigurationName).forEach { outputConfigurationName ->
|
||||||
project.configurations.findByName(outputConfigurationName)?.usesPlatformOf(compilation.target)
|
project.configurations.findByName(outputConfigurationName)?.let { configuration ->
|
||||||
|
configuration.usesPlatformOf(compilation.target)
|
||||||
|
configuration.attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.categoryByName(Category.LIBRARY))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -119,7 +119,6 @@ open class KotlinJsTargetConfigurator :
|
|||||||
isCanBeConsumed = true
|
isCanBeConsumed = true
|
||||||
attributes.attribute<Usage>(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerApiUsage(target))
|
attributes.attribute<Usage>(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerApiUsage(target))
|
||||||
attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.common)
|
attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.common)
|
||||||
setupAsPublicConfigurationIfSupported(target)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-1
@@ -161,7 +161,6 @@ open class KotlinJsIrTargetConfigurator() :
|
|||||||
isCanBeConsumed = true
|
isCanBeConsumed = true
|
||||||
attributes.attribute<Usage>(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerApiUsage(target))
|
attributes.attribute<Usage>(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerApiUsage(target))
|
||||||
attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.common)
|
attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.common)
|
||||||
setupAsPublicConfigurationIfSupported(target)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -450,7 +450,6 @@ class KotlinMetadataTargetConfigurator :
|
|||||||
project.configurations.create(COMMON_MAIN_ELEMENTS_CONFIGURATION_NAME).apply {
|
project.configurations.create(COMMON_MAIN_ELEMENTS_CONFIGURATION_NAME).apply {
|
||||||
isCanBeConsumed = true
|
isCanBeConsumed = true
|
||||||
isCanBeResolved = false
|
isCanBeResolved = false
|
||||||
setupAsPublicConfigurationIfSupported(target)
|
|
||||||
usesPlatformOf(target)
|
usesPlatformOf(target)
|
||||||
|
|
||||||
attributes.attribute(USAGE_ATTRIBUTE, KotlinUsages.producerApiUsage(target))
|
attributes.attribute(USAGE_ATTRIBUTE, KotlinUsages.producerApiUsage(target))
|
||||||
|
|||||||
-1
@@ -82,7 +82,6 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget> : AbstractKotl
|
|||||||
linkTask: TaskProvider<KotlinNativeLink>
|
linkTask: TaskProvider<KotlinNativeLink>
|
||||||
) {
|
) {
|
||||||
fun <T : Task> Configuration.configureConfiguration(taskProvider: TaskProvider<T>) {
|
fun <T : Task> Configuration.configureConfiguration(taskProvider: TaskProvider<T>) {
|
||||||
setupAsPublicConfigurationIfSupported(binary.target)
|
|
||||||
project.afterEvaluate {
|
project.afterEvaluate {
|
||||||
val task = taskProvider.get()
|
val task = taskProvider.get()
|
||||||
val artifactFile = when (task) {
|
val artifactFile = when (task) {
|
||||||
|
|||||||
-1
@@ -62,7 +62,6 @@ internal fun Project.locateOrCreateCInteropApiElementsConfiguration(target: Kotl
|
|||||||
return configurations.create(configurationName).apply {
|
return configurations.create(configurationName).apply {
|
||||||
isCanBeResolved = false
|
isCanBeResolved = false
|
||||||
isCanBeConsumed = true
|
isCanBeConsumed = true
|
||||||
setupAsPublicConfigurationIfSupported(target)
|
|
||||||
|
|
||||||
usesPlatformOf(target)
|
usesPlatformOf(target)
|
||||||
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, cinteropKlibLibraryElements())
|
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, cinteropKlibLibraryElements())
|
||||||
|
|||||||
Reference in New Issue
Block a user