From 5b5316214f52a3910c104a6801b22193d6eefef4 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Thu, 28 Feb 2019 18:17:51 +0300 Subject: [PATCH] Apply user-defined attributes in single-platform plugins The attributes applied by the user to the target and its compilations were ignored in single-platform plugins while the target was not a part of the API. Since the user will be able to access and configure the target, we need to apply the attributes to the relevant configurations in the same way as in MPP. --- .../kotlin/gradle/KotlinGradlePluginIT.kt | 42 ++++++++ .../kotlin/gradle/plugin/KotlinPlugin.kt | 5 + .../plugin/mpp/KotlinMultiplatformPlugin.kt | 101 +++++++++--------- 3 files changed, 98 insertions(+), 50 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt index 05cc6121b6c..2ec9ae71c61 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt @@ -921,4 +921,46 @@ class KotlinGradleIT : BaseGradleIT() { assertFileExists(moduleDir + "new-model-1.0-sources.jar") } } + + @Test + fun testUserDefinedAttributesInSinglePlatformProject() = + with(Project("multiprojectWithDependency", GradleVersionRequired.AtLeast("4.7"))) { + setupWorkingDir() + gradleBuildScript("projA").appendText( + "\n" + """ + def targetAttribute = Attribute.of("com.example.target", String) + def compilationAttribute = Attribute.of("com.example.compilation", String) + kotlin.target.attributes.attribute(targetAttribute, "foo") + kotlin.target.compilations["main"].attributes.attribute(compilationAttribute, "foo") + """.trimIndent() + ) + gradleBuildScript("projB").appendText( + "\n" + """ + def targetAttribute = Attribute.of("com.example.target", String) + def compilationAttribute = Attribute.of("com.example.compilation", String) + kotlin.target.attributes.attribute(targetAttribute, "foo") + kotlin.target.compilations["main"].attributes.attribute(compilationAttribute, "foo") + """.trimIndent() + ) + build(":projB:compileKotlin") { + assertSuccessful() + } + // Break dependency resolution by providing incompatible custom attributes in the target: + gradleBuildScript("projB").appendText("\nkotlin.target.attributes.attribute(targetAttribute, \"bar\")") + build(":projB:compileKotlin") { + assertFailed() + assertContains("Required com.example.target 'bar'") + } + // And using the compilation attributes (fix the target attributes first): + gradleBuildScript("projB").appendText( + "\n" + """ + kotlin.target.attributes.attribute(targetAttribute, "foo") + kotlin.target.compilations["main"].attributes.attribute(compilationAttribute, "bar") + """.trimIndent() + ) + build(":projB:compileKotlin") { + assertFailed() + assertContains("Required com.example.compilation 'bar'") + } + } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt index af227521cd4..c660b0cbd6c 100755 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt @@ -354,6 +354,8 @@ internal abstract class AbstractKotlinPlugin( { compilation -> buildSourceSetProcessor(project, compilation, kotlinPluginVersion) } ) + applyUserDefinedAttributes(target) + rewriteMppDependenciesInPom(target) configureProjectGlobalSettings(project, kotlinPluginVersion) @@ -644,6 +646,9 @@ internal open class KotlinAndroidPlugin( (project.kotlinExtension as KotlinAndroidProjectExtension).target = androidTarget applyToTarget(kotlinPluginVersion, androidTarget) + + applyUserDefinedAttributes(androidTarget) + registry.register(KotlinModelBuilder(kotlinPluginVersion, androidTarget)) project.whenEvaluated { project.components.addAll(androidTarget.components) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt index caab8f79f57..b4b35220923 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt @@ -23,7 +23,10 @@ import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.internal.reflect.Instantiator import org.gradle.jvm.tasks.Jar import org.gradle.util.ConfigureUtil -import org.jetbrains.kotlin.gradle.dsl.* +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.plugin.* import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder import org.jetbrains.kotlin.gradle.utils.SingleWarningPerBuild @@ -85,7 +88,7 @@ class KotlinMultiplatformPlugin( ) configurePublishingWithMavenPublish(project) - setUpConfigurationAttributes(project) + targetsContainer.all { applyUserDefinedAttributes(it) } // propagate compiler plugin options to the source set language settings setupCompilerPluginOptions(project) @@ -226,54 +229,6 @@ 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) { - val targets = project.multiplatformExtension.targets - - project.afterEvaluate { - targets.all { target -> - fun copyAttributes(from: AttributeContainer, to: AttributeContainer) { - fun copyAttribute(key: Attribute, from: AttributeContainer, to: AttributeContainer) { - to.attribute(key, from.getAttribute(key)!!) - } - - from.keySet().forEach { key -> copyAttribute(key, from, to) } - } - - // To copy the attributes to the output configurations, find those output configurations and their producing compilations - // based on the target's components: - val outputConfigurationsWithCompilations = - target.components.filterIsInstance().flatMap { kotlinVariant -> - kotlinVariant.usages.filterIsInstance().mapNotNull { usageContext -> - project.configurations.findByName(usageContext.dependencyConfigurationName)?.let { configuration -> - configuration to usageContext.compilation - } - } - } + 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 -> - val compilationAttributes = compilation.attributes - - compilation.relatedConfigurationNames - .mapNotNull { configurationName -> target.project.configurations.findByName(configurationName) } - .forEach { configuration -> copyAttributes(compilationAttributes, configuration.attributes) } - } - } - } - } - companion object { const val METADATA_TARGET_NAME = "metadata" @@ -286,6 +241,52 @@ 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 + */ +internal fun applyUserDefinedAttributes(target: KotlinTarget) { + val project = target.project + + project.whenEvaluated { + fun copyAttributes(from: AttributeContainer, to: AttributeContainer) { + fun copyAttribute(key: Attribute, from: AttributeContainer, to: AttributeContainer) { + to.attribute(key, from.getAttribute(key)!!) + } + + from.keySet().forEach { key -> copyAttribute(key, from, to) } + } + + // To copy the attributes to the output configurations, find those output configurations and their producing compilations + // based on the target's components: + val outputConfigurationsWithCompilations = + target.components.filterIsInstance().flatMap { kotlinVariant -> + kotlinVariant.usages.filterIsInstance().mapNotNull { usageContext -> + project.configurations.findByName(usageContext.dependencyConfigurationName)?.let { configuration -> + configuration to usageContext.compilation + } + } + } + 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 -> + val compilationAttributes = compilation.attributes + + compilation.relatedConfigurationNames + .mapNotNull { configurationName -> target.project.configurations.findByName(configurationName) } + .forEach { configuration -> copyAttributes(compilationAttributes, configuration.attributes) } + } + } +} + internal fun sourcesJarTask(compilation: KotlinCompilation<*>, componentName: String?, artifactNameAppendix: String): Jar { val project = compilation.target.project val taskName = lowerCamelCaseName(componentName, "sourcesJar")