main.name.remal.gradle_plugins.plugins.java.JavaSettingsPlugin.kt Maven / Gradle / Ivy
package name.remal.gradle_plugins.plugins.java
import name.remal.default
import name.remal.gradle_plugins.api.AutoService
import name.remal.gradle_plugins.dsl.ApplyPluginClasses
import name.remal.gradle_plugins.dsl.ApplyPluginClassesAtTheEnd
import name.remal.gradle_plugins.dsl.BaseReflectiveProjectPlugin
import name.remal.gradle_plugins.dsl.CreateConfigurationsPluginAction
import name.remal.gradle_plugins.dsl.GradleEnumVersion.GRADLE_VERSION_5_6
import name.remal.gradle_plugins.dsl.MinGradleVersion
import name.remal.gradle_plugins.dsl.Plugin
import name.remal.gradle_plugins.dsl.PluginAction
import name.remal.gradle_plugins.dsl.PluginActionsGroup
import name.remal.gradle_plugins.dsl.PluginCondition
import name.remal.gradle_plugins.dsl.WithPlugins
import name.remal.gradle_plugins.dsl.extensions.all
import name.remal.gradle_plugins.dsl.extensions.classifierCompatible
import name.remal.gradle_plugins.dsl.extensions.createDependencyTransformConfiguration
import name.remal.gradle_plugins.dsl.extensions.doSetup
import name.remal.gradle_plugins.dsl.extensions.doSetupIf
import name.remal.gradle_plugins.dsl.extensions.doSetupIfAndAfterEvaluate
import name.remal.gradle_plugins.dsl.extensions.get
import name.remal.gradle_plugins.dsl.extensions.getJavaModuleName
import name.remal.gradle_plugins.dsl.extensions.getOrNull
import name.remal.gradle_plugins.dsl.extensions.isCompilingSourceSet
import name.remal.gradle_plugins.dsl.extensions.isPluginApplied
import name.remal.gradle_plugins.dsl.extensions.isSourceJava9Compatible
import name.remal.gradle_plugins.dsl.extensions.isTargetJava8Compatible
import name.remal.gradle_plugins.dsl.extensions.javaModuleName
import name.remal.gradle_plugins.dsl.extensions.logDebug
import name.remal.gradle_plugins.dsl.extensions.parameters
import name.remal.gradle_plugins.dsl.extensions.set
import name.remal.gradle_plugins.dsl.extensions.useDefault
import name.remal.gradle_plugins.dsl.utils.MODULE_NAME_MANIFEST_ATTRIBUTE
import name.remal.gradle_plugins.plugins.classes_processing.ClassesProcessingPlugin
import name.remal.gradle_plugins.plugins.code_quality.checkstyle.CheckstyleSettingsPlugin
import name.remal.gradle_plugins.plugins.code_quality.jacoco.JacocoSettingsPlugin
import name.remal.gradle_plugins.plugins.common.CommonSettingsPlugin
import name.remal.gradle_plugins.plugins.dependencies.TransitiveDependenciesConfigurationMatcher
import name.remal.gradle_plugins.plugins.dependencies.TransitiveDependenciesPlugin
import name.remal.gradle_plugins.plugins.merge_resources.MergeResourcesPlugin
import name.remal.gradle_plugins.plugins.testing.TestSettingsPlugin
import name.remal.gradle_plugins.test_source_sets.TestSourceSetsPlugin
import name.remal.nullIfEmpty
import name.remal.uncheckedCast
import org.gradle.api.Named
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.artifacts.dsl.RepositoryHandler
import org.gradle.api.attributes.Attribute
import org.gradle.api.model.ObjectFactory
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.SourceSet.MAIN_SOURCE_SET_NAME
import org.gradle.api.tasks.SourceSetContainer
import org.gradle.api.tasks.SourceSetOutput
import org.gradle.api.tasks.TaskContainer
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.testing.Test
import org.gradle.jvm.tasks.Jar
import org.gradle.process.JavaExecSpec
import org.gradle.util.GradleVersion
import java.nio.charset.StandardCharsets.UTF_8
const val COMPILE_ONLY_ALL_CONFIGURATION_NAME = "compileOnlyAll"
const val COMPILE_OPTIONAL_CONFIGURATION_NAME = "compileOptional"
const val COMPILE_OPTIONAL_TRANSITIVE_CONFIGURATION_NAME = "compileOptional-transitive"
@Plugin(
id = "name.remal.java-settings",
description = "Plugin that configures 'java' plugin if it's applied. This plugin fixes some problems building Java 9 projects.",
tags = ["java"]
)
@WithPlugins(JavaPluginId::class)
@ApplyPluginClasses(CommonSettingsPlugin::class)
@ApplyPluginClassesAtTheEnd(
TransitiveDependenciesPlugin::class,
AptPlugin::class,
ClassesProcessingPlugin::class,
MergeResourcesPlugin::class,
TestSettingsPlugin::class,
TestSourceSetsPlugin::class,
CheckstyleSettingsPlugin::class,
JacocoSettingsPlugin::class,
JavaApplicationSettingsPlugin::class
)
class JavaSettingsPlugin : BaseReflectiveProjectPlugin() {
@PluginAction
@MinGradleVersion(GRADLE_VERSION_5_6)
fun SourceSetContainer.`Always consume JAR artifact and not classes`(configurations: ConfigurationContainer, objects: ObjectFactory) {
val libraryElementsClass: Class = Class.forName("org.gradle.api.attributes.LibraryElements").uncheckedCast()
val libraryElementsAttribute: Attribute = libraryElementsClass.getField("LIBRARY_ELEMENTS_ATTRIBUTE").get(null).uncheckedCast()
//val libraryElementClassesAndResources: String = libraryElementsClass.getField("CLASSES_AND_RESOURCES").get(null).uncheckedCast()
val libraryElementJar: String = libraryElementsClass.getField("JAR").get(null).uncheckedCast()
all { sourceSet ->
configurations[sourceSet.compileClasspathConfigurationName].attributes { attrs ->
attrs.attribute(libraryElementsAttribute, objects.named(libraryElementsClass, libraryElementJar))
}
}
}
@CreateConfigurationsPluginAction
fun ConfigurationContainer.`Create compileOnlyAll configuration`(sourceSets: SourceSetContainer) {
create(COMPILE_ONLY_ALL_CONFIGURATION_NAME) { conf ->
conf.description = "All compileOnly configurations extend this configuration"
sourceSets.all { sourceSet -> this[sourceSet.compileOnlyConfigurationName].extendsFrom(conf) }
}
}
@CreateConfigurationsPluginAction(
""
+ "Create compileOptional configuration.\n"
+ "CompileOnly configuration extends it. Also all *compile configurations extend it.\n"
+ "Dependencies from compileOptional configuration are added to all Test and JavaExec tasks including transitive dependencies. Transitive dependencies can be configured using name.remal.transitive-dependencies plugin."
)
fun ConfigurationContainer.createCompileOptionalConfiguration(sourceSets: SourceSetContainer, tasks: TaskContainer) {
val compileOptional = create(COMPILE_OPTIONAL_CONFIGURATION_NAME) { conf ->
conf.description = "Optional compile dependencies"
sourceSets.all(MAIN_SOURCE_SET_NAME) {
findByName(it.compileOnlyConfigurationName)?.extendsFrom(conf)
}
sourceSets.matching { MAIN_SOURCE_SET_NAME != it.name }.all {
findByName(it.implementationConfigurationName)?.extendsFrom(conf)
}
}
val transitiveConf = createDependencyTransformConfiguration(compileOptional, COMPILE_OPTIONAL_TRANSITIVE_CONFIGURATION_NAME) { dep ->
dep.copy().also { copyDep ->
if (copyDep is ModuleDependency) {
copyDep.isTransitive = true
}
}
}
transitiveConf.isVisible = false
transitiveConf.isCanBeConsumed = false
tasks.all { task ->
if (task is JavaExecSpec
&& task !is Test // handled below
) {
task.doSetup(Int.MIN_VALUE) { _ ->
val transitiveConfFiles = transitiveConf.files
if (transitiveConfFiles.isNotEmpty()) {
task.classpath = task.classpath + task.project.files(transitiveConfFiles)
}
}
}
}
tasks.all(Test::class.java) {
it.doSetup(Int.MIN_VALUE) {
val transitiveConfFiles = transitiveConf.files
if (transitiveConfFiles.isNotEmpty()) {
it.classpath = it.classpath + it.project.files(transitiveConfFiles)
}
}
}
}
@PluginAction
fun RepositoryHandler.`Use mavenCentral and mavenLocal repositories by default`() {
useDefault {
mavenCentral()
mavenLocal()
}
}
@PluginActionsGroup
inner class `For all JavaCompile tasks` {
@PluginAction
fun TaskContainer.`Set default encoding to UTF-8`() {
all(JavaCompile::class.java) {
if (null == it.options.encoding) {
it.logDebug("Setting encoding to UTF-8")
it.options.encoding = UTF_8.name()
}
}
}
@PluginAction
fun TaskContainer.`Enable displaying deprecation warnings`() {
all(JavaCompile::class.java) {
it.options.isDeprecation = true
}
}
@PluginAction
fun TaskContainer.`Add '-parameters' compiler option if targetting Java 8 and above`() {
all(JavaCompile::class.java) {
it.doSetupIfAndAfterEvaluate(AbstractCompile::isTargetJava8Compatible) { task ->
task.options.parameters = true
}
}
}
@PluginActionsGroup
inner class `For Gradle less than 7` {
@PluginCondition
fun `Gradle less than 7`(): Boolean {
return GradleVersion.current() < GradleVersion.version("7.0")
}
@PluginAction
fun TaskContainer.`Add '--module-path' compiler option if sources are compatible with Java 9 and above`() {
all(JavaCompile::class.java) {
it.doSetupIf(Int.MAX_VALUE, AbstractCompile::isSourceJava9Compatible) { task ->
task.options.compilerArgs.let { compilerArgs ->
if ("--module-path" !in compilerArgs) {
var classpath = task.classpath
task.options.annotationProcessorPath?.let { classpath += it }
val modulePath = classpath.asPath
if (modulePath.isNotEmpty()) {
task.logDebug("Adding --module-path {}", modulePath)
compilerArgs.add("--module-path")
compilerArgs.add(modulePath)
}
}
}
}
}
}
@PluginAction
fun TaskContainer.`Add '--patch-module' compiler option if sources are compatible with Java 9 and above`() {
all(JavaCompile::class.java) {
it.doSetupIf(Int.MAX_VALUE, AbstractCompile::isSourceJava9Compatible) { task ->
val moduleName = task.source.getJavaModuleName() ?: return@doSetupIf
val classesDirsList = (task.project.getOrNull(SourceSetContainer::class.java)?.asSequence() ?: emptySequence())
.filter(task::isCompilingSourceSet)
.map(SourceSet::getOutput)
.map(SourceSetOutput::getClassesDirs)
.toList()
.nullIfEmpty() ?: return@doSetupIf
val modulePath = classesDirsList.asSequence()
.reduce { fc1, fc2 -> fc1 + fc2 }
.asPath
.nullIfEmpty() ?: return@doSetupIf
task.options.compilerArgs.apply {
forEachIndexed { index, arg ->
if (arg == "--patch-module") {
if (getOrNull(index + 1).default().startsWith("$moduleName=")) {
task.logDebug("{} module has been already patched", moduleName)
return@apply
}
}
}
task.logDebug("Adding --patch-module for module {}: {}", moduleName, modulePath)
add("--patch-module")
add("$moduleName=$modulePath")
}
}
}
}
}
}
@PluginAction
fun TaskContainer.`Add Automatic-Module-Name manifest attribute in result jar archive if targetting Java 8 or below`(java: JavaPluginExtension) {
all(Jar::class.java) {
it.doSetup(10) { task ->
val targetCompatibility = java.sourceCompatibility
if (targetCompatibility.isJava9Compatible) return@doSetup
val moduleNameAttribute = MODULE_NAME_MANIFEST_ATTRIBUTE
if (task.classifierCompatible in setOf("sources", "javadoc", "groovydoc", "kotlindoc", "dokka", "kdoc", "scaladoc")) {
task.logDebug("Skip adding {} manifest attribute, as classifier equals to '{}'", moduleNameAttribute, it.classifierCompatible)
return@doSetup
}
val attributes = task.manifest.attributes
if (moduleNameAttribute !in attributes) {
val moduleName = task.project.javaModuleName
task.logDebug("Adding {} manifest attribute: {}", moduleNameAttribute, moduleName)
task.inputs[JavaSettingsPlugin::class.java.name + ':' + moduleNameAttribute] = moduleName
attributes[moduleNameAttribute] = moduleName
}
}
}
}
}
val ConfigurationContainer.compileOnlyAll: Configuration get() = this[COMPILE_ONLY_ALL_CONFIGURATION_NAME]
val ConfigurationContainer.compileOptional: Configuration get() = this[COMPILE_OPTIONAL_CONFIGURATION_NAME]
val ConfigurationContainer.compileOptionalTransitive: Configuration get() = this[COMPILE_OPTIONAL_TRANSITIVE_CONFIGURATION_NAME]
@AutoService
class JavaSettingsPluginTransitiveDependenciesConfigurationMatcher : TransitiveDependenciesConfigurationMatcher {
override fun matches(project: Project, configuration: Configuration): Boolean {
if (!project.isPluginApplied(JavaSettingsPlugin::class.java)) return false
if (COMPILE_ONLY_ALL_CONFIGURATION_NAME == configuration.name) return true
if (COMPILE_OPTIONAL_CONFIGURATION_NAME == configuration.name) return true
if (COMPILE_OPTIONAL_TRANSITIVE_CONFIGURATION_NAME == configuration.name) return true
return false
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy