Skip to content

Commit

Permalink
optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
jaredsburrows committed Jan 31, 2024
1 parent 91ee1f2 commit 83386d2
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 122 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,12 @@ import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import java.io.File
import java.net.URI
import java.net.URL
import java.util.Locale
import java.util.UUID

/** A [org.gradle.api.Task] that creates HTML and JSON reports of the current projects dependencies. */
internal open class LicenseReportTask : DefaultTask() { // tasks can't be final
internal open class LicenseReportTask : DefaultTask() {

@Input
var assetDirs = emptyList<File>()
Expand Down Expand Up @@ -187,11 +186,13 @@ internal open class LicenseReportTask : DefaultTask() { // tasks can't be final

// Resolve the POM artifacts
configurationSet
.asSequence()
.filter { it.isCanBeResolved }
.map { it.resolvedConfiguration }
.map { it.lenientConfiguration }
.map { it.allModuleDependencies }
.flatMap { getResolvedArtifactsFromResolvedDependencies(it) }
.toList()
.forEach { artifact ->
val id = artifact.moduleVersion.id
val gav = "${id.group}:${id.name}:${id.version}@pom"
Expand Down Expand Up @@ -235,8 +236,8 @@ internal open class LicenseReportTask : DefaultTask() { // tasks can't be final
val module = artifact.moduleVersion.id
val project =
Model().apply {
this.groupId = module.group.orEmpty().trim()
this.artifactId = module.name.orEmpty().trim()
this.groupId = module.group.trim()
this.artifactId = module.name.trim()
this.version = model.pomVersion(mavenReader, pomFile, configurations, dependencies)
this.name = model.pomName()
this.description = model.pomDescription()
Expand All @@ -255,14 +256,12 @@ internal open class LicenseReportTask : DefaultTask() { // tasks can't be final

private fun getResolvedArtifactsFromResolvedDependencies(
resolvedDependencies: Set<ResolvedDependency>,
skipSet: MutableSet<ResolvedDependency> = hashSetOf<ResolvedDependency>(),
skipSet: MutableSet<ResolvedDependency> = hashSetOf(),
): Set<ResolvedArtifact> {
val resolvedArtifacts = hashSetOf<ResolvedArtifact>()
resolvedDependencies.forEach { resolvedDependency ->
if (skipSet.contains(resolvedDependency)) {
return@forEach
} else {
skipSet.add(resolvedDependency)
return resolvedDependencies.flatMap { resolvedDependency ->
if (!skipSet.add(resolvedDependency)) {
// If the dependency is already in skipSet, skip it
return@flatMap emptySet<ResolvedArtifact>()
}

try {
Expand All @@ -274,20 +273,18 @@ internal open class LicenseReportTask : DefaultTask() { // tasks can't be final
* library project itself and enumerate its dependencies.
*/
"unspecified" ->
resolvedArtifacts +=
getResolvedArtifactsFromResolvedDependencies(
resolvedDependency.children,
skipSet,
)

else -> resolvedArtifacts += resolvedDependency.allModuleArtifacts
// Recursively collect artifacts from the children of unresolved dependencies
getResolvedArtifactsFromResolvedDependencies(resolvedDependency.children, skipSet)
else ->
// Collect artifacts from the resolved dependency
resolvedDependency.allModuleArtifacts
}
} catch (e: Exception) {
logger.warn("Failed to process '${resolvedDependency.name}': ${e.shortMessage()}")
logger.debug("Failed to process '${resolvedDependency.name}'", e)
emptySet()
}
}
return resolvedArtifacts
}.toSet()
}

/** Use Parent POM information when individual dependency license information is missing. */
Expand Down Expand Up @@ -389,7 +386,7 @@ internal open class LicenseReportTask : DefaultTask() { // tasks can't be final
return ""
}

val version = model.version.orEmpty().trim()
val version = model.pomVersion()
if (version.isNotEmpty()) {
return version.trim()
}
Expand All @@ -412,114 +409,83 @@ internal open class LicenseReportTask : DefaultTask() { // tasks can't be final
dependencies: DependencyHandler,
): List<License> {
if (pomFile.isNullOrEmpty()) {
return mutableListOf()
return emptyList()
}
val model = mavenReader.read(ReaderFactory.newXmlReader(pomFile), false)

// If the POM is missing a name, do not record it
val name = model.pomName()
if (name.isEmpty()) {
logger.warn("POM file is missing a name: $pomFile")
return mutableListOf()
return emptyList()
}

if (ANDROID_SUPPORT_GROUP_ID == model.groupId.orEmpty().trim()) {
return listOf(
License().apply {
this.name = APACHE_LICENSE_NAME
url = APACHE_LICENSE_URL
},
)
return listOf(License().apply {
this.name = APACHE_LICENSE_NAME
url = APACHE_LICENSE_URL
})
}

// License information found
if (model.licenses.orEmpty().isNotEmpty()) {
val licenses = mutableListOf<License>()
model.licenses.orEmpty().forEach { license ->
val licenseName = license.name.orEmpty().trim()
val licenseUrl = license.url.orEmpty().trim()
if (licenseUrl.isUrlValid()) {
licenses +=
License().apply {
this.name = licenseName
url = licenseUrl
}
}
return model.licenses.orEmpty().filter { it.url.orEmpty().trim().isUrlValid() }.map { license ->
License().apply {
this.name = license.name.orEmpty().trim()
this.url = license.url.orEmpty().trim()
}
return licenses
}

logger.info("Project, $name, has no license in POM file.")

if (model.parent?.artifactId.orEmpty().trim().isNotEmpty()) {
return findLicenses(
mavenReader,
getParentPomFile(model, configurations, dependencies),
configurations,
dependencies,
)
}.ifEmpty {
logger.info("Project, $name, has no license in POM file.")
model.parent?.artifactId.orEmpty().trim().takeIf { it.isNotEmpty() }?.let {
findLicenses(mavenReader, getParentPomFile(model, configurations, dependencies), configurations, dependencies)
} ?: emptyList()
}
return mutableListOf()
}

private fun String.isUrlValid(): Boolean {
var uri: URI? = null
try {
uri = URL(this).toURI()
return try {
URL(this).toURI()
true
} catch (e: Exception) {
logger.warn("Dependency has an invalid license URL '$this': ${e.shortMessage()}")
logger.debug("Dependency has an invalid license URL '$this'", e)
false
}
return uri != null
}

private fun Model.pomVersion(
mavenReader: MavenXpp3Reader,
pomFile: File?,
configurations: ConfigurationContainer,
dependencies: DependencyHandler,
): String {
return version.orEmpty().trim()
.ifEmpty { findVersion(mavenReader, pomFile, configurations, dependencies) }
}
): String = version.orEmpty().trim().ifEmpty { findVersion(mavenReader, pomFile, configurations, dependencies) }

private fun Model.pomName(): String {
return name.orEmpty().trim().ifEmpty { artifactId.orEmpty().trim() }
}
private fun Model.pomName(): String = name.orEmpty().trim().ifEmpty { artifactId.orEmpty().trim() }

private fun Model.pomDescription(): String {
return description.orEmpty().trim()
}
private fun Model.pomDescription(): String = description.orEmpty().trim()

private fun Model.pomUrl(): String {
return url.orEmpty().trim()
}
private fun Model.pomUrl(): String = url.orEmpty().trim()

private fun Model.pomInceptionYear(): String {
return inceptionYear.orEmpty().trim()
}
private fun Model.pomVersion(): String = version.orEmpty().trim()

private fun Model.pomInceptionYear(): String = inceptionYear.orEmpty().trim()

private fun Model.pomDevelopers(): List<Developer> {
val developers = mutableListOf<Developer>()
this.developers.orEmpty().forEach { developer ->
developers +=
Developer().apply {
id = developer.name.orEmpty().trim()
}
return developers.orEmpty().map { developer ->
Developer().apply {
id = developer.name.orEmpty().trim()
}
}
return developers
}

private fun File?.isNullOrEmpty(): Boolean = this == null || this.length() == 0L
private fun File?.isNullOrEmpty(): Boolean = this?.length() == 0L

private fun Exception.shortMessage(): String =
with(message ?: "<no message>") {
if (length > MAX_EXCEPTION_MESSAGE_LENGTH) {
substring(0, MAX_EXCEPTION_MESSAGE_LENGTH) + "... (see --debug for complete message)"
} else {
this
}
private fun Exception.shortMessage(): String = (message ?: "<no message>").let {
if (it.length > MAX_EXCEPTION_MESSAGE_LENGTH) {
"${it.take(MAX_EXCEPTION_MESSAGE_LENGTH)}... (see --debug for complete message)"
} else {
it
}
}

private companion object {
private const val ANDROID_SUPPORT_GROUP_ID = "com.android.support"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,26 @@ import org.gradle.api.Project
import org.gradle.api.reporting.ReportingExtension

/** Returns true if plugin exists in project. */
internal fun Project.hasPlugin(list: List<String>): Boolean {
return list.find { plugins.hasPlugin(it) } != null
}
internal fun Project.hasPlugin(list: List<String>): Boolean = list.any { plugins.hasPlugin(it) }

/** Configure common configuration for both Java and Android tasks. */
internal fun Project.configureCommon(task: LicenseReportTask) {
task.buildFile = buildFile
val reportingExtension = extensions.getByType(ReportingExtension::class.java)
val licenseExtension = extensions.getByType(LicenseReportExtension::class.java)

// Customizing internal task options
task.outputDir = extensions.getByType(ReportingExtension::class.java).file("licenses")
task.apply {
buildFile = this@configureCommon.buildFile
outputDir = reportingExtension.file("licenses")

// Customizing internal task options from extension
val extension = extensions.getByType(LicenseReportExtension::class.java)
task.generateCsvReport = extension.generateCsvReport
task.generateHtmlReport = extension.generateHtmlReport
task.generateJsonReport = extension.generateJsonReport
task.generateTextReport = extension.generateTextReport
task.copyCsvReportToAssets = extension.copyCsvReportToAssets
task.copyHtmlReportToAssets = extension.copyHtmlReportToAssets
task.copyJsonReportToAssets = extension.copyJsonReportToAssets
task.copyTextReportToAssets = extension.copyTextReportToAssets
task.useVariantSpecificAssetDirs = extension.useVariantSpecificAssetDirs
task.ignoredPatterns = extension.ignoredPatterns
generateCsvReport = licenseExtension.generateCsvReport
generateHtmlReport = licenseExtension.generateHtmlReport
generateJsonReport = licenseExtension.generateJsonReport
generateTextReport = licenseExtension.generateTextReport
copyCsvReportToAssets = licenseExtension.copyCsvReportToAssets
copyHtmlReportToAssets = licenseExtension.copyHtmlReportToAssets
copyJsonReportToAssets = licenseExtension.copyJsonReportToAssets
copyTextReportToAssets = licenseExtension.copyTextReportToAssets
useVariantSpecificAssetDirs = licenseExtension.useVariantSpecificAssetDirs
ignoredPatterns = licenseExtension.ignoredPatterns
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,7 @@ private fun Project.configureVariant(

// Custom for Android tasks
val sourceSetName = if (it.useVariantSpecificAssetDirs) variant.name else "main"
it.assetDirs =
baseExtension
.sourceSets
.getByName(sourceSetName)
.assets
.srcDirs
.toList()
it.assetDirs = baseExtension.sourceSets.findByName(sourceSetName)?.assets?.srcDirs?.toList() ?: emptyList()
it.variantName = variant.name
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,7 @@ internal fun Project.isJavaProject(): Boolean {
)
}

/**
* Configure for Java projects.
*
* All of these plugins will apply the JavaPlugin(relies on JavaBasePlugin).
*/
/** Configure for Java projects. */
internal fun Project.configureJavaProject() {
tasks.register("licenseReport", LicenseReportTask::class.java) {
// Apply common task configuration first
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ final class LicensePluginSpec extends Specification {
@Rule
public final TemporaryFolder testProjectDir = new TemporaryFolder()
private int compileSdkVersion = 34
private String agpVersion = "3.6.4"
private List<File> pluginClasspath
private String classpathString
private File buildFile
Expand Down Expand Up @@ -206,7 +205,6 @@ final class LicensePluginSpec extends Specification {
}
dependencies {
classpath "com.android.tools.build:gradle:$agpVersion"
classpath files($classpathString)
}
}
Expand All @@ -233,7 +231,6 @@ final class LicensePluginSpec extends Specification {
}
dependencies {
classpath "com.android.tools.build:gradle:$agpVersion"
classpath files($classpathString)
}
}
Expand Down

0 comments on commit 83386d2

Please sign in to comment.