Browse Source

Add the ability to include extra notices in a plugin's NOTICES file (#23898)

Adds the option for a plugin to specify extra directories containing notices
and licenses files to be incorporated into the overall notices file that is
generated for the plugin.

This can be useful, for example, where the plugin has a non-Java dependency
that itself incorporates many 3rd party components.
David Roberts 8 years ago
parent
commit
37aadb2adf

+ 30 - 13
buildSrc/src/main/groovy/org/elasticsearch/gradle/NoticeTask.groovy

@@ -37,16 +37,21 @@ public class NoticeTask extends DefaultTask {
     @OutputFile
     File outputFile = new File(project.buildDir, "notices/${name}/NOTICE.txt")
 
-    /** Configurations to inspect dependencies*/
-    private List<Project> dependencies = new ArrayList<>()
+    /** Directories to include notices from */
+    private List<File> 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 licenses found in the given project. */
-    public void dependencies(Project project) {
-        dependencies.add(project)
+    /** Add notices from the specified directory. */
+    public void licensesDir(File licensesDir) {
+        licensesDirs.add(licensesDir)
     }
 
     @TaskAction
@@ -54,17 +59,29 @@ public class NoticeTask extends DefaultTask {
         StringBuilder output = new StringBuilder()
         output.append(inputFile.getText('UTF-8'))
         output.append('\n\n')
-        Set<String> seen = new HashSet<>()
-        for (Project dep : dependencies) {
-            File licensesDir = new File(dep.projectDir, 'licenses')
-            if (licensesDir.exists() == false) continue
-            licensesDir.eachFileMatch({ it ==~ /.*-NOTICE\.txt/ && seen.contains(it) == false}) { File file ->
+        // 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<String, File> 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())
-                appendFile(file, name, 'NOTICE', output)
-                appendFile(new File(file.parentFile, "${name}-LICENSE.txt"), name, 'LICENSE', output)
-                seen.add(file.name)
+                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<String, File> 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')
     }
 

+ 0 - 1
buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy

@@ -260,7 +260,6 @@ public class PluginBuildPlugin extends BuildPlugin {
         File noticeFile = project.pluginProperties.extension.noticeFile
         if (noticeFile != null) {
             NoticeTask generateNotice = project.tasks.create('generateNotice', NoticeTask.class)
-            generateNotice.dependencies(project)
             generateNotice.inputFile = noticeFile
             project.bundlePlugin.from(generateNotice)
         }

+ 6 - 3
distribution/build.gradle

@@ -49,12 +49,12 @@ Collection distributions = project.subprojects.findAll {
 
 // integ test zip only uses core, so a different notice file is needed there
 task buildCoreNotice(type: NoticeTask) {
-  dependencies project(':core')
+  licensesDir new File(project(':core').projectDir, 'licenses')
 }
 
 // other distributions include notices from modules as well, which are added below later
 task buildFullNotice(type: NoticeTask) {
-  dependencies project(':core')
+  licensesDir new File(project(':core').projectDir, 'licenses')
 }
 
 /*****************************************************************************
@@ -73,7 +73,10 @@ ext.restTestExpansions = [
 // loop over modules to also setup cross task dependencies and increment our modules counter
 project.rootProject.subprojects.findAll { it.path.startsWith(':modules:') }.each { Project module ->
   buildFullNotice {
-    dependencies module
+    def defaultLicensesDir = new File(module.projectDir, 'licenses')
+    if (defaultLicensesDir.exists()) {
+      licensesDir defaultLicensesDir
+    }
   }
   buildModules {
     dependsOn({ project(module.path).bundlePlugin })