@@ -20,13 +20,16 @@ import com.github.jk1.license.LicenseFileDetails
2020import com.github.jk1.license.ModuleData
2121import com.github.jk1.license.ProjectData
2222import com.github.jk1.license.render.ReportRenderer
23+ import java.io.File
2324import java.io.IOException
2425import java.net.URISyntaxException
2526import java.nio.file.Files
2627import java.nio.file.Path
2728import java.nio.file.StandardCopyOption
2829import java.nio.file.StandardOpenOption
2930import java.util.ArrayList
31+ import org.gradle.api.logging.Logging
32+ import org.gradle.api.provider.Provider
3033
3134private const val APACHE_LICENSE_FILE_NAME : String = " Apache-2.0.txt"
3235private const val MIT_FILE_NAME : String = " MIT.txt"
@@ -53,21 +56,27 @@ val LICENSE_TITLE_TO_RESOURCE_FILE: Map<String, String> = buildMap {
5356
5457class AnalyzerLicensingPackagingRenderer (
5558 private val buildOutputDir : Path ,
59+ private val dependencyLicenseOverrides : Provider <Map <String , File >>,
5660) : ReportRenderer {
61+ private val logger = Logging .getLogger(AnalyzerLicensingPackagingRenderer ::class .java)
5762 private lateinit var generatedLicenseResourcesDirectory: Path
58- private val dependenciesWithUnusableLicenseFileInside: Set <String > = setOf (
59- " com.fasterxml.jackson.dataformat.jackson-dataformat-smile" ,
60- " com.fasterxml.jackson.dataformat.jackson-dataformat-yaml" ,
61- " com.fasterxml.woodstox.woodstox-core" ,
62- " org.codehaus.woodstox.stax2-api"
63+ private val defaultDependencyLicenseOverrides = mapOf (
64+ " com.fasterxml.jackson.dataformat:jackson-dataformat-smile" to APACHE_LICENSE_FILE_NAME ,
65+ " com.fasterxml.jackson.dataformat:jackson-dataformat-yaml" to APACHE_LICENSE_FILE_NAME ,
66+ " com.fasterxml.woodstox:woodstox-core" to APACHE_LICENSE_FILE_NAME ,
67+ " com.salesforce:apex-jorje-lsp-minimized" to " BSD-3.txt" ,
68+ " org.codehaus.woodstox:stax2-api" to " BSD-2.txt"
6369 )
70+
6471 private val exceptions: ArrayList <String > = ArrayList ()
6572
6673 // Generate license files for all dependencies in the licenses folder
6774 override fun render (data : ProjectData ) {
75+ logger.info(" Generating licenses report started" )
6876 generatedLicenseResourcesDirectory = buildOutputDir.resolve(" licenses" )
6977 try {
7078 generateDependencyFiles(data)
79+ logger.info(" Generating licenses report finished successfully" )
7180 } catch (e: Exception ) {
7281 throw RuntimeException (e)
7382 }
@@ -86,14 +95,25 @@ class AnalyzerLicensingPackagingRenderer(
8695
8796 /* *
8897 * Generate a license file for a given dependency.
89- * First we try to copy the license file included in the dependency itself in `copyIncludedLicenseFromDependency`
90- * If there is no License file, or the dependency contains an unusable license file,
91- * we try to derive the license from the pom in `findLicenseIdentifierInPomAndCopyFromResources`.
92- * In this method we're looking for the identifier of the license, and we copy the corresponding license file from our resources.
93- * The mapping (license identifier to resource file) is derived from the map `licenseTitleToResourceFile`.
98+ * First we try to copy a configured override in `copyOverriddenLicense`.
99+ * If there is no override, we try to copy the license file included in the dependency itself
100+ * in `copyIncludedLicenseFromDependency`.
101+ * If there is no included license file, or the dependency contains an unusable one,
102+ * we derive the license from the pom in `findLicenseIdentifierInPomAndCopyFromResources`.
103+ * That method looks up the license identifier and copies the corresponding file from our resources,
104+ * using the mapping defined in `LICENSE_TITLE_TO_RESOURCE_FILE`.
94105 */
95106 @Throws(IOException ::class , URISyntaxException ::class )
96107 private fun generateDependencyFile (data : ModuleData ) {
108+ val copyOverrideLicenseFile = copyOverriddenLicense(data)
109+ if (copyOverrideLicenseFile.success) {
110+ return
111+ }
112+ val copyDefaultOverrideLicenseFile = copyDefaultOverriddenLicense(data)
113+ if (copyDefaultOverrideLicenseFile.success) {
114+ return
115+ }
116+
97117 val copyIncludedLicenseFile = copyIncludedLicenseFromDependency(data)
98118 if (copyIncludedLicenseFile.success) {
99119 return
@@ -104,16 +124,33 @@ class AnalyzerLicensingPackagingRenderer(
104124 return
105125 }
106126
127+ exceptions.add(" ${data.group} .${data.name} : ${copyOverrideLicenseFile.message} " )
107128 exceptions.add(" ${data.group} .${data.name} : ${copyIncludedLicenseFile.message} " )
108129 exceptions.add(" ${data.group} .${data.name} : ${copyFromResources.message} " )
109130 }
110131
111132 @Throws(IOException ::class )
112- private fun copyIncludedLicenseFromDependency (data : ModuleData ): Status {
113- if (dependenciesWithUnusableLicenseFileInside.contains(" ${data.group} .${data.name} " )) {
114- return Status .failure(" Excluded copying license from dependency as it's not the right one." )
115- }
133+ private fun copyOverriddenLicense (data : ModuleData ): Status {
134+ val dependencyKey = data.dependencyKey()
135+ val overrideFile = dependencyLicenseOverrides.getOrElse(emptyMap())[dependencyKey]
136+ ? : return Status .failure(" No override configured." )
137+ copyLicenseFile(data, overrideFile.toPath())
138+ logger.info(" For the dependency {}: used configured override '{}'" , dependencyKey, overrideFile.name)
139+ return Status .success
140+ }
141+
142+ @Throws(IOException ::class )
143+ private fun copyDefaultOverriddenLicense (data : ModuleData ): Status {
144+ val dependencyKey = data.dependencyKey()
145+ val overrideFile = defaultDependencyLicenseOverrides[dependencyKey]
146+ ? : return Status .failure(" No default override." )
147+ copyLicenseResourceByFileName(data, overrideFile)
148+ logger.info(" For the dependency {}: used default override '{}'" , dependencyKey, overrideFile)
149+ return Status .success
150+ }
116151
152+ @Throws(IOException ::class )
153+ private fun copyIncludedLicenseFromDependency (data : ModuleData ): Status {
117154 val licenseFileDetails = data.licenseFiles.stream().flatMap { licenseFile -> licenseFile.fileDetails.stream() }
118155 .filter { file: LicenseFileDetails -> file.file.contains(" LICENSE" ) }
119156 .findFirst()
@@ -123,19 +160,32 @@ class AnalyzerLicensingPackagingRenderer(
123160 }
124161
125162 copyLicenseFile(data, buildOutputDir.resolve(licenseFileDetails.get().file))
163+ logger.info(
164+ " For the dependency {}: copied packaged license from '{}'" ,
165+ data.dependencyKey(),
166+ licenseFileDetails.get().file
167+ )
126168 return Status .success
127169 }
128170
129171 @Throws(IOException ::class , URISyntaxException ::class )
130172 private fun findLicenseIdentifierInPomAndCopyFromResources (data : ModuleData ): Status {
131- val pomLicense = data.poms.stream().flatMap { pomData -> pomData.licenses.stream() }
132- .findFirst()
133-
134- if (pomLicense.isEmpty) {
173+ val licenses = data.poms.stream().flatMap { pomData -> pomData.licenses.stream() }.toList()
174+ if (licenses.isEmpty()) {
135175 return Status .failure(" No license found in pom data." )
136176 }
177+ val pomLicense = licenses[0 ]
178+ if (licenses.size > 1 ) {
179+ logger.warn(
180+ " The dependency: {}: contains multiple licenses in pom data: [{}]. The '{}' was taken. " +
181+ " Please review if it is the correct one, if not define it in licenseGenerationConfig.dependencyLicenseOverrides" ,
182+ data.dependencyKey(),
183+ licenses.joinToString { it.name },
184+ pomLicense.name
185+ )
186+ }
137187
138- return copyLicenseFromResources(data, pomLicense.get(). name)
188+ return copyLicenseFromResources(data, pomLicense.name)
139189 }
140190
141191 @Throws(IOException ::class )
@@ -161,8 +211,23 @@ class AnalyzerLicensingPackagingRenderer(
161211 ): Status {
162212 val licenseResourceFileName = LICENSE_TITLE_TO_RESOURCE_FILE [licenseName]
163213 ? : return Status .failure(" License file '$licenseName ' could not be found." )
164- val resourceAsStream = AnalyzerLicensingPackagingRenderer ::class .java.getResourceAsStream(" /licenses/$licenseResourceFileName " )
165- ? : throw IOException (" Resource not found for license: $licenseName " )
214+ copyLicenseResourceByFileName(data, licenseResourceFileName)
215+ logger.info(
216+ " For the dependency {}: used bundled resource '{}' for POM license '{}'" ,
217+ " ${data.group} :${data.name} " ,
218+ licenseResourceFileName,
219+ licenseName
220+ )
221+ return Status .success
222+ }
223+
224+ @Throws(IOException ::class )
225+ private fun copyLicenseResourceByFileName (
226+ data : ModuleData ,
227+ resourceFileName : String ,
228+ ): Status {
229+ val resourceAsStream = AnalyzerLicensingPackagingRenderer ::class .java.getResourceAsStream(" /licenses/$resourceFileName " )
230+ ? : throw IOException (" Resource not found for license: $resourceFileName " )
166231 Files .copy(resourceAsStream, generateLicensePath(data), StandardCopyOption .REPLACE_EXISTING )
167232 return Status .success
168233 }
@@ -180,4 +245,6 @@ class AnalyzerLicensingPackagingRenderer(
180245 fun failure (message : String? ): Status = Status (false , message)
181246 }
182247 }
248+
249+ fun ModuleData.dependencyKey () = " $group :$name "
183250}
0 commit comments