From c37ecf06dd9deaf59eee4ea8a183be14aab758af Mon Sep 17 00:00:00 2001 From: Baptiste Mesta Date: Tue, 23 Oct 2018 20:08:15 +0200 Subject: [PATCH] refactor: convert NoticeTask to java Relates to #34459 --- .../elasticsearch/gradle/NoticeTask.groovy | 99 -------------- .../org/elasticsearch/gradle/NoticeTask.java | 128 ++++++++++++++++++ .../elasticsearch/gradle/NoticeTaskTests.java | 99 ++++++++++++++ 3 files changed, 227 insertions(+), 99 deletions(-) delete mode 100644 buildSrc/src/main/groovy/org/elasticsearch/gradle/NoticeTask.groovy create mode 100644 buildSrc/src/main/java/org/elasticsearch/gradle/NoticeTask.java create mode 100644 buildSrc/src/test/java/org/elasticsearch/gradle/NoticeTaskTests.java diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/NoticeTask.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/NoticeTask.groovy deleted file mode 100644 index 928298db7bfc2..0000000000000 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/NoticeTask.groovy +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you 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.elasticsearch.gradle - -import org.gradle.api.DefaultTask -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction - -/** - * A task to create a notice file which includes dependencies' notices. - */ -public class NoticeTask extends DefaultTask { - - @InputFile - File inputFile = project.rootProject.file('NOTICE.txt') - - @OutputFile - File outputFile = new File(project.buildDir, "notices/${name}/NOTICE.txt") - - /** Directories to include notices from */ - private List licensesDirs = new ArrayList<>() - - public NoticeTask() { - description = 'Create a notice file from dependencies' - // Default licenses directory is ${projectDir}/licenses (if it exists) - File licensesDir = new File(project.projectDir, 'licenses') - if (licensesDir.exists()) { - licensesDirs.add(licensesDir) - } - } - - /** Add notices from the specified directory. */ - public void licensesDir(File licensesDir) { - licensesDirs.add(licensesDir) - } - - @TaskAction - public void generateNotice() { - StringBuilder output = new StringBuilder() - output.append(inputFile.getText('UTF-8')) - output.append('\n\n') - // This is a map rather than a set so that the sort order is the 3rd - // party component names, unaffected by the full path to the various files - Map seen = new TreeMap<>() - for (File licensesDir : licensesDirs) { - licensesDir.eachFileMatch({ it ==~ /.*-NOTICE\.txt/ }) { File file -> - String name = file.name.substring(0, file.name.length() - '-NOTICE.txt'.length()) - if (seen.containsKey(name)) { - File prevFile = seen.get(name) - if (prevFile.text != file.text) { - throw new RuntimeException("Two different notices exist for dependency '" + - name + "': " + prevFile + " and " + file) - } - } else { - seen.put(name, file) - } - } - } - for (Map.Entry entry : seen.entrySet()) { - String name = entry.getKey() - File file = entry.getValue() - appendFile(file, name, 'NOTICE', output) - appendFile(new File(file.parentFile, "${name}-LICENSE.txt"), name, 'LICENSE', output) - } - outputFile.setText(output.toString(), 'UTF-8') - } - - static void appendFile(File file, String name, String type, StringBuilder output) { - String text = file.getText('UTF-8') - if (text.trim().isEmpty()) { - return - } - output.append('================================================================================\n') - output.append("${name} ${type}\n") - output.append('================================================================================\n') - output.append(text) - output.append('\n\n') - } -} diff --git a/buildSrc/src/main/java/org/elasticsearch/gradle/NoticeTask.java b/buildSrc/src/main/java/org/elasticsearch/gradle/NoticeTask.java new file mode 100644 index 0000000000000..5e02b090f66e5 --- /dev/null +++ b/buildSrc/src/main/java/org/elasticsearch/gradle/NoticeTask.java @@ -0,0 +1,128 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you 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.elasticsearch.gradle; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; + + +/** + * A task to create a notice file which includes dependencies' notices. + */ +public class NoticeTask extends DefaultTask { + + private static final Charset UTF8 = Charset.forName("UTF-8"); + @InputFile + private File inputFile = getProject().getRootProject().file("NOTICE.txt"); + + @OutputFile + private File outputFile = new File(getProject().getBuildDir(), "notices/" + getName() + "/NOTICE.txt"); + + /** + * Directories to include notices from + */ + private List licensesDirs = new ArrayList<>(); + + public NoticeTask() { + setDescription("Create a notice file from dependencies"); + // Default licenses directory is ${projectDir}/licenses (if it exists) + File licensesDir = new File(getProject().getProjectDir(), "licenses"); + if (licensesDir.exists()) { + licensesDirs.add(licensesDir); + } + } + + /** + * Add notices from the specified directory. + */ + public void setLicensesDir(File licensesDir) { + licensesDirs.add(licensesDir); + } + + public void setInputFile(File inputFile) { + this.inputFile = inputFile; + } + + @TaskAction + public void generateNotice() throws IOException { + StringBuilder output = new StringBuilder(); + output.append(read(inputFile)); + output.append("\n\n"); + // This is a map rather than a set so that the sort order is the 3rd + // party component names, unaffected by the full path to the various files + Map seen = new TreeMap<>(); + for (File licensesDir : licensesDirs) { + File[] files = licensesDir.listFiles((file) -> file.getName().matches(".*-NOTICE\\.txt")); + if (files == null) { + continue; + } + for (File file : files) { + String name = file.getName().substring(0, file.getName().length() - "-NOTICE.txt".length()); + if (seen.containsKey(name)) { + File prevFile = seen.get(name); + if (!read(prevFile).equals(read(file))) { + throw new RuntimeException("Two different notices exist for dependency '" + + name + "': " + prevFile + " and " + file); + } + } else { + seen.put(name, file); + } + } + } + for (Map.Entry entry : seen.entrySet()) { + String name = entry.getKey(); + File file = entry.getValue(); + appendFile(file, name, "NOTICE", output); + appendFile(new File(file.getParent(), name + "-LICENSE.txt"), name, "LICENSE", output); + } + outputFile.getParentFile().mkdirs(); + outputFile.createNewFile(); + write(outputFile, output.toString()); + } + + private String read(File file) throws IOException { + return new String(Files.readAllBytes(file.toPath()), UTF8); + } + + private void write(File file, String content) throws IOException { + Files.write(file.toPath(), content.getBytes(UTF8)); + } + + private void appendFile(File file, String name, String type, StringBuilder output) throws IOException { + String text = read(file); + if (text.trim().isEmpty()) { + return; + } + output.append("================================================================================\n"); + output.append(name).append(" ").append(type).append("\n"); + output.append("================================================================================\n"); + output.append(text).append("\n\n"); + } +} diff --git a/buildSrc/src/test/java/org/elasticsearch/gradle/NoticeTaskTests.java b/buildSrc/src/test/java/org/elasticsearch/gradle/NoticeTaskTests.java new file mode 100644 index 0000000000000..b4c0795468078 --- /dev/null +++ b/buildSrc/src/test/java/org/elasticsearch/gradle/NoticeTaskTests.java @@ -0,0 +1,99 @@ +package org.elasticsearch.gradle; + +import static java.nio.file.Files.readAllBytes; +import static java.nio.file.Files.write; +import static java.util.Collections.singletonMap; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Path; + +import org.elasticsearch.gradle.test.GradleUnitTestCase; +import org.gradle.api.Project; +import org.gradle.internal.impldep.org.junit.rules.ExpectedException; +import org.gradle.internal.impldep.org.junit.rules.TemporaryFolder; +import org.gradle.testfixtures.ProjectBuilder; +import org.junit.Rule; + +public class NoticeTaskTests extends GradleUnitTestCase { + + private static final Charset UTF8 = Charset.forName("UTF-8"); + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private Project project; + private NoticeTask task; + + private NoticeTask createTask(Project project) { + return (NoticeTask) project.task(singletonMap("type", NoticeTask.class), "noticeTask"); + } + + private Project createProject() throws IOException { + temporaryFolder.create(); + File projectDir = temporaryFolder.newFolder(); + return ProjectBuilder.builder().withProjectDir(projectDir).build(); + } + + + public void testGenerateAllNotices() throws Exception { + project = createProject(); + task = createTask(project); + Path projectDir = project.getProjectDir().toPath(); + createFileIn(projectDir, "NOTICE.txt", "This is\nthe root notice\n"); + Path licenseDir = projectDir.resolve("licenseDir"); + createFileIn(licenseDir, "myNotice-NOTICE.txt", "This is\nmy notice\n"); + createFileIn(licenseDir, "myNotice-LICENSE.txt", "This is\nmy license\n"); + task.setLicensesDir(licenseDir.toFile()); + + task.generateNotice(); + + Path notice = project.getBuildDir().toPath().resolve("notices").resolve(task.getName()).resolve("NOTICE.txt"); + String generatedContent = new String(readAllBytes(notice), UTF8); + String expected = "This is\n" + + "the root notice\n" + + "\n" + + "\n" + + "================================================================================\n" + + "myNotice NOTICE\n" + + "================================================================================\n" + + "This is\n" + + "my notice\n" + + "\n" + + "\n" + + "================================================================================\n" + + "myNotice LICENSE\n" + + "================================================================================\n" + + "This is\n" + + "my license"; + assertEquals(expected, generatedContent.trim()); + } + + public void testShouldFailWhenMultipleNoticeIsPresent() throws Exception { + project = createProject(); + task = createTask(project); + Path projectDir = project.getProjectDir().toPath(); + createFileIn(projectDir, "NOTICE.txt", "This is\nthe root notice\n"); + Path licenseDir = projectDir.resolve("licenseDir"); + createFileIn(licenseDir, "myNotice1-NOTICE.txt", "This is\nmy notice\n"); + createFileIn(licenseDir, "myNotice1-LICENSE.txt", "This is\nmy notice\n"); + createFileIn(licenseDir, "myNotice2-NOTICE.txt", "This is\nmy notice\n"); + createFileIn(licenseDir, "myNotice2-LICENSE.txt", "This is\nmy notice\n"); + task.setLicensesDir(licenseDir.toFile()); + + expectedException.expect(RuntimeException.class); + expectedException.expectMessage("Two different notices exist"); + + task.generateNotice(); + } + + private void createFileIn(Path parent, String name, String content) throws IOException { + parent.toFile().mkdir(); + Path file = parent.resolve(name); + file.toFile().createNewFile(); + write(file, content.getBytes(UTF8)); + } +}