diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java index 8cbb99bc6c..468dd12303 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/dsl/SpringBootExtension.java @@ -27,6 +27,7 @@ import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.bundling.Jar; import org.springframework.boot.gradle.tasks.buildinfo.BuildInfo; +import org.springframework.boot.gradle.tasks.buildinfo.BuildInfoProperties; /** * Entry point to Spring Boot's Gradle DSL. @@ -76,8 +77,12 @@ public class SpringBootExtension { this.project.getPlugins().withType(JavaPlugin.class, plugin -> { this.project.getTasks().getByName(JavaPlugin.CLASSES_TASK_NAME) .dependsOn(bootBuildInfo); - bootBuildInfo.getConventionMapping().map("projectArtifact", - (Callable) () -> determineArtifactBaseName()); + this.project.afterEvaluate(evaluated -> { + BuildInfoProperties properties = bootBuildInfo.getProperties(); + if (properties.getArtifact() == null) { + properties.setArtifact(determineArtifactBaseName()); + } + }); bootBuildInfo.getConventionMapping() .map("destinationDir", (Callable) () -> new File( diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java index 1f4239e702..273f7600e7 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java @@ -22,6 +22,8 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import org.gradle.api.Action; +import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.internal.ConventionTask; import org.gradle.api.tasks.Input; @@ -34,92 +36,77 @@ import org.springframework.boot.loader.tools.BuildPropertiesWriter.ProjectDetail /** * {@link Task} for generating a {@code build-info.properties} file from a - * {@code Project}. The {@link #setDestinationDir destination dir} and - * {@link #setProjectArtifact project artifact} must be configured before execution. + * {@code Project}. * * @author Andy Wilkinson */ public class BuildInfo extends ConventionTask { + private final BuildInfoProperties properties = new BuildInfoProperties(getProject()); + private File destinationDir; - private String projectGroup = getProject().getGroup().toString(); - - private String projectArtifact; - - private String projectVersion = getProject().getVersion().toString(); - - private String projectName = getProject().getName(); - - private Map additionalProperties = new HashMap<>(); - + /** + * Generates the {@code build-info.properties} file in the configured + * {@link #setDestinationDir(File) destination}. + */ @TaskAction public void generateBuildProperties() { try { new BuildPropertiesWriter( new File(getDestinationDir(), "build-info.properties")) - .writeBuildProperties(new ProjectDetails(this.projectGroup, - getProjectArtifact(), this.projectVersion, - this.projectName, - coerceToStringValues(this.additionalProperties))); + .writeBuildProperties(new ProjectDetails( + this.properties.getGroup(), + this.properties.getArtifact() == null ? "unspecified" + : this.properties.getArtifact(), + this.properties.getVersion(), + this.properties.getName(), coerceToStringValues( + this.properties.getAdditional()))); } catch (IOException ex) { throw new TaskExecutionException(this, ex); } } - @Input - public String getProjectGroup() { - return this.projectGroup; - } - - public void setProjectGroup(String projectGroup) { - this.projectGroup = projectGroup; - } - - @Input - public String getProjectArtifact() { - return this.projectArtifact; - } - - public void setProjectArtifact(String projectArtifact) { - this.projectArtifact = projectArtifact; - } - - @Input - public String getProjectVersion() { - return this.projectVersion; - } - - public void setProjectVersion(String projectVersion) { - this.projectVersion = projectVersion; - } - - @Input - public String getProjectName() { - return this.projectName; - } - - public void setProjectName(String projectName) { - this.projectName = projectName; - } - + /** + * Returns the directory to which the {@code build-info.properties} file will be + * written. Defaults to the {@link Project#getBuildDir() Project's build directory}. + * + * @return the destination directory + */ @OutputDirectory public File getDestinationDir() { - return this.destinationDir; + return this.destinationDir != null ? this.destinationDir + : getProject().getBuildDir(); } + /** + * Sets the directory to which the {@code build-info.properties} file will be written. + * + * @param destinationDir the destination directory + */ public void setDestinationDir(File destinationDir) { this.destinationDir = destinationDir; } + /** + * Returns the {@link BuildInfoProperties properties} that will be included in the + * {@code build-info.properties} file. + * + * @return the properties + */ @Input - public Map getAdditionalProperties() { - return this.additionalProperties; + public BuildInfoProperties getProperties() { + return this.properties; } - public void setAdditionalProperties(Map additionalProperties) { - this.additionalProperties = additionalProperties; + /** + * Executes the given {@code action} on the {@link #getProperties()} properties. + * + * @param action the action + */ + public void properties(Action action) { + action.execute(this.properties); } private Map coerceToStringValues(Map input) { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoProperties.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoProperties.java new file mode 100644 index 0000000000..6cbc243ef0 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoProperties.java @@ -0,0 +1,212 @@ +/* + * Copyright 2012-2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.gradle.tasks.buildinfo; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.gradle.api.Project; + +/** + * The properties that are written into the {@code build-info.properties} file. + * + * @author Andy Wilkinson + */ +public class BuildInfoProperties implements Serializable { + + private final transient Project project; + + private String group; + + private String artifact; + + private String version; + + private String name; + + private Map additionalProperties = new HashMap<>(); + + BuildInfoProperties(Project project) { + this.project = project; + } + + /** + * Returns the value used for the {@code build.group} property. Defaults to the + * {@link Project#getGroup() Project's group}. + * + * @return the group + */ + public String getGroup() { + return this.group != null ? this.group : this.project.getGroup().toString(); + } + + /** + * Sets the value used for the {@code build.group} property. + * + * @param group the group name + */ + public void setGroup(String group) { + this.group = group; + } + + /** + * Returns the value used for the {@code build.artifact} property. + * + * @return the artifact + */ + public String getArtifact() { + return this.artifact; + } + + /** + * Sets the value used for the {@code build.artifact} property. + * + * @param artifact the artifact + */ + public void setArtifact(String artifact) { + this.artifact = artifact; + } + + /** + * Returns the value used for the {@code build.version} property. Defaults to the + * {@link Project#getVersion() Project's version}. + * + * @return the version + */ + public String getVersion() { + return this.version != null ? this.version : this.project.getVersion().toString(); + } + + /** + * Sets the value used for the {@code build.version} property. + * + * @param version the version + */ + public void setVersion(String version) { + this.version = version; + } + + /** + * Returns the value used for the {@code build.name} property. Defaults to the + * {@link Project#getDisplayName() Project's display name}. + * + * @return the name + */ + public String getName() { + return this.name != null ? this.name : this.project.getName(); + } + + /** + * Sets the value used for the {@code build.name} property. + * + * @param name the name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the additional properties that will be included. When written, the name of + * each additional property is prefixed with {@code build.}. + * + * @return the additional properties + */ + public Map getAdditional() { + return this.additionalProperties; + } + + /** + * Sets the additional properties that will be included. When written, the name of + * each additional property is prefixed with {@code build.}. + * + * @param additionalProperties the additional properties + */ + public void setAdditional(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.additionalProperties == null) ? 0 + : this.additionalProperties.hashCode()); + result = prime * result + + ((this.artifact == null) ? 0 : this.artifact.hashCode()); + result = prime * result + ((this.group == null) ? 0 : this.group.hashCode()); + result = prime * result + ((this.name == null) ? 0 : this.name.hashCode()); + result = prime * result + ((this.version == null) ? 0 : this.version.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + BuildInfoProperties other = (BuildInfoProperties) obj; + if (this.additionalProperties == null) { + if (other.additionalProperties != null) { + return false; + } + } + else if (!this.additionalProperties.equals(other.additionalProperties)) { + return false; + } + if (this.artifact == null) { + if (other.artifact != null) { + return false; + } + } + else if (!this.artifact.equals(other.artifact)) { + return false; + } + if (this.group == null) { + if (other.group != null) { + return false; + } + } + else if (!this.group.equals(other.group)) { + return false; + } + if (this.name == null) { + if (other.name != null) { + return false; + } + } + else if (!this.name.equals(other.name)) { + return false; + } + if (this.version == null) { + if (other.version != null) { + return false; + } + } + else if (!this.version.equals(other.version)) { + return false; + } + return true; + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java index 41603256d1..fac2498efa 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java @@ -16,6 +16,11 @@ package org.springframework.boot.gradle.tasks.buildinfo; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Properties; + import org.gradle.testkit.runner.TaskOutcome; import org.junit.Rule; import org.junit.Test; @@ -34,10 +39,30 @@ public class BuildInfoIntegrationTests { @Rule public final GradleBuild gradleBuild = new GradleBuild(); + @Test + public void defaultValues() { + assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome()) + .isEqualTo(TaskOutcome.SUCCESS); + Properties buildInfoProperties = buildInfoProperties(); + assertThat(buildInfoProperties).containsKey("build.time"); + assertThat(buildInfoProperties).containsEntry("build.artifact", "unspecified"); + assertThat(buildInfoProperties).containsEntry("build.group", ""); + assertThat(buildInfoProperties).containsEntry("build.name", + this.gradleBuild.getProjectDir().getName()); + assertThat(buildInfoProperties).containsEntry("build.version", "unspecified"); + } + @Test public void basicExecution() { assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome()) .isEqualTo(TaskOutcome.SUCCESS); + Properties buildInfoProperties = buildInfoProperties(); + assertThat(buildInfoProperties).containsKey("build.time"); + assertThat(buildInfoProperties).containsEntry("build.artifact", "foo"); + assertThat(buildInfoProperties).containsEntry("build.group", "foo"); + assertThat(buildInfoProperties).containsEntry("build.additional", "foo"); + assertThat(buildInfoProperties).containsEntry("build.name", "foo"); + assertThat(buildInfoProperties).containsEntry("build.version", "1.0"); } @Test @@ -55,34 +80,48 @@ public class BuildInfoIntegrationTests { @Test public void notUpToDateWhenProjectArtifactChanges() { - notUpToDateWithChangeToProperty("buildInfoProjectArtifact"); + notUpToDateWithChangeToProperty("buildInfoArtifact"); } @Test public void notUpToDateWhenProjectGroupChanges() { - notUpToDateWithChangeToProperty("buildInfoProjectGroup"); + notUpToDateWithChangeToProperty("buildInfoGroup"); } @Test public void notUpToDateWhenProjectVersionChanges() { - notUpToDateWithChangeToProperty("buildInfoProjectVersion"); + notUpToDateWithChangeToProperty("buildInfoVersion"); } @Test public void notUpToDateWhenProjectNameChanges() { - notUpToDateWithChangeToProperty("buildInfoProjectName"); + notUpToDateWithChangeToProperty("buildInfoName"); } @Test public void notUpToDateWhenAdditionalPropertyChanges() { - notUpToDateWithChangeToProperty("buildInfoAdditionalProperty"); + notUpToDateWithChangeToProperty("buildInfoAdditional"); } private void notUpToDateWithChangeToProperty(String name) { - assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome()) - .isEqualTo(TaskOutcome.SUCCESS); + assertThat(this.gradleBuild.build("buildInfo", "--stacktrace").task(":buildInfo") + .getOutcome()).isEqualTo(TaskOutcome.SUCCESS); assertThat(this.gradleBuild.build("buildInfo", "-P" + name + "=changed") .task(":buildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); } + private Properties buildInfoProperties() { + File file = new File(this.gradleBuild.getProjectDir(), + "build/build-info.properties"); + assertThat(file).isFile(); + Properties properties = new Properties(); + try (FileReader reader = new FileReader(file)) { + properties.load(reader); + return properties; + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoTests.java new file mode 100644 index 0000000000..f459919e61 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoTests.java @@ -0,0 +1,136 @@ +/* + * Copyright 2012-2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.gradle.tasks.buildinfo; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Properties; + +import org.gradle.api.Project; +import org.gradle.testfixtures.ProjectBuilder; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link BuildInfo}. + * + * @author Andy Wilkinson + */ +public class BuildInfoTests { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void basicExecution() { + Properties properties = buildInfoProperties(createTask(createProject("test"))); + assertThat(properties).containsKey("build.time"); + assertThat(properties).containsEntry("build.artifact", "unspecified"); + assertThat(properties).containsEntry("build.group", ""); + assertThat(properties).containsEntry("build.name", "test"); + assertThat(properties).containsEntry("build.version", "unspecified"); + } + + @Test + public void customArtifactIsReflectedInProperties() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().setArtifact("custom"); + assertThat(buildInfoProperties(task)).containsEntry("build.artifact", "custom"); + } + + @Test + public void projectGroupIsReflectedInProperties() { + BuildInfo task = createTask(createProject("test")); + task.getProject().setGroup("com.example"); + assertThat(buildInfoProperties(task)).containsEntry("build.group", "com.example"); + } + + @Test + public void customGroupIsReflectedInProperties() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().setGroup("com.example"); + assertThat(buildInfoProperties(task)).containsEntry("build.group", "com.example"); + } + + @Test + public void customNameIsReflectedInProperties() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().setName("Example"); + assertThat(buildInfoProperties(task)).containsEntry("build.name", "Example"); + } + + @Test + public void projectVersionIsReflectedInProperties() { + BuildInfo task = createTask(createProject("test")); + task.getProject().setVersion("1.2.3"); + assertThat(buildInfoProperties(task)).containsEntry("build.version", "1.2.3"); + } + + @Test + public void customVersionIsReflectedInProperties() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().setVersion("2.3.4"); + assertThat(buildInfoProperties(task)).containsEntry("build.version", "2.3.4"); + } + + @Test + public void additionalPropertiesAreReflectedInProperties() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().getAdditional().put("a", "alpha"); + task.getProperties().getAdditional().put("b", "bravo"); + assertThat(buildInfoProperties(task)).containsEntry("build.a", "alpha"); + assertThat(buildInfoProperties(task)).containsEntry("build.b", "bravo"); + } + + private Project createProject(String projectName) { + try { + File projectDir = this.temp.newFolder(projectName); + return ProjectBuilder.builder().withProjectDir(projectDir) + .withName(projectName).build(); + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + private BuildInfo createTask(Project project) { + return project.getTasks().create("testBuildInfo", BuildInfo.class); + } + + private Properties buildInfoProperties(BuildInfo task) { + task.generateBuildProperties(); + return buildInfoProperties( + new File(task.getDestinationDir(), "build-info.properties")); + } + + private Properties buildInfoProperties(File file) { + assertThat(file).isFile(); + Properties properties = new Properties(); + try (FileReader reader = new FileReader(file)) { + properties.load(reader); + return properties; + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-additionalProperties.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-additionalProperties.gradle index 93162070be..4335ef8445 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-additionalProperties.gradle +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests-additionalProperties.gradle @@ -12,6 +12,10 @@ version = '1.0' springBoot { buildInfo { - additionalProperties 'a': 'alpha', 'b': 'bravo' + properties { + additionalProperties = [ + 'a': 'alpha', 'b': 'bravo' + ] + } } } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests-defaultValues.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests-defaultValues.gradle new file mode 100644 index 0000000000..2fe8266de8 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests-defaultValues.gradle @@ -0,0 +1,11 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +def property(String name, Object defaultValue) { + project.hasProperty(name) ? project.getProperty(name) : defaultValue +} + +task buildInfo(type: org.springframework.boot.gradle.tasks.buildinfo.BuildInfo) diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.gradle index 23db1b2d8b..26ce586d5b 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.gradle +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.gradle @@ -10,9 +10,11 @@ def property(String name, Object defaultValue) { task buildInfo(type: org.springframework.boot.gradle.tasks.buildinfo.BuildInfo) { destinationDir file(property('buildInfoDestinationDir', project.buildDir)) - projectArtifact property('buildInfoProjectArtifact', 'foo') - projectVersion property('buildInfoProjectVersion', '1.0') - projectGroup property('buildInfoProjectGroup', 'foo') - projectName property('buildInfoProjectName', 'foo') - additionalProperties 'additional': property('buildInfoAdditionalProperty', 'foo') + properties { + artifact = property('buildInfoArtifact', 'foo') + version = property('buildInfoVersion', '1.0') + group = property('buildInfoGroup', 'foo') + name = property('buildInfoName', 'foo') + additional = ['additional': property('buildInfoAdditional', 'foo')] + } }