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

main.name.remal.gradle_plugins.plugins.gradle_plugins.GradlePluginsPlugin.kt Maven / Gradle / Ivy

The newest version!
package name.remal.gradle_plugins.plugins.gradle_plugins

import io.github.classgraph.ClassGraph
import io.github.classgraph.ClassInfo
import name.remal.annotationType
import name.remal.default
import name.remal.findMethod
import name.remal.fromLowerCamelToLowerHyphen
import name.remal.gradle_plugins.dsl.*
import name.remal.gradle_plugins.dsl.extensions.GRADLE_TRANSITIVE_DEPENDENCIES_EXCLUDES
import name.remal.gradle_plugins.dsl.extensions.all
import name.remal.gradle_plugins.dsl.extensions.convention
import name.remal.gradle_plugins.dsl.extensions.doSetup
import name.remal.gradle_plugins.dsl.extensions.forClassLoader
import name.remal.gradle_plugins.dsl.extensions.get
import name.remal.gradle_plugins.dsl.extensions.getRequiredResourceAsStream
import name.remal.gradle_plugins.dsl.extensions.groovy
import name.remal.gradle_plugins.dsl.extensions.isPluginApplied
import name.remal.gradle_plugins.dsl.extensions.kotlin
import name.remal.gradle_plugins.dsl.extensions.main
import name.remal.gradle_plugins.dsl.extensions.readAll
import name.remal.gradle_plugins.dsl.extensions.toHasEntries
import name.remal.gradle_plugins.dsl.extensions.withPlugin
import name.remal.gradle_plugins.dsl.reflective_project_plugin.info.PluginInfo
import name.remal.gradle_plugins.dsl.reflective_project_plugin.info.PluginInfoCollector
import name.remal.gradle_plugins.plugins.common.CommonSettingsPlugin
import name.remal.gradle_plugins.plugins.dependencies.DependencyHandlerEmbeddedKotlinExtension
import name.remal.gradle_plugins.plugins.dependencies.TransitiveDependenciesExtension
import name.remal.gradle_plugins.plugins.dependencies.TransitiveDependenciesPlugin
import name.remal.gradle_plugins.plugins.generate_sources.GenerateSourcesPlugin
import name.remal.gradle_plugins.plugins.generate_sources.generateJavaTaskName
import name.remal.gradle_plugins.plugins.generate_sources.groovy.GeneratingGroovyClassStringWriter
import name.remal.gradle_plugins.plugins.generate_sources.java.GenerateJava
import name.remal.gradle_plugins.plugins.generate_sources.java.GeneratingJavaClassStringWriter
import name.remal.gradle_plugins.plugins.generate_sources.kotlin.GeneratingKotlinClassStringWriter
import name.remal.gradle_plugins.plugins.gradle_plugins.CrossVersionGradleLibrary.CORRESPONDING_KOTLIN
import name.remal.gradle_plugins.plugins.groovy.GroovyPluginId
import name.remal.gradle_plugins.plugins.java.JavaBasePluginId
import name.remal.gradle_plugins.plugins.java.JavaPluginId
import name.remal.gradle_plugins.plugins.java.JavaSettingsPlugin
import name.remal.gradle_plugins.plugins.java.compileOptionalTransitive
import name.remal.gradle_plugins.plugins.kotlin.KotlinAnyPluginId
import name.remal.gradle_plugins.plugins.kotlin.KotlinJvmPluginId
import name.remal.gradle_plugins.plugins.kotlin.KotlinPluginAllOpenPluginId
import name.remal.gradle_plugins.plugins.testing.testTaskName
import name.remal.gradle_plugins.test_source_sets.TestSourceSetContainer
import name.remal.gradle_plugins.test_source_sets.TestSourceSetsPlugin
import name.remal.hierarchy
import name.remal.reflection.ExtendedURLClassLoader.LoadingOrder.THIS_FIRST
import name.remal.tryLoadClass
import name.remal.uncheckedCast
import name.remal.use
import org.gradle.api.Project
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.plugins.ExtensionContainer
import org.gradle.api.plugins.JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME
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.TaskContainer
import org.gradle.api.tasks.testing.Test
import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED
import org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED
import org.gradle.api.tasks.testing.logging.TestLogEvent.STANDARD_ERROR
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.allopen.gradle.AllOpenExtension
import java.io.File
import java.io.IOException
import java.io.OutputStream
import java.io.OutputStreamWriter
import java.io.Writer
import java.lang.ClassLoader.getSystemClassLoader
import java.net.URI
import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.FileVisitResult
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.SimpleFileVisitor
import java.nio.file.attribute.BasicFileAttributes
import java.security.SecureRandom
import java.util.Arrays
import java.util.Collections
import java.util.Random
import java.util.regex.Pattern
import java.util.stream.Collectors
import java.util.stream.Stream
import kotlin.reflect.jvm.javaGetter
import org.gradle.api.Plugin as GradlePlugin

@Plugin(
    id = "name.remal.gradle-plugins",
    description = "Plugin that simplifies Gradle plugins development.",
    tags = ["gradle", "plugins"]
)
@ApplyPlugins(JavaPluginId::class)
@ApplyPluginClasses(CommonSettingsPlugin::class, TestSourceSetsPlugin::class, GenerateSourcesPlugin::class, CrossGradleVersionsChecksPlugin::class)
class GradlePluginsPlugin : BaseReflectiveProjectPlugin() {

    companion object {
        private val executeSimpleAutoTestsCleanupOnlyOnSuccess: Boolean = java.lang.Boolean.TRUE
    }


    @PluginAction(order = -110)
    fun DependencyHandler.`Add gradleApi dependency`(sourceSets: SourceSetContainer) {
        sourceSets.all { sourceSet ->
            add(sourceSet.gradleDependencyConfigurationName, gradleApi())
        }
    }

    @PluginAction(order = -100)
    fun DependencyHandler.`Add gradleTestKit dependency`(testSourceSets: TestSourceSetContainer) {
        testSourceSets.all { testSourceSet ->
            add(testSourceSet.gradleDependencyConfigurationName, gradleTestKit())
        }
    }

    @PluginAction(order = -110)
    @WithPlugins(KotlinJvmPluginId::class)
    fun DependencyHandler.`Add embeddedKotlin dependency`(sourceSets: SourceSetContainer) {
        sourceSets.all { sourceSet ->
            add(sourceSet.gradleDependencyConfigurationName, embeddedKotlin())
        }
    }

    @PluginAction(order = -10)
    @ApplyPluginClasses(TransitiveDependenciesPlugin::class)
    fun ExtensionContainer.`Apply Gradle transitive dependencies excludes`() {
        val transitiveDependencies = this[TransitiveDependenciesExtension::class.java]
        GRADLE_TRANSITIVE_DEPENDENCIES_EXCLUDES.forEach { transitiveDependencies.exclude(it) }
    }

    @PluginActionsGroup(order = Int.MAX_VALUE)
    @WithPlugins(KotlinPluginAllOpenPluginId::class)
    inner class `If 'kotlin-allopen' plugin is applied, register these allopen-annotations` {

        @PluginAction("name.remal.gradle_plugins.dsl.Plugin")
        fun ExtensionContainer.name_remal_gradle_plugins_dsl_Plugin() {
            this[AllOpenExtension::class.java].annotation(Plugin::class.java.name)
        }

        @PluginAction("name.remal.gradle_plugins.dsl.Extension")
        fun ExtensionContainer.name_remal_gradle_plugins_dsl_Extension() {
            this[AllOpenExtension::class.java].annotation(Extension::class.java.name)
        }

        @PluginAction("name.remal.gradle_plugins.dsl.BuildTask")
        fun ExtensionContainer.name_remal_gradle_plugins_dsl_BuildTask() {
            this[AllOpenExtension::class.java].annotation(BuildTask::class.java.name)
        }

    }

    @PluginAction
    @ApplyPluginClasses(GenerateSourcesPlugin::class)
    @Suppress("ComplexMethod", "LongMethod")
    fun `Generate simple auto plugin tests`(
        tasks: TaskContainer,
        sourceSets: SourceSetContainer,
        testSourceSets: TestSourceSetContainer,
        project: Project,
        configurations: ConfigurationContainer,
        dependencies: DependencyHandler,
        extensions: ExtensionContainer
    ) {
        val mainSourceSet = sourceSets.main
        val testSourceSet = testSourceSets.create("simpleAutoPluginTests") { sourceSet ->
            val baseDirsToFilterOut = listOf(
                project.file("src/${sourceSet.name}"),
                project.file("src/${sourceSet.name.fromLowerCamelToLowerHyphen()}")
            )

            fun filter(sources: SourceDirectorySet) {
                sources.setSrcDirs(sources.srcDirs.filter { dir ->
                    baseDirsToFilterOut.all {
                        dir.relativeToOrNull(it).let {
                            it == null || it.invariantSeparatorsPath.startsWith("../")
                        }
                    }
                })
            }

            filter(sourceSet.resources)
            filter(sourceSet.java)
            project.withPlugin(KotlinAnyPluginId) {
                filter(sourceSet.kotlin)
            }
            project.withPlugin(GroovyPluginId) {
                filter(sourceSet.groovy)
            }
        }

        tasks.all(Test::class.java, testSourceSet.testTaskName) {
            it.testLogging {
                it.events(PASSED, FAILED, STANDARD_ERROR)
            }
        }

        val generateTestTask = tasks[GenerateJava::class.java, testSourceSet.generateJavaTaskName]
        generateTestTask.dependsOn(tasks[mainSourceSet.classesTaskName])
        generateTestTask.doSetup {
            val pluginClassNames = mainSourceSet.output.classesDirs.forClassLoader(getSystemClassLoader()) { classLoader ->
                ClassGraph()
                    .overrideClassLoaders(classLoader)
                    .rejectPaths("java/*", "javax/*", "kotlin/*", "groovy/*", "scala/*")
                    .enableClassInfo()
                    .enableAnnotationInfo()
                    .scan().use {
                        return@forClassLoader it.getClassesWithAnnotation(Plugin::class.java.name)
                            .filter(ClassInfo::isStandardClass)
                            .filterNot(ClassInfo::isAbstract)
                            .mapTo(mutableSetOf(), { it.name })
                    }
            }
            if (pluginClassNames.isEmpty()) return@doSetup

            val beforeAnnotationClassName: String
            val afterAnnotationClassName: String
            val testAnnotationClassName: String
            val testClasspath = testSourceSet.compileClasspath
            val testClasspathEntries = testClasspath.toHasEntries()
            if (testClasspathEntries.containsClass("org.testng.annotations.Test")) {
                beforeAnnotationClassName = "org.testng.annotations.BeforeTest"
                afterAnnotationClassName = "org.testng.annotations.AfterTest"
                testAnnotationClassName = "org.testng.annotations.Test"
            } else if (testClasspathEntries.containsClass("org.junit.jupiter.api.Test")) {
                beforeAnnotationClassName = "org.junit.jupiter.api.BeforeEach"
                afterAnnotationClassName = "org.junit.jupiter.api.AfterEach"
                testAnnotationClassName = "org.junit.jupiter.api.Test"
            } else if (testClasspathEntries.containsClass("org.junit.Test")) {
                beforeAnnotationClassName = "org.junit.Before"
                afterAnnotationClassName = "org.junit.After"
                testAnnotationClassName = "org.junit.Test"
            } else {
                logger.info("Neither org.junit.jupiter.api.Test nor org.junit.Test nor org.testng.annotations.Test classes found in classpath {}", testClasspath.files)
                return@doSetup
            }

            pluginClassNames.forEach { pluginClassName ->
                generateTestTask.classFile(pluginClassName.substringBeforeLast('.', ""), pluginClassName.substringAfterLast('.') + "SimpleAutoTest") {
                    val pluginClasspath = (mainSourceSet.output.classesDirs + mainSourceSet.runtimeClasspath).files
                    pluginClasspath.forClassLoader(THIS_FIRST) { classLoader ->
                        val pluginClass = classLoader.tryLoadClass(pluginClassName)
                            ?: return@forClassLoader logger.error("Class {} can't be loaded with classpath {}", pluginClassName, pluginClasspath)
                        if (!GradlePlugin::class.java.isAssignableFrom(pluginClass)) return@forClassLoader logger.error("Class {} isn't assignable from {}", pluginClass, GradlePlugin::class.java)
                        if (pluginClass.hierarchy.any { it.declaredAnnotations.any { it.annotationType.name == DoNotGenerateSimpleTest::class.java.name } }) {
                            return@forClassLoader logger.info("Skip generating simple test for {} (is annotated by @{})", pluginClass, DoNotGenerateSimpleTest::class.java.name)
                        }

                        val pluginInfo: Any = run {
                            val collectorClass = classLoader.loadClass(PluginInfoCollector::class.java.name)
                            val collectorMethod = collectorClass.getMethod(PluginInfoCollector::collect.name, Class::class.java)
                            collectorMethod.invoke(null, pluginClass) ?: return@forClassLoader logger.error("Plugin info can't be retrieved from {}", pluginClass)
                        }

                        val pluginId: String = run {
                            pluginInfo.javaClass.getMethod(PluginInfo::id.javaGetter!!.name).invoke(pluginInfo).uncheckedCast()
                        }
                        if (pluginId.isEmpty()) return@forClassLoader logger.error("Plugin ID is empty for {}", pluginClass)

                        val allRequirePluginIds: Set> = mutableSetOf>().apply {
                            pluginInfo.javaClass.getMethod(PluginInfo::allRequirePluginIds.javaGetter!!.name).invoke(pluginInfo)
                                .uncheckedCast>()
                                .forEach { id ->
                                    add(id.javaClass.getMethod(PluginId::getAllIds.name).invoke(id).uncheckedCast())
                                }

                            val allIds = this.flatten()
                            val hasJavaBasePluginId = JavaBasePluginId.allIds.any { it in allIds }
                            val hasAnyJavaPluginId = JavaPluginId.allIds.any { it in allIds }
                            if (hasJavaBasePluginId && !hasAnyJavaPluginId) {
                                add(JavaPluginId.allIds)
                            }
                        }

                        val additionalGradleScript: String? = run {
                            pluginClass.declaredAnnotations.firstOrNull { it.annotationType.name == SimpleTestAdditionalGradleScript::class.java.name }
                                ?.let { annotation ->
                                    return@run buildString {
                                        append(annotation.annotationType.findMethod(SimpleTestAdditionalGradleScript::value.javaGetter!!.name)?.invoke(annotation)?.uncheckedCast().default())

                                        val resource: String = annotation.annotationType.findMethod(SimpleTestAdditionalGradleScript::resource.javaGetter!!.name)
                                            ?.invoke(annotation)
                                            ?.uncheckedCast()
                                            .default()
                                        if (resource.isNotEmpty()) {
                                            append("\n\n")
                                            pluginClass.getRequiredResourceAsStream(resource).use { inputStream ->
                                                val bytes = inputStream.readAll()
                                                val string = String(bytes, UTF_8)
                                                append(string)
                                            }
                                        }
                                    }
                                }
                            return@run null
                        }

                        val isDeprecationCheckEnabled: Boolean = pluginClass.declaredAnnotations.firstOrNull { it.annotationType.name == SimpleTestDisableDeprecationCheck::class.java.name } == null
                        val isMutableProjectStateCheckEnabled: Boolean = pluginClass.declaredAnnotations.firstOrNull { it.annotationType.name == SimpleTestDisableMutableProjectStateCheck::class.java.name } == null

                        writePackage()
                        writeImport(Pattern::class.java)
                        writeImport(OutputStream::class.java)
                        writeStaticImport(Files::class.java, "*")
                        writeImport(File::class.java)
                        writeImport(Writer::class.java)
                        writeImport(OutputStreamWriter::class.java)
                        writeImport("org.gradle.testkit.runner.GradleRunner")
                        writeImport("org.gradle.testkit.runner.BuildResult")
                        writeImport(URI::class.java)
                        writeImport(List::class.java)
                        writeImport(Collection::class.java)
                        writeImport(LinkedHashSet::class.java)
                        writeImport(Stream::class.java)
                        writeStaticImport(Collectors::class.java, "*")
                        writeImport(GradleVersion::class.java)
                        writeImport(Path::class.java)
                        writeImport(SimpleFileVisitor::class.java)
                        writeImport(FileVisitResult::class.java)
                        writeImport(BasicFileAttributes::class.java)
                        writeImport(IOException::class.java)
                        writeSuppressWarnings()
                        writeBlock("public class $simpleName") {
                            writeln("")
                            writeln("private static final Pattern TRIM_RIGHT = Pattern.compile(\"\\\\s+$\");")
                            writeln("private static final Pattern STACK_TRACE_LINE = Pattern.compile(\"^\\\\s+at \");")

                            writeln("")
                            writeln("private File testProjectDir;")

                            writeln("")
                            writeln("@$testAnnotationClassName")
                            writeBlock("public void simpleAutoPluginTest() throws Throwable") {
                                writeBlock("try (OutputStream outputStream = newOutputStream(testProjectDir.toPath().resolve(\"build.gradle\")))") {
                                    writeBlock("try (Writer writer = new OutputStreamWriter(outputStream, \"${escapeJava(generateTestTask.charset)}\"))") {
                                        val classpath = mutableSetOf().apply {
                                            addAll(testSourceSet.runtimeClasspath)
                                            if (project.isPluginApplied(JavaSettingsPlugin::class.java)) {
                                                addAll(configurations.compileOptionalTransitive)
                                            }

                                            if (isNotEmpty()) {
                                                configurations.detachedConfiguration(
                                                    dependencies.localGroovy(),
                                                    dependencies.gradleApi(),
                                                    dependencies.gradleTestKit(),
                                                    dependencies.embeddedKotlin()
                                                ).files.forEach { remove(it) }
                                            }
                                        }.toSet()

                                        val buildscriptContent = GeneratingGroovyClassStringWriter(classpath = classpath).apply {
                                            val crossGradleVersionsChecks = extensions[CrossGradleVersionsChecksExtension::class.java]

                                            writeImport(GradleVersion::class.java)
                                            writeImport(Collections::class.java)
                                            writeImport(Arrays::class.java)
                                            writeImport(Random::class.java)
                                            writeImport(SecureRandom::class.java)

                                            writeBlock("buildscript") {
                                                writeln("project.ext[\"${escapeJava(BaseReflectiveProjectPlugin::class.java.name + ":simple-auto-test")}\"] = true")
                                                writeln("logger.lifecycle(GradleVersion.current().toString())")
                                                writeBlock("dependencies") {
                                                    crossGradleVersionsChecks.getClasspathItems(classpath).forEach {
                                                        when (it) {
                                                            is FileClasspathItem -> writeln("classpath files(\"${escapeJava(it.file.absolutePath)}\")")
                                                            is NotationClasspathItem -> {
                                                                val notation = it.notation
                                                                    .withVersion("\${GradleVersion.current().version}")
                                                                    .withSnapshotVersion(crossGradleVersionsChecks.useSnapshotVersions)
                                                                writeln("classpath \"${escapeJava(notation.toString())}\"")
                                                            }
                                                        }
                                                    }
                                                }

                                                writeBlock("repositories") {
                                                    writeln("mavenCentral()")
                                                    writeln("gradlePluginPortal()")
                                                    writeln("maven { setUrl(\"${escapeJava(crossGradleVersionsChecks.repositoryUrl)}\") }")
                                                }

                                                writeBlock("[configurations, project.configurations].forEach") {
                                                    writeBlock("it.all") {
                                                        writeBlock("resolutionStrategy") {
                                                            writeln("cacheChangingModulesFor(365, 'DAYS')")
                                                            writeln("cacheDynamicVersionsFor(365, 'DAYS')")
                                                        }
                                                    }
                                                }
                                            }

                                            writeln("group = 'test'")
                                            writeln("version = '1'")

                                            writeln("tasks.create('_defaultEmptyTask')")
                                            writeln("project.setDefaultTasks(['_defaultEmptyTask'])")
                                            writeln("logger.lifecycle(\"Applying tested plugin: ${escapeJava(pluginId)}\")")
                                            writeln("pluginManager.apply('${escapeJava(pluginId)}')")

                                            allRequirePluginIds.forEach { requirePluginIds ->
                                                writeBlock("while (true)") {
                                                    requirePluginIds.forEach {
                                                        writeln("try {")
                                                        writeln("    logger.lifecycle(\"Applying: ${escapeJava(it)}\")")
                                                        writeln("    pluginManager.apply(\"${escapeJava(it)}\")")
                                                        writeln("    break")
                                                        writeln("} catch (UnknownPluginException e) {")
                                                        writeln("    logger.lifecycle(e.toString())")
                                                        writeln("    // do nothing")
                                                        writeln("}")
                                                    }
                                                    writeln("break")
                                                }
                                            }

                                            writeBlock("pluginManager.withPlugin('java')") {
                                                writeln("project.setDefaultTasks(project.getDefaultTasks() + ['build'])")

                                                writeBlock("repositories") {
                                                    writeln("mavenCentral()")
                                                    writeln("gradlePluginPortal()")
                                                    writeln("maven { setUrl(\"${escapeJava(crossGradleVersionsChecks.repositoryUrl)}\") }")
                                                }

                                                val classWriter = GeneratingJavaClassStringWriter(packageName = "pkg", simpleName = "JavaClass").apply {
                                                    writePackage()
                                                    writeBlock("class $simpleName")
                                                }
                                                writeBlock("project.file('src/main/java/${classWriter.relativePath}').with") {
                                                    writeln("parentFile.mkdirs()")
                                                    writeln("write(\"${escapeJava(classWriter.toString())}\", \"UTF-8\")")
                                                }

                                                writeBlock("project.file('src/main/resources/resource.txt').with") {
                                                    writeln("parentFile.mkdirs()")
                                                    writeln("write(\"text\", \"UTF-8\")")
                                                }

                                                writeBlock("tasks.withType(Test)") {
                                                    writeBlock("testLogging") {
                                                        writeln("showExceptions = true")
                                                        writeln("showCauses = true")
                                                        writeln("showStackTraces = true")
                                                        writeln("exceptionFormat = 'FULL'")
                                                        writeln("stackTraceFilters = ['GROOVY']")
                                                    }
                                                }
                                            }

                                            writeBlock("pluginManager.withPlugin('groovy')") {
                                                writeBlock("dependencies") {
                                                    writeln("$IMPLEMENTATION_CONFIGURATION_NAME localGroovy()")
                                                }

                                                val classWriter = GeneratingGroovyClassStringWriter(packageName = "pkg", simpleName = "GroovyClass").apply {
                                                    writePackage()
                                                    writeBlock("class $simpleName")
                                                }
                                                writeBlock("project.file('src/main/groovy/${classWriter.relativePath}').with") {
                                                    writeln("parentFile.mkdirs()")
                                                    writeln("write(\"${escapeJava(classWriter.toString())}\", \"UTF-8\")")
                                                }
                                            }

                                            writeBlock("pluginManager.withPlugin('kotlin')") {
                                                writeBlock("dependencies") {
                                                    val notation = CORRESPONDING_KOTLIN.notation
                                                        .withVersion("\${GradleVersion.current().version}")
                                                        .withSnapshotVersion(crossGradleVersionsChecks.useSnapshotVersions)
                                                    writeln("$IMPLEMENTATION_CONFIGURATION_NAME \"${escapeJava(notation.toString())}\"")
                                                }

                                                val classWriter = GeneratingKotlinClassStringWriter(packageName = "pkg", simpleName = "KotlinClass").apply {
                                                    writePackage()
                                                    writeBlock("class $simpleName")
                                                }
                                                writeBlock("project.file('src/main/kotlin/${classWriter.relativePath}').with") {
                                                    writeln("parentFile.mkdirs()")
                                                    writeln("write(\"${escapeJava(classWriter.toString())}\", \"UTF-8\")")
                                                }
                                            }

                                            writeBlock("pluginManager.withPlugin('maven-publish')") {
                                                writeln("apply plugin: 'java'")
                                                writeBlock("publishing") {
                                                    writeBlock("repositories") {
                                                        writeBlock("maven") {
                                                            writeln("name = 'testMaven'")
                                                            writeln("setUrl(file('.maven-repository'))")
                                                        }
                                                    }
                                                }
                                                writeBlock("afterEvaluate") {
                                                    writeBlock("publishing.publications.forEach") {
                                                        writeln("project.setDefaultTasks(project.getDefaultTasks() + ['publish' + it.name.capitalize() + 'PublicationToTestMavenRepository'])")
                                                    }
                                                }
                                            }

                                            writeBlock("pluginManager.withPlugin('checkstyle')") {
                                                writeBlock("project.file('config/checkstyle/checkstyle.xml').with") {
                                                    writeln("parentFile.mkdirs()")
                                                    val xmlContent = buildString {
                                                        append("")
                                                        append("")
                                                        append("")
                                                    }
                                                    writeln("write(\"${escapeJava(xmlContent)}\", \"UTF-8\")")
                                                }
                                            }

                                            writeBlock("pluginManager.withPlugin('application')") {
                                                writeln("tasks.startScripts.enabled = false")
                                            }

                                            if (additionalGradleScript != null) {
                                                writeln()
                                                writeln(additionalGradleScript)
                                            }
                                        }.toString()

                                        writeln("writer.write(\"${escapeJava(buildscriptContent)}\");")
                                    }
                                }

                                writeln("")
                                writeln("GradleRunner runner = GradleRunner.create()")
                                writeln("    .withProjectDir(testProjectDir)")
                                writeln("    .withArguments(\"--stacktrace\", \"--warning-mode=all\");")

                                writeln("String gradleDistributionsMirror = System.getenv(\"GRADLE_DISTRIBUTIONS_MIRROR\");")
                                writeBlock("if (gradleDistributionsMirror != null && !gradleDistributionsMirror.isEmpty())") {
                                    writeBlock("while (gradleDistributionsMirror.endsWith(\"/\"))") {
                                        writeln("gradleDistributionsMirror = gradleDistributionsMirror.substring(0, gradleDistributionsMirror.length() - 1);")
                                    }
                                    write("URI distributionURI = new URI(gradleDistributionsMirror")
                                    write(" + \"/gradle\"")
                                    write(" + '-' + GradleVersion.current().getVersion()")
                                    write(" + \"-bin.zip\"")
                                    writeln(");")
                                    writeln("runner.withGradleDistribution(distributionURI);")
                                }
                                writeBlock("else") {
                                    writeln("runner.withGradleVersion(GradleVersion.current().getVersion());")
                                }

                                writeln("")
                                writeln("BuildResult buildResult = runner.build();")
                                writeln("String output = buildResult.getOutput();")
                                writeln("List outputLines = Stream.of(output.split(\"\\n\"))")
                                writeln("    .map(it -> TRIM_RIGHT.matcher(it).replaceFirst(\"\"))")
                                writeln("    .filter(it -> !it.isEmpty())")
                                writeln("    .collect(toList());")

                                if (isDeprecationCheckEnabled) {
                                    writeln("")
                                    writeBlock {
                                        val deprecationMessages = arrayOf(
                                            "has been deprecated and is scheduled to be removed in Gradle",
                                            "Deprecated Gradle features were used in this build",
                                            "This is scheduled to be removed in Gradle",
                                            "This will fail with an error in Gradle",
                                            "This behaviour has been deprecated and is scheduled to be removed in Gradle"
                                        )
                                        writeln("Collection deprecations = new LinkedHashSet<>();")
                                        writeln("forEachLine:")
                                        writeBlock("for (int lineIndex = 0; lineIndex < outputLines.size(); ++lineIndex)") {
                                            writeln("String line = outputLines.get(lineIndex);")
                                            writeln("if (!line.contains(\"${deprecationMessages.joinToString("\") && !line.contains(\"")}\")) continue;")
                                            writeBlock("if (line.contains(\"The findbugs plugin has been deprecated. This is scheduled to be removed in Gradle\"))") {
                                                writeln("continue forEachLine;")
                                            }
                                            writeBlock("if (line.contains(\"The DefaultSourceDirectorySet constructor has been deprecated. This is scheduled to be removed in Gradle\"))") {
                                                writeBlock("for (int i = lineIndex + 1; i < outputLines.size(); ++i)") {
                                                    writeln("String stackTraceLine = outputLines.get(i);")
                                                    writeln("if (!STACK_TRACE_LINE.matcher(stackTraceLine).find()) break;")
                                                    writeln("if (stackTraceLine.contains(\"org.jetbrains.kotlin.gradle.plugin.\")) continue forEachLine;")
                                                }
                                            }
                                            writeBlock("if (line.contains(\"Internal API constructor TaskReportContainer(Class, Task) has been deprecated. This is scheduled to be removed in Gradle\"))") {
                                                writeBlock("for (int i = lineIndex + 1; i < outputLines.size(); ++i)") {
                                                    writeln("String stackTraceLine = outputLines.get(i);")
                                                    writeln("if (!STACK_TRACE_LINE.matcher(stackTraceLine).find()) break;")
                                                    writeln("if (stackTraceLine.contains(\"com.github.spotbugs.\")) continue forEachLine;")
                                                }
                                            }
                                            writeln("deprecations.add(line);")
                                        }
                                        writeBlock("if (!deprecations.isEmpty())") {
                                            writeln("StringBuilder sb = new StringBuilder();")
                                            writeln("sb.append(\"Deprecation warnings were found:\");")
                                            writeln("deprecations.forEach(it -> sb.append(\"\\n  * \").append(it));")
                                            writeln("sb.append(\"\\n\\n\").append(output);")
                                            writeln("throw new IllegalStateException(sb.toString());")
                                        }
                                    }
                                }

                                if (isMutableProjectStateCheckEnabled) {
                                    writeln("")
                                    writeBlock {
                                        val mutableProjectStateWarningMessages = arrayOf(
                                            "was resolved without accessing the project in a safe manner",
                                            "This may happen when a configuration is resolved from a thread not managed by Gradle or from a different project"
                                        )
                                        writeln("Collection mutableProjectStateWarnings = new LinkedHashSet<>();")
                                        writeln("forEachLine:")
                                        writeBlock("for (int lineIndex = 0; lineIndex < outputLines.size(); ++lineIndex)") {
                                            writeln("String line = outputLines.get(lineIndex);")
                                            writeln("if (!line.contains(\"${mutableProjectStateWarningMessages.joinToString("\") && !line.contains(\"")}\")) continue;")
                                            writeln("mutableProjectStateWarnings.add(line);")
                                        }
                                        writeBlock("if (!mutableProjectStateWarnings.isEmpty())") {
                                            writeln("StringBuilder sb = new StringBuilder();")
                                            writeln("sb.append(\"Mutable Project State warnings were found:\");")
                                            writeln("mutableProjectStateWarnings.forEach(it -> sb.append(\"\\n  * \").append(it));")
                                            writeln("sb.append(\"\\n\\n\").append(output);")
                                            writeln("throw new IllegalStateException(sb.toString());")
                                        }
                                    }
                                }

                                if (executeSimpleAutoTestsCleanupOnlyOnSuccess) {
                                    writeln("")
                                    writeln("after();")
                                }
                            }

                            writeln("")
                            writeln("@$beforeAnnotationClassName")
                            writeBlock("public void before() throws Throwable") {
                                writeln("String baseTempPath = System.getProperty(\"java.io.tmpdir\");")
                                writeln("if (baseTempPath == null) throw new IllegalStateException(\"'java.io.tmpdir' property is not set\");")
                                writeln("File baseTempDir = new File(baseTempPath).getAbsoluteFile();")
                                writeln("long now = System.currentTimeMillis();")
                                writeln("int counter = 0;")
                                writeBlock("while (true)") {
                                    writeln("File tempDir = new File(baseTempDir, \"gradle-test-project-\" + now + \"-\" + (counter++));")
                                    writeBlock("if (tempDir.mkdirs())") {
                                        writeln("testProjectDir = tempDir;")
                                        writeln("break;")
                                    }
                                }
                            }

                            writeln("")
                            if (!executeSimpleAutoTestsCleanupOnlyOnSuccess) {
                                writeln("@$afterAnnotationClassName")
                            }
                            writeBlock("public void after() throws Throwable") {
                                writeBlock("if (testProjectDir != null && testProjectDir.exists())") {
                                    writeBlock("try") {
                                        writeln("walkFileTree(testProjectDir.toPath(), new SimpleFileVisitor() {")
                                        writeln("    public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {")
                                        writeln("        deleteIfExists(path);")
                                        writeln("        return FileVisitResult.CONTINUE;")
                                        writeln("    }")
                                        writeln("    public FileVisitResult postVisitDirectory(Path path, IOException exc) throws IOException {")
                                        writeln("        deleteIfExists(path);")
                                        writeln("        return FileVisitResult.CONTINUE;")
                                        writeln("    }")
                                        writeln("});")
                                    }
                                    writeBlock("catch (Throwable ignored)") {
                                        writeln("// do nothing")
                                    }
                                }
                                writeln("testProjectDir = null;")
                            }
                        }
                    }
                }
            }
        }
    }


    private fun DependencyHandler.embeddedKotlin() = convention[DependencyHandlerEmbeddedKotlinExtension::class.java].embeddedKotlin()

    private val SourceSet.gradleDependencyConfigurationName: String
        get() {
            if (MAIN_SOURCE_SET_NAME == name) {
                return compileOnlyConfigurationName
            } else {
                return implementationConfigurationName
            }
        }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy