From ad9aa8d5191e479bd670e144d61ea424a51b2281 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 24 Mar 2022 10:56:36 +0100 Subject: [PATCH] Add plugin variants Change configuration adding Gradle plugin variants. 'main' SourceSet is used for minimal supported Gradle versions, 'common' SourceSet for common code that is similar between versions, plus, currently, 'gradle70' SourceSet for Gradle 7.0+ versions. ^KT-49227 In Progress --- build.gradle.kts | 3 +- buildSrc/src/main/kotlin/GradleCommon.kt | 319 +++++++++++++++++- ...dle-plugin-common-configuration.gradle.kts | 14 +- ...plugin-dependency-configuration.gradle.kts | 11 +- gradle/verification-metadata.xml | 6 + 5 files changed, 339 insertions(+), 14 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index bdd07982832..2973a97930e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -328,7 +328,8 @@ extra["compilerArtifactsForIde"] = listOf( // TODO: fix remaining warnings and remove this property. extra["tasksWithWarnings"] = listOf( - ":kotlin-gradle-plugin:compileKotlin" + ":kotlin-gradle-plugin:compileKotlin", + ":kotlin-gradle-plugin:compileGradle70Kotlin" ) val tasksWithWarnings: List by extra diff --git a/buildSrc/src/main/kotlin/GradleCommon.kt b/buildSrc/src/main/kotlin/GradleCommon.kt index 52f80c10acf..1c1cf17d6f1 100644 --- a/buildSrc/src/main/kotlin/GradleCommon.kt +++ b/buildSrc/src/main/kotlin/GradleCommon.kt @@ -3,22 +3,48 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ +import org.gradle.api.Action import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.ModuleDependency +import org.gradle.api.artifacts.type.ArtifactTypeDefinition +import org.gradle.api.attributes.Attribute +import org.gradle.api.attributes.LibraryElements +import org.gradle.api.attributes.java.TargetJvmEnvironment +import org.gradle.api.attributes.plugin.GradlePluginApiVersion import org.gradle.api.plugins.JavaLibraryPlugin import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.publish.PublishingExtension import org.gradle.api.publish.maven.MavenPublication +import org.gradle.api.tasks.Copy import org.gradle.api.tasks.SourceSet import org.gradle.jvm.tasks.Jar import org.gradle.kotlin.dsl.* import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin +import org.jetbrains.dokka.DokkaVersion import org.jetbrains.dokka.gradle.DokkaTask +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.project.model.KotlinPlatformTypeAttribute import plugins.configureDefaultPublishing import plugins.configureKotlinPomAttributes +/** + * Gradle plugins common variants. + * + * [minimalSupportedGradleVersion] - minimal Gradle version that is supported in this variant + * [gradleApiVersion] - Gradle API dependency version. Usually should be the same as [minimalSupportedGradleVersion]. + */ +enum class GradlePluginVariant( + val sourceSetName: String, + val minimalSupportedGradleVersion: String, + val gradleApiVersion: String +) { + GRADLE_MIN("main", "6.7", "6.9"), + GRADLE_70("gradle70", "7.0", "7.0"), + //GRADLE_71("gradle71", "7.1", "7.1"), +} + /** * Configures common pom configuration parameters */ @@ -62,10 +88,148 @@ fun Project.excludeGradleCommonDependencies(sourceSet: SourceSet) { } /** - * 'main' sources are used for Gradle 6.1-6.9 versions. - * Directories are renamed into 'src/gradle61'. + * Common sources for all variants. + * Should contain classes that are independent of Gradle API version or using minimal supported Gradle api. */ -fun Project.configureGradlePluginCommonSettings() { +fun Project.createGradleCommonSourceSet(): SourceSet { + val commonSourceSet = sourceSets.create("common") { + excludeGradleCommonDependencies(this) + + dependencies { + compileOnlyConfigurationName(kotlinStdlib()) + compileOnlyConfigurationName("dev.gradleplugins:gradle-api:7.2") + if (this@createGradleCommonSourceSet.name != "kotlin-gradle-plugin-api" && + this@createGradleCommonSourceSet.name != "android-test-fixes" + ) { + compileOnlyConfigurationName(project(":kotlin-gradle-plugin-api")) { + capabilities { + requireCapability("org.jetbrains.kotlin:kotlin-gradle-plugin-api-common") + } + } + } + } + } + + plugins.withType().configureEach { + this@createGradleCommonSourceSet.extensions.configure { + registerFeature(commonSourceSet.name) { + usingSourceSet(commonSourceSet) + disablePublication() + } + } + } + + return commonSourceSet +} + +/** + * Fixes wired SourceSet does not expose compiled common classes and common resources as secondary variant + * which is used in the Kotlin Project compilation. + */ +private fun Project.fixWiredSourceSetSecondaryVariants( + wireSourceSet: SourceSet, + commonSourceSet: SourceSet +) { + configurations + .matching { + it.name == wireSourceSet.apiElementsConfigurationName || + it.name == wireSourceSet.runtimeElementsConfigurationName + } + .configureEach { + outgoing { + variants.maybeCreate("classes").apply { + attributes { + attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.CLASSES)) + } + (commonSourceSet.output.classesDirs.files + wireSourceSet.output.classesDirs.files) + .toSet() + .forEach { + if (!artifacts.files.contains(it)) { + artifact(it) { + type = ArtifactTypeDefinition.JVM_CLASS_DIRECTORY + } + } + } + } + } + } + + configurations + .matching { it.name == wireSourceSet.runtimeElementsConfigurationName } + .configureEach { + outgoing { + val resourcesDirectories = listOfNotNull( + commonSourceSet.output.resourcesDir, + wireSourceSet.output.resourcesDir + ) + + if (resourcesDirectories.isNotEmpty()) { + variants.maybeCreate("resources").apply { + attributes { + attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.RESOURCES)) + } + resourcesDirectories.forEach { + if (!artifacts.files.contains(it)) { + artifact(it) { + type = ArtifactTypeDefinition.JVM_RESOURCES_DIRECTORY + } + } + } + } + } + } + } +} + +/** + * Make [wireSourceSet] to extend [commonSourceSet]. + */ +fun Project.wireGradleVariantToCommonGradleVariant( + wireSourceSet: SourceSet, + commonSourceSet: SourceSet +) { + wireSourceSet.compileClasspath += commonSourceSet.output + wireSourceSet.runtimeClasspath += commonSourceSet.output + @Suppress("deprecation") // Needs support from KGP + wireSourceSet.withConvention(KotlinSourceSet::class) { + val wireKotlinSourceSet = this + commonSourceSet.withConvention(KotlinSourceSet::class) { + wireKotlinSourceSet.dependsOn(this) + } + } + + configurations[wireSourceSet.apiConfigurationName].extendsFrom( + configurations[commonSourceSet.apiConfigurationName] + ) + configurations[wireSourceSet.implementationConfigurationName].extendsFrom( + configurations[commonSourceSet.implementationConfigurationName] + ) + configurations[wireSourceSet.runtimeOnlyConfigurationName].extendsFrom( + configurations[commonSourceSet.runtimeOnlyConfigurationName] + ) + configurations[wireSourceSet.compileOnlyConfigurationName].extendsFrom( + configurations[commonSourceSet.compileOnlyConfigurationName] + ) + + fixWiredSourceSetSecondaryVariants(wireSourceSet, commonSourceSet) + + tasks.withType().configureEach { + if (name == wireSourceSet.jarTaskName) { + from(wireSourceSet.output, commonSourceSet.output) + setupPublicJar(archiveBaseName.get()) + addEmbeddedRuntime() + } else if (name == wireSourceSet.sourcesJarTaskName) { + from(wireSourceSet.allSource, commonSourceSet.allSource) + } + } +} + +/** + * 'main' sources are used for minimal supported Gradle versions (6.7) up to Gradle 7.0. + */ +fun Project.reconfigureMainSourcesSetForGradlePlugin( + commonSourceSet: SourceSet +) { sourceSets.named(SourceSet.MAIN_SOURCE_SET_NAME) { plugins.withType().configureEach { // Removing Gradle api default dependency added by 'java-gradle-plugin' @@ -76,15 +240,16 @@ fun Project.configureGradlePluginCommonSettings() { "compileOnly"(kotlinStdlib()) // Decoupling gradle-api artifact from current project Gradle version. Later would be useful for // gradle plugin variants - "compileOnly"("dev.gradleplugins:gradle-api:7.2") - if (this@configureGradlePluginCommonSettings.name != "kotlin-gradle-plugin-api" && - this@configureGradlePluginCommonSettings.name != "android-test-fixes" + "compileOnly"("dev.gradleplugins:gradle-api:${GradlePluginVariant.GRADLE_MIN.gradleApiVersion}") + if (this@reconfigureMainSourcesSetForGradlePlugin.name != "kotlin-gradle-plugin-api" && + this@reconfigureMainSourcesSetForGradlePlugin.name != "android-test-fixes" ) { "api"(project(":kotlin-gradle-plugin-api")) } } excludeGradleCommonDependencies(this) + wireGradleVariantToCommonGradleVariant(this, commonSourceSet) tasks.withType().configureEach { if (name == jarTaskName) { @@ -96,7 +261,7 @@ fun Project.configureGradlePluginCommonSettings() { } plugins.withType().configureEach { - this@configureGradlePluginCommonSettings + this@reconfigureMainSourcesSetForGradlePlugin .extensions .configure { withSourcesJar() @@ -111,7 +276,17 @@ fun Project.configureGradlePluginCommonSettings() { } plugins.withId("org.jetbrains.dokka") { - val dokkaTask = tasks.named("dokkaJavadoc") + val dokkaTask = tasks.named("dokkaJavadoc") { + dokkaSourceSets { + named(commonSourceSet.name) { + suppress.set(false) + } + + named("main") { + dependsOn(commonSourceSet) + } + } + } tasks.withType().configureEach { if (name == javadocJarTaskName) { @@ -122,6 +297,130 @@ fun Project.configureGradlePluginCommonSettings() { } } +/** + * Adding plugin variants: https://docs.gradle.org/current/userguide/implementing_gradle_plugins.html#plugin-with-variants + */ +fun Project.createGradlePluginVariant( + variant: GradlePluginVariant, + commonSourceSet: SourceSet, + isGradlePlugin: Boolean = true +): SourceSet { + val variantSourceSet = sourceSets.create(variant.sourceSetName) { + excludeGradleCommonDependencies(this) + wireGradleVariantToCommonGradleVariant(this, commonSourceSet) + } + + plugins.withType().configureEach { + extensions.configure { + registerFeature(variantSourceSet.name) { + usingSourceSet(variantSourceSet) + if (isGradlePlugin) { + capability(project.group.toString(), project.name, project.version.toString()) + } + + // Enabling publishing docs jars only on CI build + // Currently dokka task runs non-incrementally and takes big amount of time + if (kotlinBuildProperties.publishGradlePluginsJavadoc || + kotlinBuildProperties.isTeamcityBuild + ) { + withJavadocJar() + } + withSourcesJar() + } + + configurations.named(variantSourceSet.apiElementsConfigurationName, commonVariantAttributes()) + configurations.named(variantSourceSet.runtimeElementsConfigurationName, commonVariantAttributes()) + } + + tasks.named(variantSourceSet.sourcesJarTaskName) { + addEmbeddedSources() + } + } + + // Enabling publishing docs jars only on CI build + // Currently dokka task runs non-incrementally and takes big amount of time + if (kotlinBuildProperties.publishGradlePluginsJavadoc || + kotlinBuildProperties.isTeamcityBuild + ) { + plugins.withId("org.jetbrains.dokka") { + val dokkaTask = tasks.register("dokka${variantSourceSet.javadocTaskName.capitalize()}") { + description = "Generates documentation in 'javadoc' format for '${variantSourceSet.javadocTaskName}' variant" + + plugins.dependencies.add( + project.dependencies.create("org.jetbrains.dokka:javadoc-plugin:${DokkaVersion.version}") + ) + + dokkaSourceSets { + named(commonSourceSet.name) { + suppress.set(false) + } + + named(variantSourceSet.name) { + dependsOn(commonSourceSet) + suppress.set(false) + } + } + } + + tasks.named(variantSourceSet.javadocJarTaskName) { + from(dokkaTask.flatMap { it.outputDirectory }) + } + } + } + + plugins.withId("java-gradle-plugin") { + tasks.named(variantSourceSet.processResourcesTaskName) { + val copyPluginDescriptors = rootSpec.addChild() + copyPluginDescriptors.into("META-INF/gradle-plugins") + copyPluginDescriptors.from(tasks.named("pluginDescriptors")) + } + } + + configurations.configureEach { + if (isCanBeConsumed && this@configureEach.name.startsWith(variantSourceSet.name)) { + attributes { + attribute( + GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, + objects.named(variant.minimalSupportedGradleVersion) + ) + } + } + } + + dependencies { + variantSourceSet.compileOnlyConfigurationName(kotlinStdlib()) + variantSourceSet.compileOnlyConfigurationName("dev.gradleplugins:gradle-api:${variant.gradleApiVersion}") + if (this@createGradlePluginVariant.name != "kotlin-gradle-plugin-api" && + this@createGradlePluginVariant.name != "android-test-fixes" + ) { + variantSourceSet.apiConfigurationName(project(":kotlin-gradle-plugin-api")) { + capabilities { + requireCapability("org.jetbrains.kotlin:kotlin-gradle-plugin-api-${variant.sourceSetName}") + } + } + } + } + + return variantSourceSet +} + +/** + * All additional configuration attributes in plugin variant should be the same as in the 'main' variant. + * Otherwise, Gradle <7.0 will fail to select plugin variant. + */ +private fun Project.commonVariantAttributes(): Action = Action { + attributes { + attribute( + TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, + objects.named(TargetJvmEnvironment.STANDARD_JVM) + ) + attribute( + Attribute.of(KotlinPlatformTypeAttribute.uniqueName, String::class.java), + KotlinPlatformTypeAttribute.JVM + ) + } +} + fun Project.configureKotlinCompileTasksGradleCompatibility() { tasks.withType().configureEach { kotlinOptions.languageVersion = "1.4" @@ -136,7 +435,8 @@ fun Project.configureKotlinCompileTasksGradleCompatibility() { // Will allow combining outputs of multiple SourceSets fun Project.publishShadowedJar( - sourceSet: SourceSet + sourceSet: SourceSet, + commonSourceSet: SourceSet ) { val jarTask = tasks.named(sourceSet.jarTaskName) @@ -149,6 +449,7 @@ fun Project.publishShadowedJar( ) addEmbeddedRuntime() from(sourceSet.output) + from(commonSourceSet.output) // When Gradle traverses the inputs, reject the shaded compiler JAR, // which leads to the content of that JAR being excluded as well: diff --git a/buildSrc/src/main/kotlin/gradle-plugin-common-configuration.gradle.kts b/buildSrc/src/main/kotlin/gradle-plugin-common-configuration.gradle.kts index 9c488453b9d..a69caa617b6 100644 --- a/buildSrc/src/main/kotlin/gradle-plugin-common-configuration.gradle.kts +++ b/buildSrc/src/main/kotlin/gradle-plugin-common-configuration.gradle.kts @@ -23,9 +23,6 @@ configure { tags = listOf("kotlin") } -configureGradlePluginCommonSettings() -publishShadowedJar(sourceSets[SourceSet.MAIN_SOURCE_SET_NAME]) - publishing { publications { withType().configureEach { @@ -39,3 +36,14 @@ publishing { } } } + +val commonSourceSet = createGradleCommonSourceSet() +reconfigureMainSourcesSetForGradlePlugin(commonSourceSet) +publishShadowedJar(sourceSets[SourceSet.MAIN_SOURCE_SET_NAME], commonSourceSet) + +// Used for Gradle 7.0+ versions +val gradle70SourceSet = createGradlePluginVariant( + GradlePluginVariant.GRADLE_70, + commonSourceSet = commonSourceSet +) +publishShadowedJar(gradle70SourceSet, commonSourceSet) diff --git a/buildSrc/src/main/kotlin/gradle-plugin-dependency-configuration.gradle.kts b/buildSrc/src/main/kotlin/gradle-plugin-dependency-configuration.gradle.kts index c231583fcde..dbe89bc329b 100644 --- a/buildSrc/src/main/kotlin/gradle-plugin-dependency-configuration.gradle.kts +++ b/buildSrc/src/main/kotlin/gradle-plugin-dependency-configuration.gradle.kts @@ -16,12 +16,21 @@ configureCommonPublicationSettingsForGradle() configureKotlinCompileTasksGradleCompatibility() extensions.extraProperties["kotlin.stdlib.default.dependency"] = "false" -configureGradlePluginCommonSettings() +val commonSourceSet = createGradleCommonSourceSet() +reconfigureMainSourcesSetForGradlePlugin(commonSourceSet) + +// Used for Gradle 7.0+ versions +createGradlePluginVariant( + GradlePluginVariant.GRADLE_70, + commonSourceSet = commonSourceSet, + isGradlePlugin = false +) publishing { publications { register(DEFAULT_MAIN_PUBLICATION_NAME) { from(components["java"]) + suppressAllPomMetadataWarnings() // Don't warn about additional published variants } } } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 3cc2837fd99..a09f3a2ae32 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -3058,6 +3058,12 @@ + + + + + +