Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

optimizations #430

Merged
merged 1 commit into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +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 +185,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 +235,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 +255,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 +272,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 +385,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,15 +408,15 @@ 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()) {
Expand All @@ -433,91 +429,63 @@ internal open class LicenseReportTask : DefaultTask() { // tasks can't be final
}

// 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)"
(message ?: "<no message>").let {
if (it.length > MAX_EXCEPTION_MESSAGE_LENGTH) {
"${it.take(MAX_EXCEPTION_MESSAGE_LENGTH)}... (see --debug for complete message)"
} else {
this
it
}
}

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
Loading