diff --git a/model/src/main/kotlin/Identifier.kt b/model/src/main/kotlin/Identifier.kt index d43afd6993777..debc933a4387d 100644 --- a/model/src/main/kotlin/Identifier.kt +++ b/model/src/main/kotlin/Identifier.kt @@ -49,7 +49,12 @@ data class Identifier( /** * The version of the component. */ - val version: String + val version: String, + + /** + * The selected variants of the component. + */ + val variants: Set = emptySet() ) : Comparable { companion object { /** diff --git a/model/src/main/kotlin/utils/DependencyGraphBuilder.kt b/model/src/main/kotlin/utils/DependencyGraphBuilder.kt index 036065e2d8726..6b6525e1d8a95 100644 --- a/model/src/main/kotlin/utils/DependencyGraphBuilder.kt +++ b/model/src/main/kotlin/utils/DependencyGraphBuilder.kt @@ -326,9 +326,16 @@ class DependencyGraphBuilder( val dependencies = dependencyHandler.dependenciesFor(dependency) if (ref.dependencies.size != dependencies.size) return false - val dependencies1 = ref.dependencies.map { dependencyIds[it.pkg] } + val dependencies1 = ref.dependencies.mapTo(mutableSetOf()) { dependencyIds[it.pkg] } val dependencies2 = dependencies.associateBy { dependencyHandler.identifierFor(it) } - if (!dependencies2.keys.containsAll(dependencies1)) return false + + if (dependencies1 == dependencies2.keys) { + if (!dependencyHandler.requiresDeepDependencyTreeComparison()) { + return true + } + } else { + return false + } return ref.dependencies.all { refDep -> dependencies2[dependencyIds[refDep.pkg]]?.let { dependencyTreeEquals(refDep, it) } == true diff --git a/model/src/main/kotlin/utils/DependencyHandler.kt b/model/src/main/kotlin/utils/DependencyHandler.kt index cf1630f8800ef..b88cfd09165b0 100644 --- a/model/src/main/kotlin/utils/DependencyHandler.kt +++ b/model/src/main/kotlin/utils/DependencyHandler.kt @@ -65,4 +65,11 @@ interface DependencyHandler { * implementation returns an empty collection. */ fun issuesForDependency(dependency: D): List = emptyList() + + /** + * Does node comparison require a deep comparison of the whole dependency subtree or not? If the underlying + * dependency management system, gives the guarantee for the latter, a costly comparison can be avoided and + * speed up the analysis process. + */ + fun requiresDeepDependencyTreeComparison() = true } diff --git a/plugins/package-managers/gradle-inspector/src/main/kotlin/GradleDependencyHandler.kt b/plugins/package-managers/gradle-inspector/src/main/kotlin/GradleDependencyHandler.kt index 10e7d8fc0c4ed..79ea1e2f693f9 100644 --- a/plugins/package-managers/gradle-inspector/src/main/kotlin/GradleDependencyHandler.kt +++ b/plugins/package-managers/gradle-inspector/src/main/kotlin/GradleDependencyHandler.kt @@ -128,6 +128,12 @@ internal class GradleDependencyHandler( isMetadataOnly = hasNoArtifacts ) } + + /* + * In case of Gradle, the costly deep comparison can be skipped, because if the direct dependencies are the same, + * their children are also the same. + */ + override fun requiresDeepDependencyTreeComparison() = false } // See http://maven.apache.org/pom.html#SCM. diff --git a/plugins/package-managers/gradle-model/src/main/kotlin/GradleModel.kt b/plugins/package-managers/gradle-model/src/main/kotlin/GradleModel.kt index 0cd71b033af87..340c0d464e8c1 100644 --- a/plugins/package-managers/gradle-model/src/main/kotlin/GradleModel.kt +++ b/plugins/package-managers/gradle-model/src/main/kotlin/GradleModel.kt @@ -41,6 +41,7 @@ interface OrtDependency { val groupId: String val artifactId: String val version: String + val variants: Set val classifier: String val extension: String val dependencies: List diff --git a/plugins/package-managers/gradle-plugin/src/main/kotlin/OrtModelBuilder.kt b/plugins/package-managers/gradle-plugin/src/main/kotlin/OrtModelBuilder.kt index ad57afb8448b0..1e357ce0a038c 100644 --- a/plugins/package-managers/gradle-plugin/src/main/kotlin/OrtModelBuilder.kt +++ b/plugins/package-managers/gradle-plugin/src/main/kotlin/OrtModelBuilder.kt @@ -223,6 +223,7 @@ internal class OrtModelBuilder : ToolingModelBuilder { groupId = id.group, artifactId = id.module, version = id.version, + variants = selectedComponent.variants.map { it.displayName }.toSet(), classifier = "", extension = modelBuildingResult?.effectiveModel?.packaging.orEmpty(), dependencies = dependencies, @@ -252,6 +253,7 @@ internal class OrtModelBuilder : ToolingModelBuilder { groupId = moduleId.group, artifactId = moduleId.name, version = moduleId.version.takeUnless { it == "unspecified" }.orEmpty(), + variants = selectedComponent.variants.map { it.displayName }.toSet(), classifier = "", extension = "", dependencies = dependencies, diff --git a/plugins/package-managers/gradle-plugin/src/main/kotlin/OrtModelImpl.kt b/plugins/package-managers/gradle-plugin/src/main/kotlin/OrtModelImpl.kt index e9f699ad95549..5cfff15519d5b 100644 --- a/plugins/package-managers/gradle-plugin/src/main/kotlin/OrtModelImpl.kt +++ b/plugins/package-managers/gradle-plugin/src/main/kotlin/OrtModelImpl.kt @@ -50,6 +50,7 @@ internal class OrtDependencyImpl( override val artifactId: String, override val version: String, override val classifier: String, + override val variants: Set, override val extension: String, override val dependencies: List, override val error: String?, diff --git a/plugins/package-managers/gradle/src/main/resources/init.gradle b/plugins/package-managers/gradle/src/main/resources/init.gradle index 80bd78e41117f..7b8be3439e936 100644 --- a/plugins/package-managers/gradle/src/main/resources/init.gradle +++ b/plugins/package-managers/gradle/src/main/resources/init.gradle @@ -71,6 +71,7 @@ interface OrtDependency { String getArtifactId() String getVersion() String getClassifier() + Set getVariants() String getExtension() List getDependencies() String getError() @@ -104,6 +105,7 @@ class OrtDependencyImpl implements OrtDependency, Serializable { String groupId = '' String artifactId = '' String version = '' + Set variants = [] String classifier = '' String extension = '' List dependencies = [] @@ -369,7 +371,7 @@ class AbstractOrtDependencyTreePlugin implements Plugin { def classifier = artifact?.classifier ?: '' def extension = artifact?.extension ?: '' - return new OrtDependencyImpl(id.group, id.module, id.version, classifier, extension, dependencies, + return new OrtDependencyImpl(id.group, id.module, id.version, [] as Set, classifier, extension, dependencies, error, warning, pomFile, null) } else if (id instanceof ProjectComponentIdentifier) { if (id.build.isCurrentBuild() || !project.gradle.gradleVersion.isAtLeastVersion(7, 2)) {