Commit d7336657 authored by Andy Wilkinson's avatar Andy Wilkinson

Merge pull request #20058 from dreis2211

* gh-20058:
  Polish "Include LICENCE and NOTICE files in shipped jars"
  Include LICENCE and NOTICE files in shipped jars

Closes gh-20058
parents fc3fb7fb 8128f309
...@@ -16,6 +16,13 @@ ...@@ -16,6 +16,13 @@
package org.springframework.boot.build; package org.springframework.boot.build;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
...@@ -24,9 +31,11 @@ import io.spring.javaformat.gradle.FormatTask; ...@@ -24,9 +31,11 @@ import io.spring.javaformat.gradle.FormatTask;
import io.spring.javaformat.gradle.SpringJavaFormatPlugin; import io.spring.javaformat.gradle.SpringJavaFormatPlugin;
import org.apache.maven.artifact.repository.MavenArtifactRepository; import org.apache.maven.artifact.repository.MavenArtifactRepository;
import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin; import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin;
import org.gradle.api.GradleException;
import org.gradle.api.Plugin; import org.gradle.api.Plugin;
import org.gradle.api.Project; import org.gradle.api.Project;
import org.gradle.api.artifacts.DependencySet; import org.gradle.api.artifacts.DependencySet;
import org.gradle.api.file.CopySpec;
import org.gradle.api.plugins.JavaBasePlugin; import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.plugins.JavaPluginExtension;
...@@ -42,12 +51,14 @@ import org.gradle.api.publish.maven.MavenPomScm; ...@@ -42,12 +51,14 @@ import org.gradle.api.publish.maven.MavenPomScm;
import org.gradle.api.publish.maven.MavenPublication; import org.gradle.api.publish.maven.MavenPublication;
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin; import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
import org.gradle.api.publish.tasks.GenerateModuleMetadata; import org.gradle.api.publish.tasks.GenerateModuleMetadata;
import org.gradle.api.resources.TextResourceFactory;
import org.gradle.api.tasks.bundling.Jar; import org.gradle.api.tasks.bundling.Jar;
import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.api.tasks.javadoc.Javadoc; import org.gradle.api.tasks.javadoc.Javadoc;
import org.gradle.api.tasks.testing.Test; import org.gradle.api.tasks.testing.Test;
import org.springframework.boot.build.testing.TestFailuresPlugin; import org.springframework.boot.build.testing.TestFailuresPlugin;
import org.springframework.util.FileCopyUtils;
/** /**
* Plugin to apply conventions to projects that are part of Spring Boot's build. * Plugin to apply conventions to projects that are part of Spring Boot's build.
...@@ -65,7 +76,8 @@ import org.springframework.boot.build.testing.TestFailuresPlugin; ...@@ -65,7 +76,8 @@ import org.springframework.boot.build.testing.TestFailuresPlugin;
* <li>{@link JavaCompile}, {@link Javadoc}, and {@link FormatTask} tasks are configured * <li>{@link JavaCompile}, {@link Javadoc}, and {@link FormatTask} tasks are configured
* to use UTF-8 encoding * to use UTF-8 encoding
* <li>{@link JavaCompile} tasks are configured to use {@code -parameters} * <li>{@link JavaCompile} tasks are configured to use {@code -parameters}
* <li>{@link Jar} tasks are configured to have the following manifest entries: * <li>{@link Jar} tasks are configured to produce jars with LICENSE.txt and NOTICE.txt
* files and the following manifest entries:
* <ul> * <ul>
* <li>{@code Automatic-Module-Name} * <li>{@code Automatic-Module-Name}
* <li>{@code Build-Jdk-Spec} * <li>{@code Build-Jdk-Spec}
...@@ -97,6 +109,7 @@ import org.springframework.boot.build.testing.TestFailuresPlugin; ...@@ -97,6 +109,7 @@ import org.springframework.boot.build.testing.TestFailuresPlugin;
* {@link AsciidoctorConventions} are applied. * {@link AsciidoctorConventions} are applied.
* *
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Christoph Dreis
*/ */
public class ConventionsPlugin implements Plugin<Project> { public class ConventionsPlugin implements Plugin<Project> {
...@@ -127,6 +140,7 @@ public class ConventionsPlugin implements Plugin<Project> { ...@@ -127,6 +140,7 @@ public class ConventionsPlugin implements Plugin<Project> {
}); });
project.getTasks().withType(Jar.class, (jar) -> { project.getTasks().withType(Jar.class, (jar) -> {
project.afterEvaluate((evaluated) -> { project.afterEvaluate((evaluated) -> {
jar.metaInf((metaInf) -> copyLegalFiles(project, metaInf));
jar.manifest((manifest) -> { jar.manifest((manifest) -> {
Map<String, Object> attributes = new TreeMap<>(); Map<String, Object> attributes = new TreeMap<>();
attributes.put("Automatic-Module-Name", project.getName().replace("-", ".")); attributes.put("Automatic-Module-Name", project.getName().replace("-", "."));
...@@ -141,6 +155,43 @@ public class ConventionsPlugin implements Plugin<Project> { ...@@ -141,6 +155,43 @@ public class ConventionsPlugin implements Plugin<Project> {
}); });
} }
private void copyLegalFiles(Project project, CopySpec metaInf) {
copyNoticeFile(project, metaInf);
copyLicenseFile(project, metaInf);
}
private void copyNoticeFile(Project project, CopySpec metaInf) {
try {
InputStream notice = getClass().getClassLoader().getResourceAsStream("NOTICE.txt");
String noticeContent = FileCopyUtils.copyToString(new InputStreamReader(notice, StandardCharsets.UTF_8))
.replace("${version}", project.getVersion().toString());
TextResourceFactory resourceFactory = project.getResources().getText();
File file = createLegalFile(resourceFactory.fromString(noticeContent).asFile(), "NOTICE.txt");
metaInf.from(file);
}
catch (IOException ex) {
throw new GradleException("Failed to copy NOTICE.txt", ex);
}
}
private void copyLicenseFile(Project project, CopySpec metaInf) {
URL license = getClass().getClassLoader().getResource("LICENSE.txt");
try {
TextResourceFactory resourceFactory = project.getResources().getText();
File file = createLegalFile(resourceFactory.fromUri(license.toURI()).asFile(), "LICENSE.txt");
metaInf.from(file);
}
catch (URISyntaxException ex) {
throw new GradleException("Failed to copy LICENSE.txt", ex);
}
}
private File createLegalFile(File source, String filename) {
File legalFile = new File(source.getParentFile(), filename);
source.renameTo(legalFile);
return legalFile;
}
private void configureSpringJavaFormat(Project project) { private void configureSpringJavaFormat(Project project) {
project.getPlugins().apply(SpringJavaFormatPlugin.class); project.getPlugins().apply(SpringJavaFormatPlugin.class);
project.getTasks().withType(FormatTask.class, (formatTask) -> formatTask.setEncoding("UTF-8")); project.getTasks().withType(FormatTask.class, (formatTask) -> formatTask.setEncoding("UTF-8"));
......
This diff is collapsed.
Spring Boot ${version}
Copyright (c) 2012-2020 Pivotal, Inc.
This product is licensed to you under the Apache License, Version 2.0
(the "License"). You may not use this product except in compliance with
the License.
\ No newline at end of file
/*
* Copyright 2012-2020 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
*
* https://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.build;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.gradle.testkit.runner.GradleRunner;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.util.FileCopyUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link ConventionsPlugin}.
*
* @author Christoph Dreis
*/
class ConventionsPluginTests {
private File projectDir;
private File buildFile;
@BeforeEach
void setup(@TempDir File projectDir) throws IOException {
this.projectDir = projectDir;
this.buildFile = new File(this.projectDir, "build.gradle");
}
@Test
void jarIncludesLegalFiles() throws IOException {
try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) {
out.println("plugins {");
out.println(" id 'java'");
out.println(" id 'org.springframework.boot.conventions'");
out.println("}");
out.println("description 'Test'");
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("${");
}
}
private void runGradle(String... args) {
GradleRunner.create().withProjectDir(this.projectDir).withArguments(args).withPluginClasspath().build();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment