Andy Wilkinson 1240c59482 Only configure plugin classpath where it's needed
When spring-boot-gradle-plugin is using GradleRunner, it needs to be
configured with a custom plugin classpath to account for the fact
that our Gradle plugin is on the classpath of the system classloader
but some of the other plugins would only be available on a
Gradle-created classloader. This imbalance cause class loading
problems as code in spring-boot-gradle-plugin can't see types at
runtime that are only available on the Gradle-created classloader.

To overcome this, we need to configure the GradleRunner with a custom
plugin classpath that contains both spring-boot-gradle-plugin and all
of the other plugins that are used in its various integration tests.
Previously, this was done in GradleBuild that's used by both
spring-boot-gradle-plugin and spring-boot-image-tests. This caused
a problem as spring-boot-image-tests does not have the
above-described problem and trying to correct it did not work leaving
tests that use spring-boot-gradle-plugin unable to see other plugins
such that the native image plugin.

This commit reworks the customization of the plugin classpath so that
it's only done in spring-boot-gradle-plugin's integration tests.

Closes gh-42338
2024-09-18 14:53:54 +01:00

253 lines
8.7 KiB
Groovy

import org.gradle.plugins.ide.eclipse.EclipsePlugin
import org.gradle.plugins.ide.eclipse.model.Classpath
import org.gradle.plugins.ide.eclipse.model.Library
plugins {
id "java-gradle-plugin"
id "maven-publish"
id "org.asciidoctor.jvm.convert"
id "org.springframework.boot.conventions"
id "org.springframework.boot.docker-test"
id "org.springframework.boot.maven-repository"
id "org.springframework.boot.optional-dependencies"
}
description = "Spring Boot Gradle Plugins"
configurations {
documentation
"testCompileClasspath" {
// Downgrade SLF4J is required for tests to run in Eclipse
resolutionStrategy.force("org.slf4j:slf4j-api:1.7.36")
}
modernGradleRuntimeClasspath {
extendsFrom runtimeClasspath
canBeConsumed = false
canBeResolved = true
}
modernGradleRuntimeElements {
extendsFrom configurations.implementation, configurations.runtimeOnly
canBeConsumed = true
canBeResolved = false
attributes {
attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category, Category.LIBRARY))
attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling, Bundling.EXTERNAL))
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements, LibraryElements.JAR))
attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, Usage.JAVA_RUNTIME))
attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, project.objects.named(GradlePluginApiVersion, "8.7"))
}
outgoing.artifacts.addAll(configurations.runtimeElements.outgoing.artifacts)
}
runtimeElements {
attributes {
attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, project.objects.named(GradlePluginApiVersion, "7.5"))
}
}
all { configuration ->
if (configuration.name == 'modernGradleRuntimeClasspath') {
return
}
resolutionStrategy {
eachDependency { dependency ->
// Downgrade Jackson as Gradle cannot cope with 2.15.0's multi-version
// jar files with bytecode in META-INF/versions/19
if (dependency.requested.group.startsWith("com.fasterxml.jackson")) {
dependency.useVersion("2.14.2")
}
// Downgrade Spring Framework as Gradle cannot cope with 6.1.0-M1's
// multi-version jar files with bytecode in META-INF/versions/21
if (dependency.requested.group.equals("org.springframework")) {
dependency.useVersion("$springFramework60xVersion")
}
// We manage the version of commons-compress here rather than
// in spring-boot-parent to minimize conflicts with Testcontainers
if (dependency.requested.group.equals("org.apache.commons")
&& dependency.requested.name.equals("commons-compress")) {
dependency.useVersion("$commonsCompressVersion")
}
// Downgrade Testcontainers for compatibility with the managed
// version of Commons Compress.
if (dependency.requested.group.equals("org.testcontainers")) {
dependency.useVersion("1.19.3")
}
}
}
}
}
components.java.addVariantsFromConfiguration(configurations.modernGradleRuntimeElements) {
mapToMavenScope("runtime")
}
dependencies {
asciidoctorExtensions("io.spring.asciidoctor:spring-asciidoctor-extensions-section-ids")
dockerTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-gradle-test-support"))
dockerTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support-docker"))
dockerTestImplementation(gradleTestKit())
dockerTestImplementation("org.assertj:assertj-core")
dockerTestImplementation("org.junit.jupiter:junit-jupiter")
dockerTestImplementation("org.testcontainers:junit-jupiter")
dockerTestImplementation("org.testcontainers:testcontainers")
implementation(project(":spring-boot-project:spring-boot-tools:spring-boot-buildpack-platform"))
implementation(project(":spring-boot-project:spring-boot-tools:spring-boot-loader-tools"))
implementation("io.spring.gradle:dependency-management-plugin")
implementation("org.apache.commons:commons-compress:$commonsCompressVersion")
implementation("org.springframework:spring-core")
optional("org.graalvm.buildtools:native-gradle-plugin")
optional("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") {
exclude(group: "commons-logging", module: "commons-logging")
}
testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-gradle-test-support"))
testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support"))
testImplementation("com.fasterxml.jackson.core:jackson-databind")
testImplementation("com.fasterxml.jackson.module:jackson-module-parameter-names")
testImplementation("com.tngtech.archunit:archunit-junit5:0.22.0")
testImplementation("net.java.dev.jna:jna-platform")
testImplementation("org.apache.commons:commons-compress")
testImplementation("org.apache.httpcomponents.client5:httpclient5")
testImplementation("org.assertj:assertj-core")
testImplementation("org.graalvm.buildtools:native-gradle-plugin")
testImplementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
testImplementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:$kotlinVersion")
testImplementation("org.jetbrains.kotlin:kotlin-compiler-runner:$kotlinVersion")
testImplementation("org.jetbrains.kotlin:kotlin-daemon-client:$kotlinVersion")
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.mockito:mockito-core")
testImplementation("org.tomlj:tomlj:1.0.0")
}
gradlePlugin {
plugins {
springBootPlugin {
id = "org.springframework.boot"
displayName = "Spring Boot Gradle Plugin"
description = "Spring Boot Gradle Plugin"
implementationClass = "org.springframework.boot.gradle.plugin.SpringBootPlugin"
}
springBootAotPlugin {
id = "org.springframework.boot.aot"
displayName = "Spring Boot AOT Gradle Plugin"
description = "Spring Boot AOT Gradle Plugin"
implementationClass = "org.springframework.boot.gradle.plugin.SpringBootAotPlugin"
}
}
}
task preparePluginValidationClasses(type: Copy) {
destinationDir = layout.buildDirectory.dir("classes/java/pluginValidation").get().asFile
from(sourceSets.main.output.classesDirs) {
exclude "**/CreateBootStartScripts.class"
}
}
validatePlugins {
classes.setFrom preparePluginValidationClasses
enableStricterValidation = true
}
task dependencyVersions(type: org.springframework.boot.build.constraints.ExtractVersionConstraints) {
enforcedPlatform(":spring-boot-project:spring-boot-dependencies")
}
tasks.withType(org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask) {
dependsOn dependencyVersions
inputs.dir('src/docs/gradle').withPathSensitivity(PathSensitivity.RELATIVE).withPropertyName('buildScripts')
doFirst {
attributes "dependency-management-plugin-version": dependencyVersions.versionConstraints["io.spring.gradle:dependency-management-plugin"]
}
}
tasks.named('test') {
inputs.dir('src/docs/gradle').withPathSensitivity(PathSensitivity.RELATIVE).withPropertyName('buildScripts')
}
asciidoctor {
sources {
include "index.adoc"
}
}
task asciidoctorPdf(type: org.asciidoctor.gradle.jvm.AsciidoctorTask) {
sources {
include "index.adoc"
}
}
tasks.withType(org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask) {
attributes "native-build-tools-version": nativeBuildToolsVersion
}
javadoc {
options {
author = true
docTitle = "Spring Boot Gradle Plugin ${project.version} API"
encoding = "UTF-8"
memberLevel = "protected"
outputLevel = "quiet"
splitIndex = true
use = true
windowTitle = "Spring Boot Gradle Plugin ${project.version} API"
links "https://docs.gradle.org/$gradle.gradleVersion/javadoc/"
links "https://docs.oracle.com/en/java/javase/17/docs/api/"
}
}
task zip(type: Zip) {
dependsOn asciidoctor, asciidoctorPdf
duplicatesStrategy "fail"
from(asciidoctorPdf.outputDir) {
into "reference/pdf"
rename "index.pdf", "${project.name}-reference.pdf"
}
from(asciidoctor.outputDir) {
into "reference/htmlsingle"
}
from(javadoc) {
into "api"
}
}
artifacts {
"documentation" zip
}
toolchain {
maximumCompatibleJavaVersion = JavaLanguageVersion.of(20)
}
publishing {
publications.matching { it.name == 'pluginMaven' }.configureEach {
versionMapping {
allVariants {
fromResolutionOf(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME)
}
}
versionMapping {
variant(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, project.objects.named(GradlePluginApiVersion, "8.7")) {
fromResolutionOf("modernGradleRuntimeClasspath")
}
}
}
}
plugins.withType(EclipsePlugin) {
eclipse {
classpath.file { merger ->
merger.whenMerged { content ->
if (content instanceof Classpath) {
content.entries.each { entry ->
if (entry instanceof Library && (entry.path.contains("gradle-api-") || entry.path.contains("groovy-"))) {
entry.entryAttributes.remove("test")
}
}
}
}
}
}
}