Licensing can now load license data from dependencies. There is master list for uncontrolled dependencies, and if a project ALSO uses this plugin, it's License info will be automatically added.
parent
5a16322ed5
commit
823d6b25e5
5
LICENSE
5
LICENSE
|
@ -1,5 +1,4 @@
|
|||
- Licensing -
|
||||
- Licensing - License definitions and legal management plugin for the Gradle build system
|
||||
https://git.dorkbox.com/dorkbox/Licensing
|
||||
Copyright 2019 - The Apache Software License, Version 2.0
|
||||
Copyright 2020 - The Apache Software License, Version 2.0
|
||||
Dorkbox LLC
|
||||
License definitions and legal management plugin for the Gradle build system
|
||||
|
|
|
@ -16,25 +16,24 @@
|
|||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import java.time.Instant
|
||||
|
||||
println("Gradle ${project.gradle.gradleVersion}")
|
||||
|
||||
plugins {
|
||||
java
|
||||
`java-gradle-plugin`
|
||||
|
||||
id("com.gradle.plugin-publish") version "0.10.1"
|
||||
id("com.dorkbox.Licensing") version "1.4"
|
||||
id("com.dorkbox.VersionUpdate") version "1.4.1"
|
||||
id("com.dorkbox.GradleUtils") version "1.2"
|
||||
id("com.gradle.plugin-publish") version "0.12.0"
|
||||
|
||||
kotlin("jvm") version "1.3.21"
|
||||
id("com.dorkbox.VersionUpdate") version "1.7"
|
||||
id("com.dorkbox.GradleUtils") version "1.8"
|
||||
|
||||
kotlin("jvm") version "1.3.72"
|
||||
}
|
||||
|
||||
|
||||
object Extras {
|
||||
// set for the project
|
||||
const val description = "License definitions and legal management plugin for the Gradle build system"
|
||||
const val group = "com.dorkbox"
|
||||
const val version = "1.4.1"
|
||||
const val version = "2.0"
|
||||
|
||||
// set as project.ext
|
||||
const val name = "Gradle Licensing Plugin"
|
||||
|
@ -52,17 +51,7 @@ object Extras {
|
|||
///// assign 'Extras'
|
||||
///////////////////////////////
|
||||
GradleUtils.load("$projectDir/../../gradle.properties", Extras)
|
||||
description = Extras.description
|
||||
group = Extras.group
|
||||
version = Extras.version
|
||||
|
||||
licensing {
|
||||
license(License.APACHE_2) {
|
||||
author(Extras.vendor)
|
||||
url(Extras.url)
|
||||
note(Extras.description)
|
||||
}
|
||||
}
|
||||
GradleUtils.fixIntellijPaths()
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
|
@ -83,14 +72,18 @@ sourceSets {
|
|||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal() // this must be first!
|
||||
jcenter()
|
||||
maven {
|
||||
url = uri("https://plugins.gradle.org/m2/")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// the kotlin version is taken from the plugin, so it is not necessary to set it here
|
||||
compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin")
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin")
|
||||
|
||||
implementation ("com.dorkbox:Version:1.0")
|
||||
implementation("com.dorkbox:Version:1.2")
|
||||
}
|
||||
|
||||
java {
|
||||
|
@ -152,3 +145,4 @@ pluginBundle {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
@ -0,0 +1,301 @@
|
|||
package dorkbox.license
|
||||
|
||||
import License
|
||||
import com.dorkbox.version.Version
|
||||
import org.gradle.api.IllegalDependencyNotation
|
||||
|
||||
/**
|
||||
* Creates a license chain, based on the mavenId, so other license info can be looked up backed on THAT maven ID
|
||||
*
|
||||
* For example...
|
||||
* :: "com.dorkbox:Version:1.0", with license data as APACHE_2
|
||||
* :: "com.dorkbox:Version:2.0", with license data as GPL_3
|
||||
* "com.dorkbox:Version:1.0" -> it will return APACHE_2
|
||||
* "com.dorkbox:Version:2.0" -> it will return GPL_3
|
||||
* "com.dorkbox:Version:3.0" -> it will return GPL_3
|
||||
*
|
||||
* :: "com.dorkbox:Version", with license data as APACHE_2
|
||||
* "com.dorkbox:Version:1.0" -> it will return APACHE_2
|
||||
* "com.dorkbox:Version:2.0" -> it will return APACHE_2
|
||||
* "com.dorkbox:Version:3.0" -> it will return APACHE_2
|
||||
*
|
||||
* This will return the Version project license info, because ALL group license info will be collapsed to a single license!! BE CAREFUL!
|
||||
* :: "com.dorkbox", with license data as APACHE_2
|
||||
* "com.dorkbox:Version:1.0" -> it will return APACHE_2
|
||||
* "com.dorkbox:Version:2.0" -> it will return APACHE_2
|
||||
* "com.dorkbox:Console:2.0" -> it will return APACHE_2, AND it will return the Version project license info!! DO NOT DO THIS!
|
||||
*
|
||||
*/
|
||||
data class LicenseChain(val mavenId: String, val licenseData: LicenseData)
|
||||
|
||||
object AppLicensing {
|
||||
// have to add the version this license applies from
|
||||
private val allLicenseData = mutableMapOf<String, MutableList<Pair<Version, LicenseData>>>()
|
||||
|
||||
// NOTE: the END copyright for these are determined by the DATE of the files!
|
||||
//
|
||||
// Super important! These are dependency projects -- The only requirement (in the most general, permissive way) is that we provide
|
||||
// attribution to each. HOWEVER, if there is a library that DOES NOT provide proper/correct attributions to THEMSELVES (meaning, they are
|
||||
// a modification to another library and do not credit that library) -- then we are not transversly in violation. THEY are in violation,
|
||||
// and not us, since we are correctly attributing their work (they are incorrectly attributing whatever THEY should).
|
||||
//
|
||||
// We DO NOT have to maintain a FULL HISTORY CHAIN of contributions of all dependent libraries -- only the library + license that we use.
|
||||
private val map = listOf(
|
||||
LicenseChain("org.jetbrains.kotlin",
|
||||
LicenseData("Kotlin", License.APACHE_2).apply {
|
||||
copyright(2000)
|
||||
author("JetBrains s.r.o. and Kotlin Programming Language contributors")
|
||||
url("https://github.com/JetBrains/kotlin")
|
||||
note("Kotlin Compiler, Test Data+Libraries, and Tools repository contain third-party code, to which different licenses may apply")
|
||||
note("See: https://github.com/JetBrains/kotlin/blob/master/license/README.md")
|
||||
}
|
||||
),
|
||||
LicenseChain("org.jetbrains:annotations",
|
||||
LicenseData("Java Annotations", License.APACHE_2).apply {
|
||||
description("Annotations for JVM-based languages")
|
||||
url("https://github.com/JetBrains/java-annotations")
|
||||
copyright(2000)
|
||||
author("JetBrains s.r.o.")
|
||||
}
|
||||
),
|
||||
LicenseChain("org.jetbrains.kotlinx",
|
||||
LicenseData("kotlinx.coroutines", License.APACHE_2).apply {
|
||||
description("Library support for Kotlin coroutines with multiplatform support")
|
||||
url("https://github.com/Kotlin/kotlinx.coroutines")
|
||||
copyright(2000)
|
||||
author("JetBrains s.r.o.")
|
||||
}
|
||||
),
|
||||
LicenseChain("io.github.microutils:kotlin-logging",
|
||||
LicenseData("kotlin-logging", License.APACHE_2).apply {
|
||||
description("Lightweight logging framework for Kotlin")
|
||||
url("https://github.com/MicroUtils/kotlin-logging")
|
||||
copyright(2016)
|
||||
author("Ohad Shai")
|
||||
}
|
||||
),
|
||||
LicenseChain("org.slf4j:slf4j-api",
|
||||
LicenseData("SLF4J", License.MIT).apply {
|
||||
description("Simple facade or abstraction for various logging frameworks")
|
||||
url("http://www.slf4j.org")
|
||||
copyright(2004)
|
||||
author("QOS.ch")
|
||||
}
|
||||
),
|
||||
LicenseChain("net.java.dev.jna:jna:1.0",
|
||||
LicenseData("JNA", License.LGPLv2_1).apply {
|
||||
description("Simplified native library access for Java.")
|
||||
url("https://github.com/twall/jna")
|
||||
copyright(2004)
|
||||
author("Timothy Wall")
|
||||
}
|
||||
),
|
||||
LicenseChain("net.java.dev.jna:jna:4.0",
|
||||
LicenseData("JNA", License.APACHE_2).apply {
|
||||
description("Simplified native library access for Java.")
|
||||
url("https://github.com/twall/jna")
|
||||
copyright(2013)
|
||||
author("Timothy Wall")
|
||||
}
|
||||
),
|
||||
|
||||
LicenseChain("net.java.dev.jna:jna-platform:1.0",
|
||||
LicenseData("JNA-Platform", License.LGPLv2_1).apply {
|
||||
description("Mappings for a number of commonly used platform functions")
|
||||
url("https://github.com/twall/jna")
|
||||
copyright(2013)
|
||||
author("Timothy Wall")
|
||||
}
|
||||
),
|
||||
LicenseChain("net.java.dev.jna:jna-platform:4.0",
|
||||
LicenseData("JNA-Platform", License.APACHE_2).apply {
|
||||
description("Mappings for a number of commonly used platform functions")
|
||||
url("https://github.com/twall/jna")
|
||||
copyright(2013)
|
||||
author("Timothy Wall")
|
||||
}
|
||||
),
|
||||
|
||||
LicenseChain("com.hierynomus:sshj",
|
||||
LicenseData("SSHJ", License.APACHE_2).apply {
|
||||
description("SSHv2 library for Java")
|
||||
url("https://github.com/hierynomus/sshj")
|
||||
copyright(2009)
|
||||
author("Jeroen van Erp")
|
||||
author("SSHJ Contributors")
|
||||
|
||||
extra("Apache MINA", License.APACHE_2) {
|
||||
it.url("https://mina.apache.org/sshd-project/")
|
||||
it.copyright(2003).to(2017)
|
||||
it.author("The Apache Software Foundation")
|
||||
}
|
||||
extra("Apache Commons-Net", License.APACHE_2) {
|
||||
it.url("https://commons.apache.org/proper/commons-net/")
|
||||
it.copyright(2001).to(2017)
|
||||
it.author("The Apache Software Foundation")
|
||||
}
|
||||
extra("JZlib", License.APACHE_2) {
|
||||
it.url("http://www.jcraft.com/jzlib")
|
||||
it.copyright(2002).to(2008)
|
||||
it.author("Atsuhiko Yamanaka")
|
||||
it.author("JCraft, Inc.")
|
||||
}
|
||||
extra("Bouncy Castle Crypto", License.APACHE_2) {
|
||||
it.url("http://www.bouncycastle.org")
|
||||
it.copyright(2000).to(2006)
|
||||
it.author("The Legion of the Bouncy Castle Inc")
|
||||
}
|
||||
extra("ed25519-java", License.CC0) {
|
||||
it.url("https://github.com/str4d/ed25519-java")
|
||||
it.author("https://github.com/str4d")
|
||||
}
|
||||
}
|
||||
),
|
||||
|
||||
LicenseChain("org.bouncycastle",
|
||||
LicenseData("Bouncy Castle Crypto", License.APACHE_2).apply {
|
||||
description("Lightweight cryptography API and JCE Extension")
|
||||
copyright(2000)
|
||||
author("The Legion of the Bouncy Castle Inc")
|
||||
url("http://www.bouncycastle.org")
|
||||
}
|
||||
),
|
||||
|
||||
LicenseChain("com.fasterxml.uuid:java-uuid-generator",
|
||||
LicenseData("Java Uuid Generator", License.APACHE_2).apply {
|
||||
description("A set of Java classes for working with UUIDs")
|
||||
copyright(2002)
|
||||
author("Tatu Saloranta (tatu.saloranta@iki.fi)")
|
||||
author("Contributors. See source release-notes/CREDITS")
|
||||
url("https://github.com/cowtowncoder/java-uuid-generator")
|
||||
}
|
||||
),
|
||||
LicenseChain("org.tukaani:xz",
|
||||
LicenseData("XZ for Java", License.CC0).apply {
|
||||
description("Complete implementation of XZ data compression in pure Java")
|
||||
author("Lasse Collin")
|
||||
author("Igor Pavlov")
|
||||
url("https://tukaani.org/xz/java.html")
|
||||
}
|
||||
),
|
||||
LicenseChain("io.netty",
|
||||
LicenseData("Netty", License.APACHE_2).apply {
|
||||
description("An event-driven asynchronous network application framework")
|
||||
copyright(2014)
|
||||
author("The Netty Project")
|
||||
author("Contributors. See source NOTICE")
|
||||
url("https://netty.io")
|
||||
}
|
||||
),
|
||||
LicenseChain("org.lwjgl:lwjgl-xxhash",
|
||||
LicenseData("Lightweight Java Game Library", License.BSD_3).apply {
|
||||
description("Java library that enables cross-platform access to popular native APIs")
|
||||
copyright(2012)
|
||||
author("Lightweight Java Game Library")
|
||||
url("https://github.com/LWJGL/lwjgl3")
|
||||
}
|
||||
),
|
||||
LicenseChain("net.jodah:typetools",
|
||||
LicenseData("TypeTools", License.APACHE_2).apply {
|
||||
description("A simple, zero-dependency library for working with types. Supports Java 1.6+ and Android.")
|
||||
copyright(2010)
|
||||
author("Jonathan Halterman and friends")
|
||||
url("https://github.com/jhalterman/typetools")
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
)
|
||||
// NOTE: the END copyright for these are determined by the DATE of the files!
|
||||
|
||||
|
||||
|
||||
init {
|
||||
map.forEach {
|
||||
license(it)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: generated license information copyright date is based on the DATE of the manifest file in the jar!
|
||||
fun getLicense(name: String): LicenseData? {
|
||||
var (moduleId, version) = getFromModuleName(name)
|
||||
|
||||
var internalList = allLicenseData[moduleId]
|
||||
var offset = 1
|
||||
while (internalList == null && offset < 3) {
|
||||
// try using simpler module info (since we can specify more generic license info)
|
||||
val thing = getFromModuleName(name, offset++)
|
||||
moduleId = thing.first
|
||||
version = thing.second
|
||||
|
||||
|
||||
internalList = allLicenseData[moduleId]
|
||||
}
|
||||
|
||||
// println(" - found list entries")
|
||||
internalList?.forEach {
|
||||
// println(" - checking $version against ${it.first}")
|
||||
// if MY version is >= to the version saved in our internal DB, then we use that license
|
||||
if (version.greaterThanOrEqualTo(it.first)) {
|
||||
return it.second
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
private fun license(licenseChain: LicenseChain) {
|
||||
val (moduleId, version) = getFromModuleName(licenseChain.mavenId)
|
||||
|
||||
val internalList = allLicenseData.getOrPut(moduleId) { mutableListOf() }
|
||||
internalList.add(Pair(version, licenseChain.licenseData))
|
||||
|
||||
// largest version number is first, smallest version number is last.
|
||||
// when checking WHAT license applies to WHICH version, we start at the largest (so we stop looking at the first match <= to us)
|
||||
// this is because if X.Y=MIT, then X.Y+1=MIT and X+1.Y+1=MIT
|
||||
// a real-world example is JNA. JNA v3 -> GPL, JNA v4 -> APACHE
|
||||
|
||||
// to demonstrate:
|
||||
// v1 -> GPL
|
||||
// v4 -> MIT
|
||||
// v8 -> APACHE
|
||||
|
||||
// We are version 6, so we are MIT
|
||||
// We are version 12, so we are APACHE
|
||||
// We are version 2, so we are GPL
|
||||
internalList.sortByDescending { it.first.toString() }
|
||||
}
|
||||
|
||||
private fun getFromModuleName(fullName: String, override: Int = 0) : Pair<String, Version> {
|
||||
val split = fullName.split(':')
|
||||
|
||||
val moduleId = when ((split.size - override).coerceAtLeast(0)) {
|
||||
0 -> {
|
||||
fullName
|
||||
}
|
||||
1 -> {
|
||||
split[0]
|
||||
}
|
||||
else -> {
|
||||
val group = split[0]
|
||||
val name = split[1]
|
||||
"$group:$name"
|
||||
}
|
||||
}
|
||||
|
||||
val version = when (split.size) {
|
||||
3 -> Version.from(split[2])
|
||||
else -> Version.from(0)
|
||||
}
|
||||
|
||||
|
||||
if (split.size > 4) {
|
||||
throw IllegalDependencyNotation("Supplied String module notation '${moduleId}' is invalid. " +
|
||||
"Example notations: 'com.dorkbox:Version:1.0', 'org.mockito:mockito-core:1.9.5:javadoc'")
|
||||
}
|
||||
|
||||
// println("Got: $moduleId, $version from $fullName")
|
||||
return Pair(moduleId, version)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
package dorkbox.license
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.ResolvedArtifact
|
||||
import org.gradle.api.artifacts.ResolvedDependency
|
||||
import java.io.*
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.util.*
|
||||
import java.util.concurrent.Executor
|
||||
import java.util.zip.ZipFile
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
class DependencyScanner(private val project: Project, private val extension: Licensing) {
|
||||
fun scanForLicenseData() {
|
||||
// The default configuration extends from the runtime configuration, which means that it contains all the dependencies and artifacts of the runtime configuration, and potentially more.
|
||||
// THIS MUST BE IN "afterEvaluate" or run from a specific task.
|
||||
// Using the "runtime" classpath (weirdly) DOES NOT WORK. Only "default" works.
|
||||
|
||||
val projectDependencies = mutableListOf<Dependency>()
|
||||
val existingNames = mutableSetOf<String>()
|
||||
project.configurations.getByName("default").resolvedConfiguration.firstLevelModuleDependencies.forEach { dep ->
|
||||
// we know the FIRST series will exist
|
||||
val makeDepTree = makeDepTree(dep, existingNames)
|
||||
if (makeDepTree != null) {
|
||||
// it's only null if we've ALREADY scanned it
|
||||
projectDependencies.add(makeDepTree)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val missingLicenseInfo = mutableListOf<Dependency>()
|
||||
val actuallyMissingLicenseInfo = mutableListOf<Dependency>()
|
||||
|
||||
if (extension.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)
|
||||
println("\tLicense Detection")
|
||||
print("\t\tScanning for preloaded license data...")
|
||||
|
||||
val primaryLicense = extension.licenses.first()
|
||||
var found = false
|
||||
|
||||
projectDependencies.forEach { info: Dependency ->
|
||||
val license: LicenseData? = AppLicensing.getLicense(info.mavenId())
|
||||
if (license == null) {
|
||||
missingLicenseInfo.add(info)
|
||||
} else {
|
||||
if (!primaryLicense.extras.contains(license)) {
|
||||
// get the OLDEST date from the artifacts and use that as the copyright date
|
||||
var oldestDate = 0L
|
||||
info.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())
|
||||
}
|
||||
}
|
||||
|
||||
if (oldestDate == 0L) {
|
||||
oldestDate = Instant.now().toEpochMilli()
|
||||
}
|
||||
|
||||
val year = Date(oldestDate).toInstant().atZone(ZoneId.systemDefault()).toLocalDate().year
|
||||
if (license.copyrights.size == 1) {
|
||||
CopyrightRange(license.copyrights.first(), license.copyrights).to(year)
|
||||
} else {
|
||||
license.copyright(year)
|
||||
}
|
||||
|
||||
primaryLicense.extras.add(license)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
found = false
|
||||
println(" found")
|
||||
} else {
|
||||
println()
|
||||
}
|
||||
|
||||
|
||||
print("\t\tScanning for embedded license data...")
|
||||
|
||||
if (missingLicenseInfo.isNotEmpty()) {
|
||||
missingLicenseInfo.forEach { info ->
|
||||
// see if we have it in the dependency jar
|
||||
val output = ByteArrayOutputStream()
|
||||
var missingFound = false
|
||||
info.artifacts.forEach { artifact ->
|
||||
ZipFile(artifact.file).use {
|
||||
try {
|
||||
val ze = it.getEntry(LicenseInjector.LICENSE_BLOB)
|
||||
if (ze != null) {
|
||||
it.getInputStream(ze).use { licenseStream ->
|
||||
licenseStream.copyTo(output)
|
||||
missingFound = true
|
||||
found = true
|
||||
return@forEach
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ignored: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!missingFound) {
|
||||
actuallyMissingLicenseInfo.add(info)
|
||||
} else {
|
||||
// println("Found license info in: $info")
|
||||
|
||||
ObjectInputStream(ByteArrayInputStream(output.toByteArray())).use { ois ->
|
||||
val size = ois.readInt()
|
||||
for (i in 0 until size) {
|
||||
val license = LicenseData("", License.CUSTOM)
|
||||
license.readObject(ois)
|
||||
|
||||
// println("Adding license: $license")
|
||||
if (!primaryLicense.extras.contains(license)) {
|
||||
primaryLicense.extras.add(license)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (found) {
|
||||
found = false
|
||||
println(" found")
|
||||
} else {
|
||||
println()
|
||||
}
|
||||
|
||||
if (actuallyMissingLicenseInfo.isNotEmpty()) {
|
||||
println("\t\tLicense information is missing for the following. Please submit an issue with this information to include it in future license scans\n")
|
||||
|
||||
actuallyMissingLicenseInfo.forEach { missingDep ->
|
||||
val flatDependencies = mutableSetOf<Dependency>()
|
||||
missingDep.children.forEach {
|
||||
flattenDep(it, flatDependencies)
|
||||
}
|
||||
println("\t\t ${missingDep.mavenId()} ${flatDependencies.map { it.mavenId() }}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// how to resolve dependencies
|
||||
// NOTE: it is possible, when we have a project DEPEND on an older version of that project (ie: bootstrapped from an older version)
|
||||
// we can have infinite recursion.
|
||||
// This is a problem, so we limit how much a dependency can show up the the tree
|
||||
private fun makeDepTree(dep: ResolvedDependency, existingNames: MutableSet<String>): Dependency? {
|
||||
val module = dep.module.id
|
||||
val group = module.group
|
||||
val name = module.name
|
||||
val version = module.version
|
||||
|
||||
if (!existingNames.contains("$group:$name")) {
|
||||
// println("Searching: $group:$name:$version")
|
||||
val artifacts: List<DependencyInfo> = dep.moduleArtifacts.map { artifact: ResolvedArtifact ->
|
||||
val artifactModule = artifact.moduleVersion.id
|
||||
DependencyInfo(artifactModule.group, artifactModule.name, artifactModule.version, artifact.file.absoluteFile)
|
||||
}
|
||||
|
||||
val children = mutableListOf<Dependency>()
|
||||
dep.children.forEach {
|
||||
existingNames.add("$group:$name")
|
||||
val makeDep = makeDepTree(it, existingNames)
|
||||
if (makeDep != null) {
|
||||
children.add(makeDep)
|
||||
}
|
||||
}
|
||||
|
||||
return Dependency(group, name, version, artifacts, children.toList())
|
||||
}
|
||||
|
||||
// we already have this dependency in our chain.
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten the dependency children
|
||||
*/
|
||||
fun flattenDeps(dep: Dependency): List<Dependency> {
|
||||
val flatDeps = mutableSetOf<Dependency>()
|
||||
flattenDep(dep, flatDeps)
|
||||
return flatDeps.toList()
|
||||
}
|
||||
|
||||
private fun flattenDep(dep: Dependency, flatDeps: MutableSet<Dependency>) {
|
||||
flatDeps.add(dep)
|
||||
dep.children.forEach {
|
||||
flattenDep(it, flatDeps)
|
||||
}
|
||||
}
|
||||
|
||||
data class Dependency(val group: String,
|
||||
val name: String,
|
||||
val version: String,
|
||||
val artifacts: List<DependencyInfo>,
|
||||
val children: List<Dependency>) {
|
||||
|
||||
fun mavenId(): String {
|
||||
return "$group:$name:$version"
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return mavenId()
|
||||
}
|
||||
}
|
||||
|
||||
data class DependencyInfo(val group: String, val name: String, val version: String, val file: File) {
|
||||
val id: String
|
||||
get() {
|
||||
return "$group:$name:$version"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,9 +16,27 @@
|
|||
package dorkbox.license
|
||||
|
||||
import License
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.GradleException
|
||||
import java.io.IOException
|
||||
import java.io.ObjectInputStream
|
||||
import java.io.ObjectOutputStream
|
||||
import java.time.LocalDate
|
||||
|
||||
class LicenseData(val name: String, val license: License) : Comparable<LicenseData> {
|
||||
|
||||
class LicenseData(var name: String, var license: License) : java.io.Serializable, Comparable<LicenseData> {
|
||||
/**
|
||||
* Description/title
|
||||
*/
|
||||
var description = ""
|
||||
|
||||
/**
|
||||
* If not specified, will be blank after the name
|
||||
*/
|
||||
fun description(description: String) {
|
||||
this.description = description
|
||||
}
|
||||
|
||||
/**
|
||||
* Copyright
|
||||
*/
|
||||
|
@ -27,8 +45,9 @@ class LicenseData(val name: String, val license: License) : Comparable<LicenseDa
|
|||
/**
|
||||
* If not specified, will use the current year
|
||||
*/
|
||||
fun copyright(copyright: Int) {
|
||||
fun copyright(copyright: Int = LocalDate.now().year): CopyrightRange {
|
||||
copyrights.add(copyright)
|
||||
return CopyrightRange(copyright, copyrights)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,6 +86,29 @@ class LicenseData(val name: String, val license: License) : Comparable<LicenseDa
|
|||
authors.add(author)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra License information
|
||||
*/
|
||||
val extras = mutableListOf<LicenseData>()
|
||||
|
||||
/**
|
||||
* Specifies the extra license information for this project
|
||||
*/
|
||||
fun extra(name: String, license: License, licenseAction: Action<LicenseData>) {
|
||||
val licenseData = LicenseData(name, license)
|
||||
licenseAction.execute(licenseData)
|
||||
extras.add(licenseData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the extra license information for this project
|
||||
*/
|
||||
fun extra(name: String, license: License, licenseAction: (LicenseData) -> Unit) {
|
||||
val licenseData = LicenseData(name, license)
|
||||
licenseAction(licenseData)
|
||||
extras.add(licenseData)
|
||||
}
|
||||
|
||||
/**
|
||||
* ignore case when sorting these
|
||||
*/
|
||||
|
@ -86,10 +128,12 @@ class LicenseData(val name: String, val license: License) : Comparable<LicenseDa
|
|||
|
||||
if (name != other.name) return false
|
||||
if (license != other.license) return false
|
||||
if (description != other.description) return false
|
||||
if (copyrights != other.copyrights) return false
|
||||
if (urls != other.urls) return false
|
||||
if (notes != other.notes) return false
|
||||
if (authors != other.authors) return false
|
||||
if (extras != other.extras) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -97,30 +141,125 @@ class LicenseData(val name: String, val license: License) : Comparable<LicenseDa
|
|||
override fun hashCode(): Int {
|
||||
var result = name.hashCode()
|
||||
result = 31 * result + license.hashCode()
|
||||
result = 31 * result + description.hashCode()
|
||||
result = 31 * result + copyrights.hashCode()
|
||||
result = 31 * result + urls.hashCode()
|
||||
result = 31 * result + notes.hashCode()
|
||||
result = 31 * result + authors.hashCode()
|
||||
result = 31 * result + extras.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun writeObject(s: ObjectOutputStream) {
|
||||
s.writeUTF(name)
|
||||
s.writeUTF(license.name)
|
||||
s.writeUTF(description)
|
||||
|
||||
s.writeInt(copyrights.size)
|
||||
copyrights.forEach {
|
||||
s.writeInt(it)
|
||||
}
|
||||
|
||||
s.writeInt(urls.size)
|
||||
urls.forEach {
|
||||
s.writeUTF(it)
|
||||
}
|
||||
|
||||
s.writeInt(notes.size)
|
||||
notes.forEach {
|
||||
s.writeUTF(it)
|
||||
}
|
||||
|
||||
s.writeInt(authors.size)
|
||||
authors.forEach {
|
||||
s.writeUTF(it)
|
||||
}
|
||||
|
||||
s.writeInt(extras.size)
|
||||
if (extras.size > 0) {
|
||||
extras.forEach {
|
||||
it.writeObject(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Gradle only needs to serialize objects, so this isn't strictly needed
|
||||
@Throws(IOException::class)
|
||||
fun readObject(s: ObjectInputStream) {
|
||||
name = s.readUTF()
|
||||
license = License.valueOfLicenseName(s.readUTF())
|
||||
description = s.readUTF()
|
||||
|
||||
|
||||
val copyrightsSize = s.readInt()
|
||||
for (i in 1..copyrightsSize) {
|
||||
copyrights.add(s.readInt())
|
||||
}
|
||||
|
||||
val urlsSize = s.readInt()
|
||||
for (i in 1..urlsSize) {
|
||||
urls.add(s.readUTF())
|
||||
}
|
||||
|
||||
val notesSize = s.readInt()
|
||||
for (i in 1..notesSize) {
|
||||
notes.add(s.readUTF())
|
||||
}
|
||||
|
||||
val authorsSize = s.readInt()
|
||||
for (i in 1..authorsSize) {
|
||||
authors.add(s.readUTF())
|
||||
}
|
||||
|
||||
val extrasSize = s.readInt()
|
||||
for (i in 1..extrasSize) {
|
||||
val dep = LicenseData("", License.CUSTOM)
|
||||
dep.readObject(s) // can recursively create objects
|
||||
extras.add(dep)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LINE_SEPARATOR = System.getProperty("line.separator")
|
||||
private val newLineRegex = "\n".toRegex()
|
||||
private const val serialVersionUID = 1L
|
||||
|
||||
// NOTE: we ALWAYS use unix line endings!
|
||||
private const val NL = "\n"
|
||||
private const val HEADER = " - "
|
||||
private const val HEADR4 = " ---- "
|
||||
private const val SPACER3 = " "
|
||||
private const val SPACER4 = " "
|
||||
|
||||
private fun prefix(prefix: Int, builder: StringBuilder): StringBuilder {
|
||||
if (prefix == 0) {
|
||||
builder.append("")
|
||||
} else {
|
||||
for (i in 0 until prefix) {
|
||||
builder.append(" ")
|
||||
}
|
||||
}
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
private fun line(prefix: Int, builder: StringBuilder, vararg strings: Any) {
|
||||
prefix(prefix, builder)
|
||||
|
||||
strings.forEach {
|
||||
builder.append(it.toString())
|
||||
}
|
||||
|
||||
builder.append(NL)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LICENSE text file, as a combo of the listed licenses. Duplicates are removed.
|
||||
*/
|
||||
fun buildString(licenses: MutableList<LicenseData>): String {
|
||||
fun buildString(licenses: MutableList<LicenseData>, prefixOffset: Int = 0): String {
|
||||
val b = StringBuilder(256)
|
||||
|
||||
sortAndClean(licenses)
|
||||
|
||||
val NL = LINE_SEPARATOR
|
||||
val HEADER = " - "
|
||||
val SPACER = " "
|
||||
val SPACR1 = " "
|
||||
|
||||
var first = true
|
||||
|
||||
licenses.forEach { license ->
|
||||
|
@ -132,59 +271,72 @@ class LicenseData(val name: String, val license: License) : Comparable<LicenseDa
|
|||
b.append(NL).append(NL)
|
||||
}
|
||||
|
||||
b.append(HEADER).append(license.name).append(" - ").append(NL)
|
||||
|
||||
license.urls.forEach {
|
||||
b.append(SPACER).append(it).append(NL)
|
||||
}
|
||||
|
||||
b.append(SPACER).append("Copyright")
|
||||
if (license.copyrights.isEmpty()) {
|
||||
// append the current year
|
||||
b.append(" ").append(LocalDate.now().year)
|
||||
}
|
||||
else {
|
||||
license.copyrights.forEach {
|
||||
b.append(" ").append(it).append(",")
|
||||
}
|
||||
b.deleteCharAt(b.length-1)
|
||||
}
|
||||
|
||||
b.append(" - ").append(license.license.preferedName).append(NL)
|
||||
|
||||
license.authors.forEach {
|
||||
b.append(SPACR1).append(it).append(NL)
|
||||
}
|
||||
|
||||
if (license.license === License.CUSTOM) {
|
||||
license.notes.forEach {
|
||||
b.append(fixSpace(it, SPACER, 1)).append(NL)
|
||||
}
|
||||
}
|
||||
else {
|
||||
license.notes.forEach {
|
||||
b.append(SPACER).append(it).append(NL)
|
||||
}
|
||||
}
|
||||
buildLicenseString(b, license, prefixOffset)
|
||||
}
|
||||
|
||||
return b.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* fixes new lines that may appear in the text
|
||||
* @param text text to format
|
||||
* @param spacer how big will the space in front of each line be?
|
||||
*/
|
||||
private fun fixSpace(text: String, spacerSize: String, spacer: Int): String {
|
||||
val trimmedText = text.trim { it <= ' ' }
|
||||
// NOTE: we ALWAYS use unix line endings!
|
||||
private fun buildLicenseString(b: StringBuilder, license: LicenseData, prefixOffset: Int) {
|
||||
line(prefixOffset, b, HEADER, license.name, " - ", license.description)
|
||||
|
||||
var space = ""
|
||||
for (i in 0 until spacer) {
|
||||
space += spacerSize
|
||||
license.urls.forEach {
|
||||
line(prefixOffset, b, SPACER3, it)
|
||||
}
|
||||
|
||||
return space + trimmedText.replace(newLineRegex, "\n" + space)
|
||||
prefix(prefixOffset, b).append(SPACER3).append("Copyright")
|
||||
if (license.copyrights.isEmpty()) {
|
||||
// append the current year
|
||||
b.append(" ").append(LocalDate.now().year)
|
||||
}
|
||||
else if (license.copyrights.size == 1) {
|
||||
// this is 2001
|
||||
b.append(" ").append(license.copyrights.first())
|
||||
} else {
|
||||
// is this 2001,2002,2004,2014 <OR> 2001-2014
|
||||
val sumA = license.copyrights.sum()
|
||||
val sumB = license.copyrights.first().rangeTo(license.copyrights.last()).sum()
|
||||
if (sumA == sumB) {
|
||||
// this is 2001-2004
|
||||
b.append(" ").append(license.copyrights.first()).append("-").append(license.copyrights.last())
|
||||
} else {
|
||||
// this is 2001,2002,2004
|
||||
license.copyrights.forEach {
|
||||
b.append(" ").append(it).append(",")
|
||||
}
|
||||
b.deleteCharAt(b.length-1)
|
||||
}
|
||||
}
|
||||
b.append(HEADER).append(license.license.preferedName).append(NL)
|
||||
|
||||
license.authors.forEach {
|
||||
line(prefixOffset, b, SPACER4, it)
|
||||
}
|
||||
|
||||
if (license.license === License.CUSTOM) {
|
||||
line(prefixOffset, b, HEADR4)
|
||||
}
|
||||
|
||||
license.notes.forEach {
|
||||
line(prefixOffset, b, SPACER3, it)
|
||||
}
|
||||
|
||||
// now add the DEPENDENCY license information. This info is nested (and CAN contain duplicates from elsewhere!)
|
||||
if (license.extras.isNotEmpty()) {
|
||||
var isFirstExtra = true
|
||||
|
||||
line(prefixOffset, b, SPACER3, NL, "Extra license information")
|
||||
license.extras.forEach { extraLicense ->
|
||||
if (isFirstExtra) {
|
||||
isFirstExtra = false
|
||||
} else {
|
||||
b.append(NL)
|
||||
}
|
||||
|
||||
buildLicenseString(b, extraLicense, prefixOffset + 4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -195,7 +347,7 @@ class LicenseData(val name: String, val license: License) : Comparable<LicenseDa
|
|||
return
|
||||
}
|
||||
|
||||
// The FIRST one is always FIRST! (the rest are alphabetical)
|
||||
// The FIRST one is always FIRST! (the rest are alphabetical by name)
|
||||
val firstLicense = licenses[0]
|
||||
|
||||
// remove dupes
|
||||
|
@ -218,3 +370,21 @@ class LicenseData(val name: String, val license: License) : Comparable<LicenseDa
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CopyrightRange internal constructor(private val start: Int, private val copyrights: MutableList<Int>) {
|
||||
fun to(copyRight: Int) {
|
||||
if (start >= copyRight) {
|
||||
throw GradleException("Cannot have a start copyright date that is equal or greater than the `to` copyright date")
|
||||
}
|
||||
|
||||
val newStart = start+1
|
||||
if (newStart < copyRight) {
|
||||
// increment by 1, since the first part of the range is already added
|
||||
copyrights.addAll((newStart).rangeTo(copyRight))
|
||||
}
|
||||
}
|
||||
|
||||
fun toNow() {
|
||||
to(LocalDate.now().year)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,30 @@
|
|||
package dorkbox.license
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.ObjectOutputStream
|
||||
import javax.inject.Inject
|
||||
|
||||
internal open class LicenseInjector : DefaultTask() {
|
||||
|
||||
|
||||
internal open class LicenseInjector @Inject constructor(val extension: Licensing) : DefaultTask() {
|
||||
// only want to build these files once
|
||||
private var alreadyBuilt = false
|
||||
|
||||
lateinit var licenses: MutableList<LicenseData>
|
||||
lateinit var outputDir: File
|
||||
lateinit var rootDir: File
|
||||
companion object {
|
||||
const val LICENSE_FILE = "LICENSE"
|
||||
const val LICENSE_BLOB = "LICENSE.blob"
|
||||
}
|
||||
|
||||
@Input lateinit var licenses: MutableList<LicenseData>
|
||||
@OutputDirectory lateinit var outputDir: File
|
||||
@InputDirectory lateinit var rootDir: File
|
||||
|
||||
init {
|
||||
outputs.upToDateWhen {
|
||||
|
@ -25,6 +39,13 @@ internal open class LicenseInjector : DefaultTask() {
|
|||
}
|
||||
alreadyBuilt = true
|
||||
|
||||
// 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
|
||||
DependencyScanner(project, extension).scanForLicenseData()
|
||||
|
||||
// true if there was any work done
|
||||
didWork = buildLicenseFiles(outputDir, licenses) && buildLicenseFiles(rootDir, licenses)
|
||||
}
|
||||
|
@ -37,23 +58,26 @@ internal open class LicenseInjector : DefaultTask() {
|
|||
if (!outputDir.exists()) outputDir.mkdirs()
|
||||
|
||||
val licenseText = LicenseData.buildString(licenses)
|
||||
val licenseFile = File(outputDir, "LICENSE")
|
||||
val licenseFile = File(outputDir, LICENSE_FILE)
|
||||
|
||||
if (fileIsNotSame(licenseFile, licenseText)) {
|
||||
// write out the LICENSE and various license files
|
||||
needsToDoWork = true
|
||||
}
|
||||
|
||||
licenses.forEach {
|
||||
val license = it.license
|
||||
val file = File(outputDir, license.licenseFile)
|
||||
val sourceText = license.licenseText
|
||||
if (!needsToDoWork) {
|
||||
licenses.forEach {
|
||||
val license = it.license
|
||||
val file = File(outputDir, license.licenseFile)
|
||||
val sourceText = license.licenseText
|
||||
|
||||
if (fileIsNotSame(file, sourceText)) {
|
||||
needsToDoWork = true
|
||||
if (fileIsNotSame(file, sourceText)) {
|
||||
needsToDoWork = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return needsToDoWork
|
||||
}
|
||||
|
||||
|
@ -66,24 +90,41 @@ internal open class LicenseInjector : DefaultTask() {
|
|||
if (!outputDir.exists()) outputDir.mkdirs()
|
||||
|
||||
val licenseText = LicenseData.buildString(licenses)
|
||||
val licenseFile = File(outputDir, "LICENSE")
|
||||
if (licenseText.isEmpty()) {
|
||||
println("\tNo License information defined in the project. Unable to build license data")
|
||||
} else {
|
||||
val licenseFile = File(outputDir, LICENSE_FILE)
|
||||
val licenseBlob = File(outputDir, LICENSE_BLOB)
|
||||
|
||||
if (fileIsNotSame(licenseFile, licenseText)) {
|
||||
// write out the LICENSE files
|
||||
licenseFile.writeText(licenseText)
|
||||
hasDoneWork = true
|
||||
}
|
||||
if (fileIsNotSame(licenseFile, licenseText)) {
|
||||
// write out the LICENSE files
|
||||
licenseFile.writeText(licenseText)
|
||||
|
||||
licenses.forEach {
|
||||
val license = it.license
|
||||
val file = File(outputDir, license.licenseFile)
|
||||
val sourceText = license.licenseText
|
||||
// save off the blob, so we can check when reading dependencies if we can
|
||||
// import this license info as extra license info for the project
|
||||
ObjectOutputStream(FileOutputStream(licenseBlob)).use { oos ->
|
||||
oos.writeInt(licenses.size)
|
||||
|
||||
licenses.forEach {
|
||||
it.writeObject(oos)
|
||||
}
|
||||
}
|
||||
|
||||
if (fileIsNotSame(file, sourceText)) {
|
||||
// write out the various license text files
|
||||
file.writeText(sourceText)
|
||||
hasDoneWork = true
|
||||
}
|
||||
|
||||
licenses.forEach {
|
||||
val license = it.license
|
||||
val file = File(outputDir, license.licenseFile)
|
||||
val sourceText = license.licenseText
|
||||
|
||||
if (fileIsNotSame(file, sourceText)) {
|
||||
// write out the various license text files
|
||||
file.writeText(sourceText)
|
||||
|
||||
hasDoneWork = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hasDoneWork
|
||||
|
|
|
@ -19,12 +19,21 @@ import License
|
|||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.ResolvedArtifact
|
||||
import org.gradle.api.artifacts.ResolvedDependency
|
||||
import org.gradle.api.plugins.JavaPlugin
|
||||
import org.gradle.api.publish.PublishingExtension
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.gradle.api.tasks.bundling.AbstractArchiveTask
|
||||
import org.gradle.api.tasks.compile.AbstractCompile
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import java.io.File
|
||||
import java.io.*
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.util.*
|
||||
import java.util.zip.ZipFile
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* License definition and management plugin for the Gradle build system
|
||||
|
@ -35,9 +44,9 @@ class LicensePlugin : Plugin<Project> {
|
|||
val outputDir = File(project.buildDir, "licensing")
|
||||
|
||||
// Create the Plugin extension object (for users to configure our execution).
|
||||
val extension = project.extensions.create(Licensing.NAME, Licensing::class.java, project, outputDir)
|
||||
val extension: Licensing = project.extensions.create(Licensing.NAME, Licensing::class.java, project, outputDir)
|
||||
|
||||
val licenseInjector = project.tasks.create("generateLicenseFiles", LicenseInjector::class.java).apply {
|
||||
val licenseInjector = project.tasks.create("generateLicenseFiles", LicenseInjector::class.java, extension).apply {
|
||||
group = "other"
|
||||
}
|
||||
|
||||
|
@ -45,21 +54,17 @@ class LicensePlugin : Plugin<Project> {
|
|||
licenseInjector.rootDir = project.rootDir
|
||||
licenseInjector.licenses = extension.licenses
|
||||
|
||||
// the task will only build files that it needs to (and will only run once)
|
||||
project.tasks.forEach {
|
||||
when (it) {
|
||||
is KotlinCompile -> it.dependsOn += licenseInjector
|
||||
is AbstractCompile -> it.dependsOn += licenseInjector
|
||||
is AbstractArchiveTask -> it.dependsOn += licenseInjector
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
project.afterEvaluate { prj ->
|
||||
// collect all of the dependencies
|
||||
project.configurations.asIterable().forEach { extension.projectDependencies.addAll(it.dependencies) }
|
||||
|
||||
// the task will only build files that it needs to (and will only run once)
|
||||
project.tasks.forEach {
|
||||
when (it) {
|
||||
is AbstractCompile -> it.dependsOn(licenseInjector)
|
||||
is AbstractArchiveTask -> it.dependsOn(licenseInjector)
|
||||
}
|
||||
}
|
||||
|
||||
project.configurations.asIterable().forEach { extension.projectDependencies.addAll(it.dependencies) }
|
||||
|
||||
val licensing = extension.licenses
|
||||
if (licensing.isNotEmpty()) {
|
||||
|
@ -87,7 +92,7 @@ class LicensePlugin : Plugin<Project> {
|
|||
|
||||
// only include license "notes" if we are a custom license **which is the license itself**
|
||||
if (license == License.CUSTOM) {
|
||||
val notes = licenseData.notes.asSequence().joinToString("")
|
||||
val notes = licenseData.notes.joinToString("")
|
||||
newLic.comments.set(notes)
|
||||
}
|
||||
}
|
||||
|
@ -101,19 +106,6 @@ class LicensePlugin : Plugin<Project> {
|
|||
// there aren't always maven publishing used
|
||||
}
|
||||
|
||||
|
||||
// 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 projectLicenses = mutableSetOf<String>()
|
||||
for (dependency in extension.projectDependencies) {
|
||||
println("DEP: " + dependency.group + dependency.name)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// the task will only build files that it needs to (and will only run once)
|
||||
project.tasks.forEach {
|
||||
if (it is AbstractArchiveTask) {
|
||||
|
@ -128,3 +120,5 @@ class LicensePlugin : Plugin<Project> {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -34,7 +34,9 @@ open class Licensing(project: Project, private val outputDir: File) {
|
|||
fun output() : List<File> {
|
||||
val files = mutableSetOf<File>()
|
||||
|
||||
files.add(File(outputDir, "LICENSE"))
|
||||
files.add(File(outputDir, LicenseInjector.LICENSE_FILE))
|
||||
files.add(File(outputDir, LicenseInjector.LICENSE_BLOB))
|
||||
|
||||
licenses.forEach {
|
||||
files.add(File(outputDir, it.license.licenseFile))
|
||||
}
|
||||
|
@ -56,18 +58,18 @@ open class Licensing(project: Project, private val outputDir: File) {
|
|||
/**
|
||||
* Adds a new license section using the project's name as the assigned name
|
||||
*/
|
||||
fun license(license: License, licenseData: Action<LicenseData>) {
|
||||
val licenseAction = LicenseData(projectName, license)
|
||||
licenseData.execute(licenseAction)
|
||||
licenses.add(licenseAction)
|
||||
fun license(license: License, licenseAction: Action<LicenseData>) {
|
||||
val licenseData = LicenseData(projectName, license)
|
||||
licenseAction.execute(licenseData)
|
||||
licenses.add(licenseData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new license section using the specified name as the assigned name
|
||||
*/
|
||||
fun license(name: String, license: License, licenseData: Action<LicenseData>) {
|
||||
val licenseAction = LicenseData(name, license)
|
||||
licenseData.execute(licenseAction)
|
||||
licenses.add(licenseAction)
|
||||
fun license(name: String, license: License, licenseAction: Action<LicenseData>) {
|
||||
val licenseData = LicenseData(name, license)
|
||||
licenseAction.execute(licenseData)
|
||||
licenses.add(licenseData)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue