All Downloads are FREE. Search and download functionalities are using the official Maven repository.

name.remal.gradle_plugins.toolkit.build_logic.java.gradle Maven / Gradle / Ivy

import static org.objectweb.asm.ClassReader.SKIP_CODE
import static org.objectweb.asm.ClassReader.SKIP_DEBUG
import static org.objectweb.asm.ClassReader.SKIP_FRAMES

import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
import org.gradle.api.internal.tasks.compile.HasCompileOptions
import org.objectweb.asm.ClassReader
import org.objectweb.asm.tree.ClassNode

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

buildscript {
    dependencies {
        classpath platform("org.ow2.asm:asm-bom:9.7")
        classpath 'org.ow2.asm:asm-tree'
        classpath 'org.ow2.asm:asm-commons'
    }
    repositories {
        mavenCentral()
    }
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

allprojects {
    pluginManager.withPlugin('java') {
        apply plugin: 'name.remal.test-source-sets'
        apply plugin: 'name.remal.classes-relocation'
        apply plugin: 'name.remal.insert-null-checks'

        java {
            sourceCompatibility = targetCompatibility = project.javaRuntimeMinVersion

            consistentResolution {
                useCompileClasspathVersions()
            }
        }

        repositories {
            mavenCentral()
            gradlePluginPortal()

            maven {
                name = 'Gradle API'
                url = 'https://maven.pkg.github.com/remal-gradle-api/packages'
                credentials {
                    username = System.getenv('GITHUB_ACTOR') ?: 'x-access-token'
                    password = System.getenv('READ_PACKAGES_GITHUB_TOKEN') ?:
                        'g' + 'hp_xmGQ2dHvCiK685' + 'qNEFuA3IAvv6Vfg62WM1hG'
                }
            }
        }
        repositories.all {
            content {
                if (name == 'Gradle API') {
                    includeGroup('name.remal.gradle-api')
                } else {
                    excludeGroup('name.remal.gradle-api')
                }
            }
        }

        configurations.create('indirectApi') { Configuration conf ->
            conf.canBeResolved = false
            conf.canBeConsumed = false
            conf.description = "Indirect API dependencies (form example: default dependencies for configurations that are created by the plugin)"
        }

        configurations.create('compileOnlyAll') { Configuration conf ->
            conf.canBeResolved = false
            conf.canBeConsumed = false
            conf.description = "Compile-only dependencies for all source-sets"
            sourceSets.all { SourceSet sourceSet ->
                configurations[sourceSet.compileOnlyConfigurationName].extendsFrom(conf)
            }
        }

        configurations.create('optional') { Configuration conf ->
            conf.canBeResolved = true
            conf.canBeConsumed = false
            conf.description = "Optional dependencies"
            sourceSets.all { SourceSet sourceSet ->
                if (sourceSet.name == SourceSet.MAIN_SOURCE_SET_NAME) {
                    configurations[sourceSet.compileOnlyConfigurationName].extendsFrom(conf)
                } else {
                    configurations[sourceSet.implementationConfigurationName].extendsFrom(conf)
                }
            }
        }

        configurations.create('optionalHidden') { Configuration conf ->
            conf.canBeResolved = true
            conf.canBeConsumed = false
            conf.description = "Optional dependencies hidden from IDE"

            tasks.withType(AbstractCompile).configureEach { AbstractCompile task ->
                onlyIf {
                    task.classpath += conf
                    return true
                }
            }

            tasks.withType(Javadoc).configureEach { Javadoc task ->
                onlyIf {
                    task.classpath += conf
                    return true
                }
            }

            tasks.withType(Test).configureEach { Test task ->
                onlyIf {
                    task.classpath += conf
                    return true
                }
            }
        }

        configurations.create('apt') { Configuration conf ->
            conf.canBeResolved = false
            conf.canBeConsumed = false
            conf.description = "Annotation processors and compile-only dependencies for all source-sets"
            sourceSets.all { SourceSet sourceSet ->
                configurations[sourceSet.annotationProcessorConfigurationName].extendsFrom(conf)
                configurations[sourceSet.compileOnlyConfigurationName].extendsFrom(conf)
            }
        }

        configurations.create('annotationProcessorAll') { Configuration conf ->
            conf.canBeResolved = false
            conf.canBeConsumed = false
            conf.description = "Annotation processors for all source-sets"
            sourceSets.all { SourceSet sourceSet ->
                configurations[sourceSet.annotationProcessorConfigurationName].extendsFrom(conf)
            }
        }

        dependencies {
            relocateClasses 'org.apache.commons:commons-lang3'
            relocateClasses 'org.apache.commons:commons-text'
            relocateClasses 'com.google.guava:guava'

            compileOnlyAll 'com.google.code.findbugs:jsr305'
            compileOnlyAll 'org.jetbrains:annotations'
            compileOnlyAll 'name.remal:gradle-plugins-api'
            optionalHidden 'com.github.spotbugs:spotbugs-annotations'

            annotationProcessorAll 'com.google.auto.service:auto-service'
            compileOnlyAll 'com.google.auto.service:auto-service-annotations'
            annotationProcessorAll 'org.immutables:value'
            compileOnlyAll 'org.immutables:value-annotations'
            compileOnlyAll 'org.immutables:builder'
            compileOnlyAll('org.immutables:gson') {
                exclude(group: 'com.google.code.gson', module: 'gson')
            }
            optionalHidden 'org.immutables:gson'


            testImplementation 'org.junit.jupiter:junit-jupiter-api'
            testImplementation 'org.junit.jupiter:junit-jupiter-params'
            testImplementation 'org.assertj:assertj-core'
            testImplementation 'org.apache.commons:commons-lang3'
            testImplementation 'com.google.guava:guava'

            testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
            testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
        }

        if (!rootProject.allprojects.any { it.group == 'name.remal.gradle-plugins.toolkit' }) {
            dependencies {
                relocateClasses 'name.remal.gradle-plugins.toolkit:toolkit'
                testImplementation 'name.remal.gradle-plugins.toolkit:testkit'
            }
        }


        Configuration mockito4Conf = configurations.create('mockito4') { Configuration conf ->
            conf.canBeResolved = false
            conf.canBeConsumed = false
            conf.dependencies.addAll(
                [
                    project.dependencies.platform('org.mockito:mockito-bom:4' + '.' + '11.0'),
                    project.dependencies.create('org.mockito:mockito-junit-jupiter'),
                    project.dependencies.create('org.mockito:mockito-inline'),
                ]
            )
        }
        Configuration mockito5Conf = configurations.create('mockito5') { Configuration conf ->
            conf.canBeResolved = false
            conf.canBeConsumed = false
            conf.dependencies.addAll(
                [
                    project.dependencies.platform('org.mockito:mockito-bom:5.14.0'),
                    project.dependencies.create('org.mockito:mockito-junit-jupiter'),
                ]
            )
        }
        configurations.create('mockito') { Configuration conf ->
            conf.canBeResolved = false
            conf.canBeConsumed = false
            if ((project.isJavaRuntimeVersionSet && project.javaRuntimeVersion.isJava11Compatible())
                || (!project.isJavaRuntimeVersionSet && project.javaRuntimeMinVersion.isJava11Compatible())
            ) {
                conf.extendsFrom(mockito5Conf)
            } else {
                conf.extendsFrom(mockito4Conf)
            }
            configurations.testImplementation.extendsFrom(conf)
        }
        tasks.withType(Test).configureEach {
            onlyIf {
                Collection bytebuddyAgentFiles = configurations.testRuntimeClasspath.resolvedConfiguration
                    .lenientConfiguration
                    .allModuleDependencies
                    .findAll { "${it.moduleGroup}:${it.moduleName}" == 'net.bytebuddy:byte-buddy-agent' }
                    .collect { it.allModuleArtifacts.findAll { it.type === 'jar' }.collect { it.file } }
                    .flatten()
                bytebuddyAgentFiles.forEach {
                    jvmArgs("-javaagent:${it.absolutePath}")
                }
                return true
            }
        }


        sourceSets.all { SourceSet sourceSet ->
            project.configurations[sourceSet.compileClasspathConfigurationName].attributes { attrs ->
                attrs.attribute(
                    LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
                    objects.named(LibraryElements, LibraryElements.JAR)
                )
            }
            project.configurations[sourceSet.runtimeClasspathConfigurationName].attributes { attrs ->
                attrs.attribute(
                    LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
                    objects.named(LibraryElements, LibraryElements.JAR)
                )
            }
        }

        tasks.matching({ it instanceof HasCompileOptions }).configureEach { HasCompileOptions task ->
            task.options.with {
                it.fork = false
                it.incremental = false
                it.encoding = 'UTF-8'
                it.deprecation = true
                it.compilerArgs.addAll(
                    [
                        '-parameters',
                        '-Werror',
                        '-Xlint:all',
                        '-Xlint:-rawtypes',
                        '-Xlint:-serial',
                        '-Xlint:-processing',
                    ]
                )
            }
        }

        tasks.withType(JavaCompile).configureEach {
            onlyIf {
                JavaVersion compatibilityVersion = JavaVersion.toVersion(targetCompatibility)
                options.release = Integer.parseInt(compatibilityVersion.majorVersion)
                return true
            }
        }

        tasks.named(sourceSets.main.jarTaskName, Jar) {
            manifest {
                attributes(
                    'Automatic-Module-Name': project.calculateJavaModuleName()
                )
            }
        }

        tasks.withType(Test).configureEach {
            javaLauncher = javaToolchains.launcherFor {
                languageVersion = JavaLanguageVersion.of(project.javaRuntimeVersion.majorVersion)
                //vendor = project.defaultJvmVendor
            }

            if (project.javaRuntimeVersion.isJava9Compatible()) {
                if (project.javaRuntimeVersion <= JavaVersion.VERSION_16) {
                    jvmArgs('--illegal-access=deny')
                }

                /*
                 * Required for `org.gradle.testfixtures.ProjectBuilder`.
                 * See https://github.com/gradle/gradle/issues/18647
                 */
                jvmArgs('--add-opens=java.base/java.lang=ALL-UNNAMED')
            }

            exclude { FileTreeElement element ->
                if (element.directory) return false
                if (element.name.endsWith(".class")) {
                    ClassNode classNode = new ClassNode()
                    element.open().withCloseable {
                        new ClassReader(it).accept(classNode, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES)
                    }
                    Integer minSupportedJavaVersion = classNode.visibleAnnotations
                        ?.find { it.desc == 'Lname/remal/gradle_plugins/toolkit/testkit/MinSupportedJavaVersion;' }
                        ?.with {
                            for (int i = 0; i < it.values.size() - 1; i += 2) {
                                if (it.values[i] == 'value' && it.values[i + 1] instanceof Integer) {
                                    return it.values[i + 1]
                                }
                            }
                            return null
                        }
                    if (minSupportedJavaVersion != null) {
                        if (project.javaRuntimeVersion.majorVersion.toInteger() < minSupportedJavaVersion) {
                            return true
                        }
                    }
                    Integer maxSupportedJavaVersion = classNode.visibleAnnotations
                        ?.find { it.desc == 'Lname/remal/gradle_plugins/toolkit/testkit/MaxSupportedJavaVersion;' }
                        ?.with {
                            for (int i = 0; i < it.values.size() - 1; i += 2) {
                                if (it.values[i] == 'value' && it.values[i + 1] instanceof Integer) {
                                    return it.values[i + 1]
                                }
                            }
                            return null
                        }
                    if (maxSupportedJavaVersion != null) {
                        if (project.javaRuntimeVersion.majorVersion.toInteger() > maxSupportedJavaVersion) {
                            return true
                        }
                    }
                }
                return false
            }

            useJUnitPlatform()
            systemProperty('junit.jupiter.extensions.autodetection.enabled', 'true')
            enableAssertions = true

            testLogging {
                showExceptions = true
                showCauses = true
                showStackTraces = true
                exceptionFormat = 'FULL'
                stackTraceFilters('GROOVY')
                events('FAILED')
            }

            Set testsWithStdErr = Collections.newSetFromMap(new ConcurrentHashMap<>())
            ConcurrentMap> testsMessages = new ConcurrentHashMap<>()
            onOutput { TestDescriptor descr, TestOutputEvent event ->
                if (event.destination.name() == 'StdErr') {
                    testsWithStdErr.add(descr.id)
                }

                List testMessages = testsMessages.computeIfAbsent(descr.id, { new ArrayList<>() })
                testMessages.add(event.getMessage())
            }
            afterTest { TestDescriptor descr, TestResult result ->
                if (result.resultType.name() == 'FAILURE' || testsWithStdErr.contains(descr.id)) {
                    List testMessages = testsMessages.get(descr.id)
                    if (testMessages != null) {
                        println()
                        println("Output of $descr.className > $descr.displayName:")
                        testMessages.forEach { print(" > $it") }
                    }
                }
            }
        }

        project.testSourceSets.all { SourceSet sourceSet ->
            String testType = sourceSet.name.replaceFirst(/Test$/, '')
            if (sourceSet.name == 'test') {
                testType = 'unit'
            }
            tasks.named(sourceSet.name, Test).configure {
                environment('NAME_REMAL_GRADLE_PLUGINS_TEST', 'true')
                environment("NAME_REMAL_GRADLE_PLUGINS_TEST_${testType.toUpperCase()}", 'true')

                onlyIf {
                    Configuration runtimeClasspathConf = configurations[sourceSet.runtimeClasspathConfigurationName]
                    String junitVersion = runtimeClasspathConf.resolvedConfiguration
                        .lenientConfiguration
                        .allModuleDependencies
                        .find { ResolvedDependency dep ->
                            "${dep.moduleGroup}:${dep.moduleName}" == 'org.junit.jupiter:junit-jupiter-api'
                        }
                        ?.moduleVersion
                    systemProperty('junit.version', junitVersion ?: '')
                    return true
                }
            }
        }

        project.sourceSets.all { SourceSet sourceSet ->
            project.tasks.withType(AbstractCopyTask)
                .matching { it.name == sourceSet.sourcesJarTaskName }
                .configureEach { AbstractCopyTask task ->
                    task.exclude('**/internal', '**/internal/**/*')
                }
        }
    }
}