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:
Sergey Igushkin
2021-10-20 17:23:30 +04:00
committed by Space
parent f0e703e1ee
commit a474e8a00b
12 changed files with 179 additions and 58 deletions
@@ -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
} }
} }
} }
@@ -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)
}
}
} }
@@ -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(
@@ -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)
} }
} }
@@ -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)
@@ -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)
} }
} }
@@ -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))
}
} }
} }
} }
@@ -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)
} }
} }
} }
@@ -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)
} }
} }
} }
@@ -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))
@@ -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) {
@@ -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())