Fix user-specified attributes in Android targets (KT-27714)
Ensure that custom target and compilation attributes are copied to relevant configurations of Android targets as well, which did not happen because of Android variants (and missing simple apiElements, runtimeElements) and Android compilations being created late in the project configuration. Issue #KT-27714 Fixed
This commit is contained in:
+64
@@ -196,7 +196,71 @@ class KotlinAndroid32GradleIT : KotlinAndroid3GradleIT(androidGradlePluginVersio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCustomAttributesInAndroidTargets() = with(Project("new-mpp-android", GradleVersionRequired.AtLeast("4.7"))) {
|
||||||
|
// Test the fix for KT-27714
|
||||||
|
|
||||||
|
setupWorkingDir()
|
||||||
|
|
||||||
|
// Enable publishing for all Android variants:
|
||||||
|
gradleBuildScript("lib").appendText("\nkotlin.android('androidLib') { publishAllLibraryVariants() }")
|
||||||
|
|
||||||
|
val groupDir = "lib/build/repo/com/example/"
|
||||||
|
|
||||||
|
build("publish") {
|
||||||
|
assertSuccessful()
|
||||||
|
|
||||||
|
// Also check that custom user-specified attributes are written in all Android modules metadata:
|
||||||
|
assertFileContains(
|
||||||
|
groupDir + "lib-androidlib/1.0/lib-androidlib-1.0.module",
|
||||||
|
"\"com.example.target\": \"androidLib\"",
|
||||||
|
"\"com.example.compilation\": \"release\""
|
||||||
|
)
|
||||||
|
assertFileContains(
|
||||||
|
groupDir + "lib-androidlib-debug/1.0/lib-androidlib-debug-1.0.module",
|
||||||
|
"\"com.example.target\": \"androidLib\"",
|
||||||
|
"\"com.example.compilation\": \"debug\""
|
||||||
|
)
|
||||||
|
|
||||||
|
projectDir.resolve(groupDir).deleteRecursively()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the consumer side uses custom attributes specified in the target and compilations:
|
||||||
|
run {
|
||||||
|
val appBuildScriptBackup = gradleBuildScript("app").readText()
|
||||||
|
|
||||||
|
gradleBuildScript("app").appendText(
|
||||||
|
"\n" + """
|
||||||
|
kotlin.targets.androidApp.attributes.attribute(
|
||||||
|
Attribute.of("com.example.target", String),
|
||||||
|
"notAndroidLib"
|
||||||
|
)
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
build(":app:compileDebugKotlinAndroidApp") {
|
||||||
|
assertFailed() // dependency resolution should fail
|
||||||
|
assertContains("Required com.example.target 'notAndroidLib'")
|
||||||
|
}
|
||||||
|
|
||||||
|
gradleBuildScript("app").writeText(
|
||||||
|
appBuildScriptBackup + "\n" + """
|
||||||
|
kotlin.targets.androidApp.compilations.all {
|
||||||
|
attributes.attribute(
|
||||||
|
Attribute.of("com.example.compilation", String),
|
||||||
|
"notDebug"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
build(":app:compileDebugKotlinAndroidApp") {
|
||||||
|
assertFailed()
|
||||||
|
assertContains("Required com.example.compilation 'notDebug'")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+11
-1
@@ -66,7 +66,17 @@ kotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
targets {
|
targets {
|
||||||
fromPreset(presets.android, 'androidLib')
|
fromPreset(presets.android, 'androidLib') {
|
||||||
|
attributes {
|
||||||
|
attribute(Attribute.of("com.example.target", String), "androidLib")
|
||||||
|
}
|
||||||
|
compilations.all {
|
||||||
|
attributes {
|
||||||
|
attribute(Attribute.of("com.example.compilation", String), compilationName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fromPreset(presets.jvm, 'jvmLib')
|
fromPreset(presets.jvm, 'jvmLib')
|
||||||
fromPreset(presets.js, 'jsLib')
|
fromPreset(presets.js, 'jsLib')
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -2,6 +2,7 @@ package org.jetbrains.kotlin.gradle.plugin
|
|||||||
|
|
||||||
import com.android.build.gradle.BaseExtension
|
import com.android.build.gradle.BaseExtension
|
||||||
import com.android.build.gradle.BasePlugin
|
import com.android.build.gradle.BasePlugin
|
||||||
|
import com.android.build.gradle.api.BaseVariant
|
||||||
import com.android.builder.model.SourceProvider
|
import com.android.builder.model.SourceProvider
|
||||||
import groovy.lang.Closure
|
import groovy.lang.Closure
|
||||||
import org.gradle.api.*
|
import org.gradle.api.*
|
||||||
@@ -748,6 +749,8 @@ abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationT
|
|||||||
// in afterEvaluate, a user's build script might have already attached item handlers to the compilations container, and those
|
// in afterEvaluate, a user's build script might have already attached item handlers to the compilations container, and those
|
||||||
// handlers might break when fired on a compilation that is not yet properly configured (e.g. KT-29964):
|
// handlers might break when fired on a compilation that is not yet properly configured (e.g. KT-29964):
|
||||||
kotlinAndroidTarget.compilationFactory.create(variantName).let { compilation ->
|
kotlinAndroidTarget.compilationFactory.create(variantName).let { compilation ->
|
||||||
|
compilation.androidVariant = variant as BaseVariant
|
||||||
|
|
||||||
setUpDependencyResolution(variant, compilation)
|
setUpDependencyResolution(variant, compilation)
|
||||||
|
|
||||||
processVariant(variant, compilation, project, ext, plugin, kotlinOptions, kotlinConfigurationTools.kotlinTasksProvider)
|
processVariant(variant, compilation, project, ext, plugin, kotlinOptions, kotlinConfigurationTools.kotlinTasksProvider)
|
||||||
|
|||||||
+30
-21
@@ -78,8 +78,6 @@ class KotlinMultiplatformPlugin(
|
|||||||
configureDefaultVersionsResolutionStrategy(project, kotlinPluginVersion)
|
configureDefaultVersionsResolutionStrategy(project, kotlinPluginVersion)
|
||||||
configureSourceSets(project)
|
configureSourceSets(project)
|
||||||
|
|
||||||
setUpConfigurationAttributes(project)
|
|
||||||
|
|
||||||
// set up metadata publishing
|
// set up metadata publishing
|
||||||
targetsFromPreset.fromPreset(
|
targetsFromPreset.fromPreset(
|
||||||
KotlinMetadataTargetPreset(project, instantiator, fileResolver, kotlinPluginVersion),
|
KotlinMetadataTargetPreset(project, instantiator, fileResolver, kotlinPluginVersion),
|
||||||
@@ -87,6 +85,8 @@ class KotlinMultiplatformPlugin(
|
|||||||
)
|
)
|
||||||
configurePublishingWithMavenPublish(project)
|
configurePublishingWithMavenPublish(project)
|
||||||
|
|
||||||
|
setUpConfigurationAttributes(project)
|
||||||
|
|
||||||
// propagate compiler plugin options to the source set language settings
|
// propagate compiler plugin options to the source set language settings
|
||||||
setupCompilerPluginOptions(project)
|
setupCompilerPluginOptions(project)
|
||||||
|
|
||||||
@@ -226,40 +226,49 @@ class KotlinMultiplatformPlugin(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes attached to the targets and compilations need to be propagated to the relevant Gradle configurations:
|
||||||
|
* 1. Output configurations of each target need the corresponding compilation's attributes (and, indirectly, the target's attributes)
|
||||||
|
* 2. Resolvable configurations of each compilation need the compilation's attributes
|
||||||
|
*/
|
||||||
private fun setUpConfigurationAttributes(project: Project) {
|
private fun setUpConfigurationAttributes(project: Project) {
|
||||||
val targets = project.multiplatformExtension.targets
|
val targets = project.multiplatformExtension.targets
|
||||||
|
|
||||||
project.afterEvaluate {
|
project.afterEvaluate {
|
||||||
targets.all { target ->
|
targets.all { target ->
|
||||||
val mainCompilationAttributes = target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME)?.attributes
|
fun copyAttributes(from: AttributeContainer, to: AttributeContainer) {
|
||||||
?: return@all
|
fun <T> copyAttribute(key: Attribute<T>, from: AttributeContainer, to: AttributeContainer) {
|
||||||
|
to.attribute(key, from.getAttribute(key)!!)
|
||||||
|
}
|
||||||
|
|
||||||
fun <T> copyAttribute(key: Attribute<T>, from: AttributeContainer, to: AttributeContainer) {
|
from.keySet().forEach { key -> copyAttribute(key, from, to) }
|
||||||
to.attribute(key, from.getAttribute(key)!!)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
listOf(
|
// To copy the attributes to the output configurations, find those output configurations and their producing compilations
|
||||||
target.apiElementsConfigurationName,
|
// based on the target's components:
|
||||||
target.runtimeElementsConfigurationName,
|
val outputConfigurationsWithCompilations =
|
||||||
target.defaultConfigurationName
|
target.components.filterIsInstance<KotlinVariant>().flatMap { kotlinVariant ->
|
||||||
)
|
kotlinVariant.usages.filterIsInstance<KotlinUsageContext>().mapNotNull { usageContext ->
|
||||||
.mapNotNull { configurationName -> target.project.configurations.findByName(configurationName) }
|
project.configurations.findByName(usageContext.dependencyConfigurationName)?.let { configuration ->
|
||||||
.forEach { configuration ->
|
configuration to usageContext.compilation
|
||||||
mainCompilationAttributes.keySet().forEach { key ->
|
}
|
||||||
copyAttribute(key, mainCompilationAttributes, configuration.attributes)
|
|
||||||
}
|
}
|
||||||
}
|
} + listOfNotNull(
|
||||||
|
target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME)?.let { mainCompilation ->
|
||||||
|
project.configurations.findByName(target.defaultConfigurationName)?.to(mainCompilation)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
outputConfigurationsWithCompilations.forEach { (configuration, compilation) ->
|
||||||
|
copyAttributes(compilation.attributes, configuration.attributes)
|
||||||
|
}
|
||||||
|
|
||||||
target.compilations.all { compilation ->
|
target.compilations.all { compilation ->
|
||||||
val compilationAttributes = compilation.attributes
|
val compilationAttributes = compilation.attributes
|
||||||
|
|
||||||
compilation.relatedConfigurationNames
|
compilation.relatedConfigurationNames
|
||||||
.mapNotNull { configurationName -> target.project.configurations.findByName(configurationName) }
|
.mapNotNull { configurationName -> target.project.configurations.findByName(configurationName) }
|
||||||
.forEach { configuration ->
|
.forEach { configuration -> copyAttributes(compilationAttributes, configuration.attributes) }
|
||||||
compilationAttributes.keySet().forEach { key ->
|
|
||||||
copyAttribute(key, compilationAttributes, configuration.attributes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
|
|||||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
import kotlin.collections.HashSet
|
|
||||||
|
|
||||||
internal fun KotlinCompilation<*>.composeName(prefix: String? = null, suffix: String? = null): String {
|
internal fun KotlinCompilation<*>.composeName(prefix: String? = null, suffix: String? = null): String {
|
||||||
val compilationNamePart = compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }
|
val compilationNamePart = compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }
|
||||||
|
|||||||
+9
-4
@@ -106,10 +106,15 @@ class KotlinVariantWithMetadataDependency(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun metadataDependency(): ModuleDependency {
|
private fun metadataDependency(): ModuleDependency {
|
||||||
val metadataPublication = (metadataTarget.kotlinComponents.single() as KotlinTargetComponentWithPublication).publicationDelegate!!
|
val metadataComponent = metadataTarget.kotlinComponents.single() as KotlinTargetComponentWithPublication
|
||||||
val metadataGroupId = metadataPublication.groupId
|
val project = metadataTarget.project
|
||||||
val metadataArtifactId = metadataPublication.artifactId
|
|
||||||
val metadataVersion = metadataPublication.version
|
// The metadata component may not be published, e.g. if the whole project is not published:
|
||||||
|
val metadataPublication: MavenPublication? = metadataComponent.publicationDelegate
|
||||||
|
|
||||||
|
val metadataGroupId = metadataPublication?.groupId ?: project.group
|
||||||
|
val metadataArtifactId = metadataPublication?.artifactId ?: metadataComponent.defaultArtifactId
|
||||||
|
val metadataVersion = metadataPublication?.version ?: project.version
|
||||||
return target.project.dependencies.module("$metadataGroupId:$metadataArtifactId:$metadataVersion") as ModuleDependency
|
return target.project.dependencies.module("$metadataGroupId:$metadataArtifactId:$metadataVersion") as ModuleDependency
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-9
@@ -6,15 +6,24 @@
|
|||||||
@file:Suppress("PackageDirectoryMismatch") // Old package for compatibility
|
@file:Suppress("PackageDirectoryMismatch") // Old package for compatibility
|
||||||
package org.jetbrains.kotlin.gradle.plugin.mpp
|
package org.jetbrains.kotlin.gradle.plugin.mpp
|
||||||
|
|
||||||
|
import com.android.build.gradle.api.BaseVariant
|
||||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
|
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
|
||||||
|
|
||||||
class KotlinJvmAndroidCompilation(
|
class KotlinJvmAndroidCompilation(
|
||||||
target: KotlinAndroidTarget,
|
target: KotlinAndroidTarget,
|
||||||
name: String
|
name: String
|
||||||
) : AbstractKotlinCompilationToRunnableFiles<KotlinJvmOptions>(target, name) {
|
) : AbstractKotlinCompilationToRunnableFiles<KotlinJvmOptions>(target, name) {
|
||||||
override val compileKotlinTask: org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|
||||||
get() = super.compileKotlinTask as org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|
||||||
|
|
||||||
override val relatedConfigurationNames: List<String>
|
lateinit internal var androidVariant: BaseVariant
|
||||||
get() = super.relatedConfigurationNames + listOf("${name}ApiElements", "${name}RuntimeElements")
|
|
||||||
}
|
override val compileKotlinTask: org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
get() = super.compileKotlinTask as org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
|
override val relatedConfigurationNames: List<String>
|
||||||
|
get() = super.relatedConfigurationNames + listOf(
|
||||||
|
"${androidVariant.name}ApiElements",
|
||||||
|
"${androidVariant.name}RuntimeElements",
|
||||||
|
androidVariant.compileConfiguration.name,
|
||||||
|
androidVariant.runtimeConfiguration.name
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user