diff --git a/buildSrc/src/main/java/org/springframework/boot/build/JavaConventions.java b/buildSrc/src/main/java/org/springframework/boot/build/JavaConventions.java index e096d9a4afe..5a9fecc81aa 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/JavaConventions.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/JavaConventions.java @@ -20,8 +20,10 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.TreeMap; import java.util.function.Consumer; +import java.util.stream.Collectors; import io.spring.javaformat.gradle.FormatTask; import io.spring.javaformat.gradle.SpringJavaFormatPlugin; @@ -34,6 +36,8 @@ import org.gradle.api.plugins.JavaBasePlugin; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.quality.CheckstyleExtension; import org.gradle.api.plugins.quality.CheckstylePlugin; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.SourceSetContainer; import org.gradle.api.tasks.bundling.Jar; import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.api.tasks.javadoc.Javadoc; @@ -97,6 +101,11 @@ class JavaConventions { extractLegalResources.getDestinationDirectory().set(project.getLayout().getBuildDirectory().dir("legal")); extractLegalResources.setResourcesNames(Arrays.asList("LICENSE.txt", "NOTICE.txt")); extractLegalResources.property("version", project.getVersion().toString()); + SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); + Set sourceJarTaskNames = sourceSets.stream().map(SourceSet::getSourcesJarTaskName) + .collect(Collectors.toSet()); + Set javadocJarTaskNames = sourceSets.stream().map(SourceSet::getJavadocJarTaskName) + .collect(Collectors.toSet()); project.getTasks().withType(Jar.class, (jar) -> project.afterEvaluate((evaluated) -> { jar.metaInf((metaInf) -> metaInf.from(extractLegalResources)); jar.manifest((manifest) -> { @@ -104,13 +113,25 @@ class JavaConventions { attributes.put("Automatic-Module-Name", project.getName().replace("-", ".")); attributes.put("Build-Jdk-Spec", project.property("sourceCompatibility")); attributes.put("Built-By", "Spring"); - attributes.put("Implementation-Title", project.getDescription()); + attributes.put("Implementation-Title", + determineImplementationTitle(project, sourceJarTaskNames, javadocJarTaskNames, jar)); attributes.put("Implementation-Version", project.getVersion()); manifest.attributes(attributes); }); })); } + private String determineImplementationTitle(Project project, Set sourceJarTaskNames, + Set javadocJarTaskNames, Jar jar) { + if (sourceJarTaskNames.contains(jar.getName())) { + return "Source for " + project.getName(); + } + if (javadocJarTaskNames.contains(jar.getName())) { + return "Javadoc for " + project.getName(); + } + return project.getDescription(); + } + private void configureTestConventions(Project project) { project.getTasks().withType(Test.class, (test) -> { withOptionalBuildJavaHome(project, (javaHome) -> test.setExecutable(javaHome + "/bin/java")); diff --git a/buildSrc/src/test/java/org/springframework/boot/build/ConventionsPluginTests.java b/buildSrc/src/test/java/org/springframework/boot/build/ConventionsPluginTests.java index d8e92ff1c47..8edb0ed8006 100644 --- a/buildSrc/src/test/java/org/springframework/boot/build/ConventionsPluginTests.java +++ b/buildSrc/src/test/java/org/springframework/boot/build/ConventionsPluginTests.java @@ -23,6 +23,7 @@ import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.Collections; import java.util.Map; +import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -71,23 +72,99 @@ class ConventionsPluginTests { out.println(" id 'java'"); out.println(" id 'org.springframework.boot.conventions'"); out.println("}"); - out.println("description 'Test'"); + out.println("version = '1.2.3'"); + out.println("sourceCompatibility = '1.8'"); + out.println("description 'Test project for manifest customization'"); out.println("jar.archiveFileName = 'test.jar'"); } runGradle("jar"); File file = new File(this.projectDir, "/build/libs/test.jar"); assertThat(file).exists(); try (JarFile jar = new JarFile(file)) { - JarEntry license = jar.getJarEntry("META-INF/LICENSE.txt"); - assertThat(license).isNotNull(); - JarEntry notice = jar.getJarEntry("META-INF/NOTICE.txt"); - assertThat(notice).isNotNull(); - String noticeContent = FileCopyUtils.copyToString(new InputStreamReader(jar.getInputStream(notice))); - // Test that variables were replaced - assertThat(noticeContent).doesNotContain("${"); + assertThatLicenseIsPresent(jar); + assertThatNoticeIsPresent(jar); + Attributes mainAttributes = jar.getManifest().getMainAttributes(); + assertThat(mainAttributes.getValue("Implementation-Title")) + .isEqualTo("Test project for manifest customization"); + assertThat(mainAttributes.getValue("Automatic-Module-Name")) + .isEqualTo(this.projectDir.getName().replace("-", ".")); + assertThat(mainAttributes.getValue("Implementation-Version")).isEqualTo("1.2.3"); + assertThat(mainAttributes.getValue("Built-By")).isEqualTo("Spring"); + assertThat(mainAttributes.getValue("Build-Jdk-Spec")).isEqualTo("1.8"); } } + @Test + void sourceJarIsBuilt() throws IOException { + try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) { + out.println("plugins {"); + out.println(" id 'java'"); + out.println(" id 'maven-publish'"); + out.println(" id 'org.springframework.boot.conventions'"); + out.println("}"); + out.println("version = '1.2.3'"); + out.println("sourceCompatibility = '1.8'"); + out.println("description 'Test'"); + } + runGradle("build"); + File file = new File(this.projectDir, "/build/libs/" + this.projectDir.getName() + "-1.2.3-sources.jar"); + assertThat(file).exists(); + try (JarFile jar = new JarFile(file)) { + assertThatLicenseIsPresent(jar); + assertThatNoticeIsPresent(jar); + Attributes mainAttributes = jar.getManifest().getMainAttributes(); + assertThat(mainAttributes.getValue("Implementation-Title")) + .isEqualTo("Source for " + this.projectDir.getName()); + assertThat(mainAttributes.getValue("Automatic-Module-Name")) + .isEqualTo(this.projectDir.getName().replace("-", ".")); + assertThat(mainAttributes.getValue("Implementation-Version")).isEqualTo("1.2.3"); + assertThat(mainAttributes.getValue("Built-By")).isEqualTo("Spring"); + assertThat(mainAttributes.getValue("Build-Jdk-Spec")).isEqualTo("1.8"); + } + } + + @Test + void javadocJarIsBuilt() throws IOException { + try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) { + out.println("plugins {"); + out.println(" id 'java'"); + out.println(" id 'maven-publish'"); + out.println(" id 'org.springframework.boot.conventions'"); + out.println("}"); + out.println("version = '1.2.3'"); + out.println("sourceCompatibility = '1.8'"); + out.println("description 'Test'"); + } + runGradle("build"); + File file = new File(this.projectDir, "/build/libs/" + this.projectDir.getName() + "-1.2.3-javadoc.jar"); + assertThat(file).exists(); + try (JarFile jar = new JarFile(file)) { + assertThatLicenseIsPresent(jar); + assertThatNoticeIsPresent(jar); + Attributes mainAttributes = jar.getManifest().getMainAttributes(); + assertThat(mainAttributes.getValue("Implementation-Title")) + .isEqualTo("Javadoc for " + this.projectDir.getName()); + assertThat(mainAttributes.getValue("Automatic-Module-Name")) + .isEqualTo(this.projectDir.getName().replace("-", ".")); + assertThat(mainAttributes.getValue("Implementation-Version")).isEqualTo("1.2.3"); + assertThat(mainAttributes.getValue("Built-By")).isEqualTo("Spring"); + assertThat(mainAttributes.getValue("Build-Jdk-Spec")).isEqualTo("1.8"); + } + } + + private void assertThatLicenseIsPresent(JarFile jar) { + JarEntry license = jar.getJarEntry("META-INF/LICENSE.txt"); + assertThat(license).isNotNull(); + } + + private void assertThatNoticeIsPresent(JarFile jar) throws IOException { + JarEntry notice = jar.getJarEntry("META-INF/NOTICE.txt"); + assertThat(notice).isNotNull(); + String noticeContent = FileCopyUtils.copyToString(new InputStreamReader(jar.getInputStream(notice))); + // Test that variables were replaced + assertThat(noticeContent).doesNotContain("${"); + } + @Test void testRetryIsConfiguredWithThreeRetriesOnCI() throws IOException { try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) {