main.name.remal.gradle_plugins.plugins.publish.MavenPublishSettingsPlugin.kt Maven / Gradle / Ivy
package name.remal.gradle_plugins.plugins.publish
import name.remal.appendElement
import name.remal.asString
import name.remal.debug
import name.remal.getChildElements
import name.remal.gradle_plugins.dsl.AfterProjectEvaluation
import name.remal.gradle_plugins.dsl.ApplyPluginClasses
import name.remal.gradle_plugins.dsl.ApplyPlugins
import name.remal.gradle_plugins.dsl.BaseReflectiveProjectPlugin
import name.remal.gradle_plugins.dsl.BuildTask
import name.remal.gradle_plugins.dsl.CreateExtensionsPluginAction
import name.remal.gradle_plugins.dsl.Plugin
import name.remal.gradle_plugins.dsl.PluginAction
import name.remal.gradle_plugins.dsl.SimpleTestAdditionalGradleScript
import name.remal.gradle_plugins.dsl.WithPlugins
import name.remal.gradle_plugins.dsl.extensions.ResolvedDependencyMapping
import name.remal.gradle_plugins.dsl.extensions.addPlugin
import name.remal.gradle_plugins.dsl.extensions.afterEvaluateOrNow
import name.remal.gradle_plugins.dsl.extensions.all
import name.remal.gradle_plugins.dsl.extensions.atTheEndOfAfterEvaluationOrNow
import name.remal.gradle_plugins.dsl.extensions.compileClasspath
import name.remal.gradle_plugins.dsl.extensions.contains
import name.remal.gradle_plugins.dsl.extensions.convention
import name.remal.gradle_plugins.dsl.extensions.dependsOn
import name.remal.gradle_plugins.dsl.extensions.forEach
import name.remal.gradle_plugins.dsl.extensions.get
import name.remal.gradle_plugins.dsl.extensions.getResolvedDependencyMappings
import name.remal.gradle_plugins.dsl.extensions.invoke
import name.remal.gradle_plugins.dsl.extensions.isPluginApplied
import name.remal.gradle_plugins.dsl.extensions.mustRunAfter
import name.remal.gradle_plugins.dsl.extensions.runtimeClasspath
import name.remal.gradle_plugins.dsl.extensions.setupTasksDependenciesAfterEvaluateOrNow
import name.remal.gradle_plugins.dsl.extensions.useDefault
import name.remal.gradle_plugins.plugins.common.CommonSettingsPlugin
import name.remal.gradle_plugins.plugins.java.JavaPluginId
import name.remal.plusAssign
import name.remal.remove
import name.remal.setNoOpEntityResolver
import name.remal.setNoValidatingXMLReaderFactory
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.Dependency.ARCHIVES_CONFIGURATION
import org.gradle.api.artifacts.DependencyArtifact.DEFAULT_TYPE
import org.gradle.api.artifacts.ExcludeRule
import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.artifacts.dsl.ArtifactHandler
import org.gradle.api.artifacts.repositories.ArtifactRepository
import org.gradle.api.plugins.BasePlugin.BUILD_GROUP
import org.gradle.api.plugins.ExtensionContainer
import org.gradle.api.plugins.JavaBasePlugin.BUILD_TASK_NAME
import org.gradle.api.plugins.JavaPlugin.JAR_TASK_NAME
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin.PUBLISH_LOCAL_LIFECYCLE_TASK_NAME
import org.gradle.api.publish.maven.tasks.AbstractPublishToMaven
import org.gradle.api.publish.maven.tasks.PublishToMavenRepository
import org.gradle.api.publish.plugins.PublishingPlugin.PUBLISH_LIFECYCLE_TASK_NAME
import org.gradle.api.publish.plugins.PublishingPlugin.PUBLISH_TASK_GROUP
import org.gradle.api.tasks.SourceSetContainer
import org.gradle.api.tasks.TaskCollection
import org.gradle.api.tasks.TaskContainer
import org.jdom2.input.SAXBuilder
import org.w3c.dom.Element
import org.w3c.dom.Node
import java.io.StringReader
import java.util.TreeSet
import kotlin.LazyThreadSafetyMode.NONE
const val BUILD_INSTALL_TASK_NAME = "buildInstall"
@Plugin(
id = "name.remal.maven-publish-settings",
description = "Plugin that configures 'maven-publish' plugin if it's applied.",
tags = ["java", "publish", "publication", "maven", "maven-publish"]
)
@WithPlugins(MavenPublishPluginId::class)
@ApplyPlugins(JavaPluginId::class)
@ApplyPluginClasses(CommonSettingsPlugin::class)
@SimpleTestAdditionalGradleScript(
"""
publishing.publications {
mavenDefault()
mavenBom()
}
"""
)
class MavenPublishSettingsPlugin : BaseReflectiveProjectPlugin() {
companion object {
private const val DEFAULT_SCOPE = "compile"
}
@PluginAction
fun ArtifactHandler.`Add 'jar' task to 'archives' configuration artifacts`(tasks: TaskContainer) {
add(ARCHIVES_CONFIGURATION, tasks[JAR_TASK_NAME])
}
private val TaskContainer.publishToMavenTasks: TaskCollection
get() = matching {
it is AbstractPublishToMaven
}
@PluginAction
fun TaskContainer.`Make all publish tasks depend on 'build' task`(tasks: TaskContainer) {
publishToMavenTasks.all { task ->
task.dependsOn {
sequenceOf(BUILD_TASK_NAME)
.mapNotNull(tasks::findByName)
.toList()
}
}
}
@PluginAction
fun TaskContainer.`Make all publish tasks run after 'build' task of all projects`(project: Project) {
publishToMavenTasks.all { publishTask ->
publishTask.mustRunAfter {
project.rootProject.allprojects.asSequence()
.filter { it.isPluginApplied(JavaPluginId) }
.mapNotNull { it.tasks.findByName(BUILD_TASK_NAME) }
.toList()
}
}
}
@PluginAction(order = -10)
fun TaskContainer.`Create 'buildInstall' task`(tasks: TaskContainer) {
create(BUILD_INSTALL_TASK_NAME, BuildInstall::class.java) { task ->
task.group = BUILD_GROUP
task.dependsOn {
sequenceOf(BUILD_TASK_NAME, PUBLISH_LOCAL_LIFECYCLE_TASK_NAME)
.mapNotNull(tasks::findByName)
.toList()
}
}
}
@PluginAction(order = 101)
@Suppress("ComplexMethod", "LongMethod")
fun ExtensionContainer.`Setup POM file dependencies`(configurations: ConfigurationContainer, project: Project) {
invoke(PublishingExtension::class.java) {
it.publications.all(MavenPublication::class.java) {
it.pom.withXml {
val root: Element = it.asElement()
root.getChildElements("dependencyManagement").forEach { it.remove() }
root.getChildElements("dependencies").forEach { it.remove() }
val managementNode: Element by lazy(NONE) { root.appendElement("dependencyManagement").appendElement("dependencies") }
val dependenciesNode: Element by lazy(NONE) { root.appendElement("dependencies") }
fun addMavenDependencies(scope: String, configuration: Configuration, dependencyConfigurationFilter: (configuration: Configuration) -> Boolean) {
val resolvedDependencyMappings = configuration.getResolvedDependencyMappings(project)
.filter { dependencyConfigurationFilter(it.configuration) }
val shouldBeExplicitlyDefined = resolvedDependencyMappings
.filter(ResolvedDependencyMapping::isShouldBeExplicitlyDefined)
.filter { it.dependency is ModuleDependency }
val commonExclusions = resolvedDependencyMappings
.filter(ResolvedDependencyMapping::isSubstituted)
.filter { it.dependency is ModuleDependency }
operator fun Node.plusAssign(resolvedDependencyMapping: ResolvedDependencyMapping) {
appendElement("groupId") += resolvedDependencyMapping.group
appendElement("artifactId") += resolvedDependencyMapping.module
resolvedDependencyMapping.classifier.let { classifier ->
if (classifier.isNotEmpty()) appendElement("classifier") += classifier
}
resolvedDependencyMapping.type.let { type ->
if (type.isNotEmpty() && DEFAULT_TYPE != type) appendElement("type") += type
}
}
shouldBeExplicitlyDefined.forEach { resolvedDependencyMapping ->
managementNode.appendElement("dependency").apply {
this += resolvedDependencyMapping
appendElement("version") += resolvedDependencyMapping.version
TreeSet(Comparator(::compareExcludeRules))
.apply excludeRulesApply@{
resolvedDependencyMapping.dependency.let { dependency ->
if (dependency is ModuleDependency) {
if (!dependency.isTransitive) {
add(ExcludeRuleImpl.create(null, null))
return@excludeRulesApply
}
}
}
resolvedDependencyMapping.dependency.let { dependency ->
if (dependency is ModuleDependency) {
dependency.excludeRules.forEach {
add(ExcludeRuleImpl.create(it.group, it.module))
}
}
}
commonExclusions.forEach {
add(ExcludeRuleImpl.create(it.requestedGroup, it.requestedModule))
}
}
.filter { it.group != resolvedDependencyMapping.group || it.module != resolvedDependencyMapping.module }
.let { excludeRules ->
if (excludeRules.isNotEmpty()) {
appendElement("exclusions").apply {
excludeRules.forEach { excludeRule ->
appendElement("exclusion").apply {
appendElement("groupId") += excludeRule.group
appendElement("artifactId") += excludeRule.module
}
}
}
}
}
}
if (resolvedDependencyMapping.isFirstLevel || resolvedDependencyMapping.isSubstituted) {
dependenciesNode.appendElement("dependency").apply {
this += resolvedDependencyMapping
if (scope != DEFAULT_SCOPE) {
appendElement("scope") += scope
}
}
}
}
}
val compileClasspath = configurations.compileClasspath
val runtimeClasspath = configurations.runtimeClasspath
run {
val runtimeClasspathHierarchy = runtimeClasspath.hierarchy
addMavenDependencies(
"compile",
compileClasspath,
{ it in runtimeClasspathHierarchy }
)
}
run {
val compileClasspathHierarchy = compileClasspath.hierarchy
addMavenDependencies(
"runtime",
runtimeClasspath,
{ it !in compileClasspathHierarchy }
)
}
}
}
}
}
@PluginAction(order = 102)
fun ExtensionContainer.`Pretty print POM xml`(project: Project) {
project.atTheEndOfAfterEvaluationOrNow { _ ->
invoke(PublishingExtension::class.java) {
it.publications.forEach(MavenPublication::class.java) {
it.pom.withXml {
try {
val stringBuilder = it.asString()
val saxBuilder = SAXBuilder().apply {
setNoValidatingXMLReaderFactory()
setNoOpEntityResolver()
}
val document = saxBuilder.build(StringReader(stringBuilder.toString()))
stringBuilder.setLength(0)
stringBuilder.append(document.document.asString(true))
} catch (e: Exception) {
logger.debug(e)
}
}
}
}
}
}
@PluginAction
@AfterProjectEvaluation
fun ExtensionContainer.`Setup POM packaging from artifacts`(project: Project) {
invoke(PublishingExtension::class.java) {
it.publications.all(MavenPublication::class.java) { publication ->
publication.artifacts.singleOrNull { it.classifier.isNullOrEmpty() }?.let { mainArtifact ->
val extension = mainArtifact.extension
if (!extension.isNullOrEmpty()) {
publication.pom.packaging = extension
}
}
}
}
}
@CreateExtensionsPluginAction("Add ${PublishingExtension.NAME}.publications extension methods")
fun ExtensionContainer.addPublicationsExtensionMethods(tasks: TaskContainer, sourceSets: SourceSetContainer, project: Project) {
invoke(PublishingExtension::class.java) {
it.publications.apply {
convention.addPlugin(MavenPublicationsExtensions(this, tasks, sourceSets, project))
}
}
}
@PluginAction
fun Project.`Create publish-to-repository tasks`(tasks: TaskContainer) {
setupTasksDependenciesAfterEvaluateOrNow { _ ->
fun forRepositoryTasks(repository: ArtifactRepository, publishTasks: List) {
val publishToRepositoryTaskName = "publishTo" + repository.name.capitalize()
if (publishToRepositoryTaskName !in tasks) {
tasks.create(publishToRepositoryTaskName) { task ->
tasks.all(PUBLISH_LIFECYCLE_TASK_NAME) { it.dependsOn(task) }
task.dependsOn(publishTasks)
task.group = PUBLISH_TASK_GROUP
}
}
}
tasks.asSequence()
.filterIsInstance(PublishToMavenRepository::class.java)
.groupBy(PublishToMavenRepository::getRepository)
.forEach { repository, publishTasks ->
forRepositoryTasks(repository, publishTasks)
}
tasks.asSequence()
.filterIsInstance(BasePublishToMavenRepository::class.java)
.groupBy(BasePublishToMavenRepository<*>::repository)
.forEach { repository, publishTasks ->
forRepositoryTasks(repository, publishTasks)
}
}
}
@PluginAction
fun ExtensionContainer.`Setup default maven publication`(project: Project) {
project.afterEvaluateOrNow {
invoke(PublishingExtension::class.java) {
it.publications.useDefault(MavenPublication::class.java) {
convention[MavenPublicationsExtensions::class.java].mavenDefault()
}
}
}
}
}
private fun String?.defaultExcludeRule(): String = if (this == null || this.isEmpty()) "*" else this
private fun compareExcludeRules(o1: ExcludeRule, o2: ExcludeRule): Int {
o1.group.defaultExcludeRule().compareTo(o2.group.defaultExcludeRule()).let { if (it != 0) return it }
o1.module.defaultExcludeRule().compareTo(o2.module.defaultExcludeRule()).let { if (it != 0) return it }
return 0
}
private data class ExcludeRuleImpl private constructor(val groupId: String, val artifactId: String) : ExcludeRule {
companion object {
fun create(groupId: String?, artifactId: String?): ExcludeRule = ExcludeRuleImpl(
groupId.defaultExcludeRule(),
artifactId.defaultExcludeRule()
)
}
override fun getGroup() = groupId
override fun getModule() = artifactId
}
@BuildTask
private class BuildInstall : DefaultTask()
© 2015 - 2024 Weber Informatics LLC | Privacy Policy