Suppress missing license information for sub-projects in a multi-project build.

master
Robinson 2022-11-16 01:03:36 +01:00
parent b2bb05e6cc
commit 5d7e14d380
No known key found for this signature in database
GPG Key ID: 8E7DB78588BD6F5C
4 changed files with 73 additions and 35 deletions

View File

@ -15,9 +15,10 @@ object LicenseDependencyScanner {
// scans and loads license data into the extension
// - from jars on runtime/compile classpath
data class ScanDep(val project: Project, val preloadedText: MutableList<String>, val embeddedText: MutableList<String>, val missingText: MutableList<String>)
// THIS MUST BE IN "afterEvaluate" or run from a specific task.
fun scanForLicenseData(project: Project, allProjects: Boolean,
licenses: MutableList<LicenseData>): Triple<MutableList<String>, MutableList<String>, MutableList<String>> {
fun scanForLicenseData(project: Project, allProjects: Boolean, licenses: MutableList<LicenseData>): ScanDep {
val preloadedText = mutableListOf<String>();
val embeddedText = mutableListOf<String>();
@ -25,7 +26,7 @@ object LicenseDependencyScanner {
// NOTE: there will be some duplicates, so we want to remove them
val dependencies = mutableSetOf<Dependency>()
val dependencies = mutableSetOf<ProjAndDependency>()
if (allProjects) {
project.allprojects.forEach { proj ->
@ -39,36 +40,46 @@ object LicenseDependencyScanner {
dependencies.addAll(scan(project, "runtimeClasspath"))
}
// this will contain duplicates if sub-projects ALSO have the same deps
val projectDependencies = dependencies.toList()
val missingLicenseInfo = mutableSetOf<Dependency>()
val actuallyMissingLicenseInfo = mutableSetOf<Dependency>()
val missingLicenseInfo = mutableSetOf<ProjAndDependency>()
val actuallyMissingLicenseInfo = mutableSetOf<ProjAndDependency>()
val alreadyScanDeps = mutableSetOf<Dependency>()
if (licenses.isNotEmpty()) {
// when we scan, we ONLY want to scan a SINGLE LAYER (if we have license info for module ID, then we don't need license info for it's children)
val primaryLicense = licenses.first()
// scan to see if we have in our predefined section
projectDependencies.forEach { info: Dependency ->
projectDependencies.forEach { projAndDep: ProjAndDependency ->
val dep = projAndDep.dep
if (alreadyScanDeps.contains(dep)) {
return@forEach
}
val data: LicenseData? = try {
AppLicensing.getLicense(info.mavenId())
AppLicensing.getLicense(dep.mavenId())
} catch (e: Exception) {
println("\tError getting license information for ${info.mavenId()}")
println("\tError getting license information for ${dep.mavenId()}")
null
}
if (data == null) {
missingLicenseInfo.add(info)
missingLicenseInfo.add(projAndDep)
} else {
if (!primaryLicense.extras.contains(data)) {
preloadedText.add("\t\t${info.mavenId()} [${data.license}]")
alreadyScanDeps.add(dep)
preloadedText.add("\t\t${dep.mavenId()} [${data.license}]")
// NOTE: the END copyright for these are determined by the DATE of the files!
// Some dates are WRONG (because the jar build is mucked with), so we manually fix it
if (data.copyright == 0) {
// get the OLDEST date from the artifacts and use that as the copyright date
var oldestDate = 0L
info.artifacts.forEach { artifact ->
dep.artifacts.forEach { artifact ->
// get the date of the manifest file (which is the first entry)
ZipInputStream(FileInputStream(artifact.file)).use {
oldestDate = oldestDate.coerceAtLeast(it.nextEntry.lastModifiedTime.toMillis())
@ -96,10 +107,15 @@ object LicenseDependencyScanner {
// now scan to see if the jar has a license blob in it
if (missingLicenseInfo.isNotEmpty()) {
missingLicenseInfo.forEach { info ->
missingLicenseInfo.forEach { projAndDep: ProjAndDependency ->
val dep = projAndDep.dep
if (alreadyScanDeps.contains(dep)) {
return@forEach
}
// see if we have it in the dependency jar
var licenseData: License? = null
info.artifacts.forEach search@{ artifact ->
dep.artifacts.forEach search@{ artifact ->
val file = artifact.file
try {
if (file.canRead()) {
@ -125,7 +141,7 @@ object LicenseDependencyScanner {
}
}
} catch (e: Exception) {
println("\t$info [ERROR $file], ${e.message ?: e.javaClass}")
println("\t$dep [ERROR $file], ${e.message ?: e.javaClass}")
}
return@search
@ -134,27 +150,48 @@ object LicenseDependencyScanner {
}
}
} catch (e: Exception) {
println("\t$info [ERROR $file], ${e.message ?: e.javaClass}")
println("\t$dep [ERROR $file], ${e.message ?: e.javaClass}")
}
}
if (licenseData != null) {
embeddedText.add("\t\t$info [$licenseData]")
alreadyScanDeps.add(dep)
embeddedText.add("\t\t$dep [$licenseData]")
} else {
actuallyMissingLicenseInfo.add(info)
actuallyMissingLicenseInfo.add(projAndDep)
}
}
}
if (actuallyMissingLicenseInfo.isNotEmpty()) {
actuallyMissingLicenseInfo.forEach { missingDep ->
missingText.add("\t ${missingDep.mavenId()}")
// we have to prune sub-project data first...
val projectMavenIds = mutableSetOf<String>()
project.allprojects.forEach {
projectMavenIds.add("${it.group}:${it.name}:${it.version}")
}
actuallyMissingLicenseInfo.forEach { missingDepAndProj ->
// we DO NOT want to show missing deps for project sub-projects.
val proj = missingDepAndProj.project
val dep = missingDepAndProj.dep
if (alreadyScanDeps.contains(dep)) {
return@forEach
}
alreadyScanDeps.add(dep)
val mavenId = dep.mavenId()
// if the missing dep IS ALSO the same as a subproject .... we don't want to report it
if (!projectMavenIds.contains(mavenId)) {
missingText.add("\t ${dep.mavenId()}")
}
}
}
}
return Triple(preloadedText, embeddedText, missingText)
return ScanDep(project, preloadedText, embeddedText, missingText)
}
@ -167,9 +204,9 @@ object LicenseDependencyScanner {
*
* This is an actual problem...
*/
private fun scan(project: Project, configurationName: String): List<Dependency> {
private fun scan(project: Project, configurationName: String): List<ProjAndDependency> {
val projectDependencies = mutableListOf<Dependency>()
val projectDependencies = mutableListOf<ProjAndDependency>()
val config = project.configurations.getByName(configurationName)
if (!config.isCanBeResolved) {
return projectDependencies
@ -204,16 +241,19 @@ object LicenseDependencyScanner {
} catch (e: Exception) {
listOf()
}
projectDependencies.add(Dependency(group, name, version, artifacts))
projectDependencies.add(ProjAndDependency(project, Dependency(group, name, version, artifacts)))
}
return projectDependencies
}
internal data class Dependency(val group: String,
val name: String,
val version: String,
val artifacts: List<Artifact>) {
internal data class ProjAndDependency(val project: Project, val dep: Dependency)
internal data class Dependency(
val group: String,
val name: String,
val version: String,
val artifacts: List<Artifact>) {
fun mavenId(): String {
return "$group:$name:$version"

View File

@ -36,7 +36,7 @@ internal open class LicenseInjector @Inject constructor(@Internal val extension:
// This MUST be first, since it loads license data that is used elsewhere
// show scanning or missing, but not both
// NOTE: we scan the dependencies in ALL subprojects as well.
val (preloadedText, embeddedText, missingText) = extension.scanDependencies(project, true)
val (proj, preloadedText, embeddedText, missingText) = extension.scanDependencies(project, true)
// validate the license text configuration section in the gradle file ONLY WHEN PUSHING A JAR
val licensing = extension.licenses

View File

@ -53,7 +53,3 @@ class LicensePlugin : Plugin<Project> {
}
}
}

View File

@ -181,14 +181,16 @@ open class Licensing(private val project: Project) {
}
// scan as part of the plugin
fun scanDependencies(project: Project, allProjects: Boolean): Triple<MutableList<String>, MutableList<String>, MutableList<String>> {
fun scanDependencies(project: Project, allProjects: Boolean): LicenseDependencyScanner.ScanDep {
// now we want to add license information that we know about from our dependencies to our list
// just to make it clear, license information CAN CHANGE BETWEEN VERSIONS! For example, JNA changed from GPL to Apache in version 4+
// we associate the artifact group + id + (start) version as a license.
// if a license for a dependency is UNKNOWN, then we emit a warning to the user to add it as a pull request
// if a license version is not specified, then we use the default
val textOutput = LicenseDependencyScanner.scanForLicenseData(project, allProjects, this.licenses)
val depInfo = LicenseDependencyScanner.scanForLicenseData(project, allProjects, this.licenses)
// we only should include the kotlin license information IF we actually use kotlin.
@ -211,7 +213,7 @@ open class Licensing(private val project: Project) {
}
}
return textOutput
return depInfo
}
/**