From bdb53bf98ec1f22ad5a1649bceff39d6e74730d6 Mon Sep 17 00:00:00 2001 From: Marek Kubiczek Date: Tue, 22 Dec 2020 09:14:35 +0100 Subject: [PATCH] Add android library subproject dependencies to the report --- .../license/LicenseReportTask.kt | 62 ++++---- .../license/LicensePluginAndroidSpec.groovy | 138 ++++++++++++++++++ 2 files changed, 172 insertions(+), 28 deletions(-) diff --git a/src/main/kotlin/com/jaredsburrows/license/LicenseReportTask.kt b/src/main/kotlin/com/jaredsburrows/license/LicenseReportTask.kt index 0a932255..b7bb7063 100644 --- a/src/main/kotlin/com/jaredsburrows/license/LicenseReportTask.kt +++ b/src/main/kotlin/com/jaredsburrows/license/LicenseReportTask.kt @@ -18,6 +18,8 @@ import java.util.UUID import org.gradle.api.DefaultTask import org.gradle.api.Task import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.ResolvedArtifact +import org.gradle.api.artifacts.ResolvedDependency import org.gradle.api.logging.LogLevel import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal @@ -96,38 +98,16 @@ open class LicenseReportTask : DefaultTask() { // tasks can't be final // If Android project, add extra configurations variantName?.let { variant -> - // Add buildType configurations - configurations.find { it.name == "compile" }?.let { - configurationSet.add(configurations.getByName("${buildType}Compile")) - } - configurations.find { it.name == "api" }?.let { - configurationSet.add(configurations.getByName("${buildType}Api")) - } - configurations.find { it.name == "implementation" }?.let { - configurationSet.add(configurations.getByName("${buildType}Implementation")) - } - - // Add productFlavors configurations - productFlavors.forEach { flavor -> - // Works for productFlavors and productFlavors with dimensions - if (variant.capitalize().contains(flavor.name.capitalize())) { - configurations.find { it.name == "compile" }?.let { - configurationSet.add(configurations.getByName("${flavor.name}Compile")) - } - configurations.find { it.name == "api" }?.let { - configurationSet.add(configurations.getByName("${flavor.name}Api")) - } - configurations.find { it.name == "implementation" }?.let { - configurationSet.add(configurations.getByName("${flavor.name}Implementation")) - } - } + configurations.find { it.name == "${variant}RuntimeClasspath" }?.also { + configurationSet.add(it) } } // Iterate through all the configurations's dependencies - configurationSet.forEach { set -> - if (set.isCanBeResolved) { - set.resolvedConfiguration.lenientConfiguration.artifacts.forEach { artifact -> + configurationSet.forEach { configuration -> + if (configuration.isCanBeResolved) { + val allDeps = configuration.resolvedConfiguration.lenientConfiguration.allModuleDependencies + getResolvedArtifactsFromResolvedDependencies(allDeps).forEach { artifact -> val id = artifact.moduleVersion.id val gav = "${id.group}:${id.name}:${id.version}@pom" configurations.getByName(pomConfiguration).dependencies.add( @@ -138,6 +118,32 @@ open class LicenseReportTask : DefaultTask() { // tasks can't be final } } + private fun getResolvedArtifactsFromResolvedDependencies( + resolvedDependencies: Set + ): Set { + val resolvedArtifacts = hashSetOf() + for (resolvedDependency in resolvedDependencies) { + try { + if (resolvedDependency.moduleVersion == "unspecified") { + /** + * Attempting to getAllModuleArtifacts on a local library project will result + * in AmbiguousVariantSelectionException as there are not enough criteria + * to match a specific variant of the library project. Instead we skip the + * the library project itself and enumerate its dependencies. + */ + resolvedArtifacts.addAll( + getResolvedArtifactsFromResolvedDependencies(resolvedDependency.children) + ) + } else { + resolvedArtifacts.addAll(resolvedDependency.allModuleArtifacts) + } + } catch (e: Exception) { + logger.warn("Failed to process $resolvedDependency.name", e) + } + } + return resolvedArtifacts + } + /** Get POM information from the dependency artifacts. */ private fun generatePOMInfo() { // Iterate through all POMs in order from our custom POM configuration diff --git a/src/test/groovy/com/jaredsburrows/license/LicensePluginAndroidSpec.groovy b/src/test/groovy/com/jaredsburrows/license/LicensePluginAndroidSpec.groovy index 9f4b0113..8ea84f07 100644 --- a/src/test/groovy/com/jaredsburrows/license/LicensePluginAndroidSpec.groovy +++ b/src/test/groovy/com/jaredsburrows/license/LicensePluginAndroidSpec.groovy @@ -960,6 +960,144 @@ final class LicensePluginAndroidSpec extends Specification { taskName << ['licenseDebugReport', 'licenseReleaseReport'] } + @Unroll def '#taskName with default buildTypes, multi module and android and android'() { + given: + testProjectDir.newFile('settings.gradle') << + """ + include 'subproject' + """ + + buildFile << + """ + buildscript { + dependencies { + classpath files($classpathString) + } + } + + allprojects { + repositories { + maven { + url '${mavenRepoUrl}' + } + } + } + + apply plugin: 'com.android.application' + apply plugin: 'com.jaredsburrows.license' + + android { + compileSdkVersion 28 + + defaultConfig { + applicationId 'com.example' + } + } + + dependencies { + api project(':subproject') + implementation 'group:name:1.0.0' + } + + project(':subproject') { + apply plugin: 'com.android.library' + + android { + compileSdkVersion 28 + } + + dependencies { + implementation 'com.android.support:design:26.1.0' + } + } + """ + + when: + def result = gradleWithCommand(testProjectDir.root, "${taskName}", '-s') + def actualHtml = new File("${reportFolder}/${taskName}.html").text + def expectedHtml = + """ + + + + Open source licenses + + +

Notice for packages:

+ + + + """ + def actualJson = new File("${reportFolder}/${taskName}.json").text + def expectedJson = + """ + [ + { + "project":"design", + "description":null, + "version":"26.1.0", + "developers":[], + "url":null, + "year":null, + "licenses":[ + { + "license":"The Apache Software License", + "license_url":"http://www.apache.org/licenses/LICENSE-2.0.txt" + } + ], + "dependency":"com.android.support:design:26.1.0" + }, + { + "project":"Fake dependency name", + "description":"Fake dependency description", + "version":"1.0.0", + "developers":[ + "name" + ], + "url":"https://github.com/user/repo", + "year":"2017", + "licenses":[ + { + "license":"Some license", + "license_url":"http://website.tld/" + } + ], + "dependency":"group:name:1.0.0" + } + ] + """ + + then: + result.task(":${taskName}").outcome == SUCCESS + result.output.find("Wrote HTML report to .*${reportFolder}/${taskName}.html.") + result.output.find("Wrote JSON report to .*${reportFolder}/${taskName}.json.") + assertHtml(expectedHtml, actualHtml) + assertJson(expectedJson, actualJson) + + where: + taskName << ['licenseDebugReport', 'licenseReleaseReport'] + } + @Unroll def '#taskName with reports enabled and copy enabled #copyEnabled'() { given: buildFile <<