From f11ddb4bd7a0affcd628f9217d6aafbfc8f38d1a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 15 Mar 2022 12:51:01 +0000 Subject: [PATCH] Prevent eager creation of bootWar task Previously, querying the artifact's extension in SinglePublishedArtifact would result in eager creation of the task that creates the artifact. Typically, this is the bootWar task. Instead of querying the extension, this commit reworks SinglePublishedArtifact and its callers to call separate methods for jar and war artifacts so that the extension check is no longer required. Tests have been added to ensure that running help does not trigger any unexpected task creation. The tests' assertions tolerate some variation in behavior that depend on the version of Gradle and whether the configuration cache is enabled. Closes gh-30211 --- .../boot/gradle/plugin/JavaPluginAction.java | 2 +- .../plugin/SinglePublishedArtifact.java | 20 +++++++++---- .../boot/gradle/plugin/WarPluginAction.java | 2 +- ...plicationPluginActionIntegrationTests.java | 28 ++++++++++++++++- .../JavaPluginActionIntegrationTests.java | 28 ++++++++++++++++- .../KotlinPluginActionIntegrationTests.java | 30 ++++++++++++++++++- .../WarPluginActionIntegrationTests.java | 28 ++++++++++++++++- .../boot/gradle/testkit/GradleBuild.java | 6 +++- ...ionTests-taskConfigurationIsAvoided.gradle | 9 ++++++ ...ionTests-taskConfigurationIsAvoided.gradle | 8 +++++ ...ionTests-taskConfigurationIsAvoided.gradle | 9 ++++++ ...ionTests-taskConfigurationIsAvoided.gradle | 8 +++++ 12 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-taskConfigurationIsAvoided.gradle create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests-taskConfigurationIsAvoided.gradle create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests-taskConfigurationIsAvoided.gradle create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests-taskConfigurationIsAvoided.gradle diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java index 8bfc275fe3fc..dc5308f482dc 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java @@ -133,7 +133,7 @@ private void configureBootBuildImageTask(Project project, TaskProvider private void configureArtifactPublication(TaskProvider bootJar) { LazyPublishArtifact artifact = new LazyPublishArtifact(bootJar); - this.singlePublishedArtifact.addCandidate(artifact); + this.singlePublishedArtifact.addJarCandidate(artifact); } private void configureBootRunTask(Project project) { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SinglePublishedArtifact.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SinglePublishedArtifact.java index 31d13fe6e72a..ab18a059a7ee 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SinglePublishedArtifact.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SinglePublishedArtifact.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,14 +38,22 @@ final class SinglePublishedArtifact implements Buildable { this.artifacts = artifacts; } - void addCandidate(PublishArtifact candidate) { - if (this.currentArtifact == null || "war".equals(candidate.getExtension())) { - this.artifacts.remove(this.currentArtifact); - this.artifacts.add(candidate); - this.currentArtifact = candidate; + void addWarCandidate(PublishArtifact candidate) { + add(candidate); + } + + void addJarCandidate(PublishArtifact candidate) { + if (this.currentArtifact == null) { + add(candidate); } } + private void add(PublishArtifact artifact) { + this.artifacts.remove(this.currentArtifact); + this.artifacts.add(artifact); + this.currentArtifact = artifact; + } + @Override public TaskDependency getBuildDependencies() { return this.artifacts.getBuildDependencies(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java index 7879ae728890..38199a15fab9 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java @@ -108,7 +108,7 @@ private void configureBootBuildImageTask(Project project, TaskProvider private void configureArtifactPublication(TaskProvider bootWar) { LazyPublishArtifact artifact = new LazyPublishArtifact(bootWar); - this.singlePublishedArtifact.addCandidate(artifact); + this.singlePublishedArtifact.addWarCandidate(artifact); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.java index 5a595258c069..4492ebf04d1e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,19 +16,25 @@ package org.springframework.boot.gradle.plugin; +import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.StringReader; import java.util.ArrayList; import java.util.Enumeration; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.function.Consumer; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.TaskOutcome; +import org.gradle.util.GradleVersion; import org.junit.jupiter.api.TestTemplate; import org.springframework.boot.gradle.junit.GradleCompatibility; @@ -154,6 +160,26 @@ void scriptsHaveCorrectPermissions() throws IOException { }); } + @TestTemplate + void taskConfigurationIsAvoided() throws IOException { + BuildResult result = this.gradleBuild.build("help"); + String output = result.getOutput(); + BufferedReader reader = new BufferedReader(new StringReader(output)); + String line; + Set configured = new HashSet<>(); + while ((line = reader.readLine()) != null) { + if (line.startsWith("Configuring :")) { + configured.add(line.substring("Configuring :".length())); + } + } + if (GradleVersion.version(this.gradleBuild.getGradleVersion()).compareTo(GradleVersion.version("7.3.3")) < 0) { + assertThat(configured).containsExactly("help"); + } + else { + assertThat(configured).containsExactlyInAnyOrder("help", "clean"); + } + } + private List zipEntryNames(File distribution) throws IOException { List entryNames = new ArrayList<>(); try (ZipFile zipFile = new ZipFile(distribution)) { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java index 9633fc18303c..ca6ad16fd2f0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,18 @@ package org.springframework.boot.gradle.plugin; +import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.StringReader; +import java.util.HashSet; +import java.util.Set; import java.util.jar.JarOutputStream; import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.TaskOutcome; +import org.gradle.util.GradleVersion; import org.junit.jupiter.api.TestTemplate; import org.springframework.boot.gradle.junit.GradleCompatibility; @@ -159,6 +164,27 @@ void productionRuntimeClasspathIsConfiguredWithResolvabilityAndConsumabilityThat assertThat(productionRuntime).contains("canBeConsumed: false"); } + @TestTemplate + void taskConfigurationIsAvoided() throws IOException { + BuildResult result = this.gradleBuild.build("help"); + String output = result.getOutput(); + BufferedReader reader = new BufferedReader(new StringReader(output)); + String line; + Set configured = new HashSet<>(); + while ((line = reader.readLine()) != null) { + if (line.startsWith("Configuring :")) { + configured.add(line.substring("Configuring :".length())); + } + } + if (!this.gradleBuild.isConfigurationCache() && GradleVersion.version(this.gradleBuild.getGradleVersion()) + .compareTo(GradleVersion.version("7.3.3")) < 0) { + assertThat(configured).containsExactly("help"); + } + else { + assertThat(configured).containsExactlyInAnyOrder("help", "clean"); + } + } + private void createMinimalMainSource() throws IOException { File examplePackage = new File(this.gradleBuild.getProjectDir(), "src/main/java/com/example"); examplePackage.mkdirs(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests.java index 4f3a0732e35b..e7d6205dd407 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,14 @@ package org.springframework.boot.gradle.plugin; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.HashSet; +import java.util.Set; + +import org.gradle.testkit.runner.BuildResult; +import org.gradle.util.GradleVersion; import org.junit.jupiter.api.TestTemplate; import org.springframework.boot.gradle.junit.GradleCompatibility; @@ -57,4 +65,24 @@ void kotlinCompileTasksCanOverrideDefaultJavaParametersFlag() { .contains("compileKotlin java parameters: false").contains("compileTestKotlin java parameters: false"); } + @TestTemplate + void taskConfigurationIsAvoided() throws IOException { + BuildResult result = this.gradleBuild.build("help"); + String output = result.getOutput(); + BufferedReader reader = new BufferedReader(new StringReader(output)); + String line; + Set configured = new HashSet<>(); + while ((line = reader.readLine()) != null) { + if (line.startsWith("Configuring :")) { + configured.add(line.substring("Configuring :".length())); + } + } + if (GradleVersion.version(this.gradleBuild.getGradleVersion()).compareTo(GradleVersion.version("7.3.3")) < 0) { + assertThat(configured).containsExactly("help"); + } + else { + assertThat(configured).containsExactlyInAnyOrder("help", "clean", "compileKotlin", "compileTestKotlin"); + } + } + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.java index 73e7cda463f0..162b6a57128f 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,16 @@ package org.springframework.boot.gradle.plugin; +import java.io.BufferedReader; import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.util.HashSet; +import java.util.Set; import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.TaskOutcome; +import org.gradle.util.GradleVersion; import org.junit.jupiter.api.TestTemplate; import org.springframework.boot.gradle.junit.GradleCompatibility; @@ -67,4 +73,24 @@ void errorMessageIsHelpfulWhenMainClassCannotBeResolved() { assertThat(result.getOutput()).contains("Main class name has not been configured and it could not be resolved"); } + @TestTemplate + void taskConfigurationIsAvoided() throws IOException { + BuildResult result = this.gradleBuild.build("help"); + String output = result.getOutput(); + BufferedReader reader = new BufferedReader(new StringReader(output)); + String line; + Set configured = new HashSet<>(); + while ((line = reader.readLine()) != null) { + if (line.startsWith("Configuring :")) { + configured.add(line.substring("Configuring :".length())); + } + } + if (GradleVersion.version(this.gradleBuild.getGradleVersion()).compareTo(GradleVersion.version("7.3.3")) < 0) { + assertThat(configured).containsExactly("help"); + } + else { + assertThat(configured).containsExactlyInAnyOrder("help", "clean"); + } + } + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java index ddc6c0465838..d73f5857d4de 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -150,6 +150,10 @@ public GradleBuild configurationCache() { return this; } + public boolean isConfigurationCache() { + return this.configurationCache; + } + public GradleBuild scriptProperty(String key, String value) { this.scriptProperties.put(key, value); return this; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-taskConfigurationIsAvoided.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-taskConfigurationIsAvoided.gradle new file mode 100644 index 000000000000..5e38257d3e83 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests-taskConfigurationIsAvoided.gradle @@ -0,0 +1,9 @@ +plugins { + id 'org.springframework.boot' version '{version}' + id 'java' + id 'application' +} + +tasks.configureEach { + println "Configuring ${it.path}" +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests-taskConfigurationIsAvoided.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests-taskConfigurationIsAvoided.gradle new file mode 100644 index 000000000000..152dcf13e1d3 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests-taskConfigurationIsAvoided.gradle @@ -0,0 +1,8 @@ +plugins { + id 'org.springframework.boot' version '{version}' + id 'java' +} + +tasks.configureEach { + println "Configuring ${it.path}" +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests-taskConfigurationIsAvoided.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests-taskConfigurationIsAvoided.gradle new file mode 100644 index 000000000000..57b997af8295 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests-taskConfigurationIsAvoided.gradle @@ -0,0 +1,9 @@ +plugins { + id 'org.springframework.boot' version '{version}' +} + +apply plugin: 'org.jetbrains.kotlin.jvm' + +tasks.configureEach { + println "Configuring ${it.path}" +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests-taskConfigurationIsAvoided.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests-taskConfigurationIsAvoided.gradle new file mode 100644 index 000000000000..93d99ceb1148 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests-taskConfigurationIsAvoided.gradle @@ -0,0 +1,8 @@ +plugins { + id 'org.springframework.boot' version '{version}' + id 'war' +} + +tasks.configureEach { + println "Configuring ${it.path}" +}