Skip to content

Commit ae58a8c

Browse files
committed
fix(DependencyGraphBuilder): Skip deep comparison for Gradle projects
This can be avoided as it leads to a long runtime (and possible an infinite loop / recursion) for large dependency graphs. In order to be able to skip the deep comparison, the selected variants need to be tracked in the Identifier. In the DependencyGraphBuilder all 'scopes' (runtimeClasspath, compileClasspath, ...) are thrown together. Gradle may have selected different variants of the components in different 'scopes'. Signed-off-by: Jendrik Johannes <[email protected]>
1 parent 8af12fb commit ae58a8c

File tree

8 files changed

+35
-4
lines changed

8 files changed

+35
-4
lines changed

model/src/main/kotlin/Identifier.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ data class Identifier(
4949
/**
5050
* The version of the component.
5151
*/
52-
val version: String
52+
val version: String,
53+
54+
/**
55+
* The selected variants of the component.
56+
*/
57+
val variants: Set<String> = emptySet()
5358
) : Comparable<Identifier> {
5459
companion object {
5560
/**

model/src/main/kotlin/utils/DependencyGraphBuilder.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,9 +326,16 @@ class DependencyGraphBuilder<D>(
326326
val dependencies = dependencyHandler.dependenciesFor(dependency)
327327
if (ref.dependencies.size != dependencies.size) return false
328328

329-
val dependencies1 = ref.dependencies.map { dependencyIds[it.pkg] }
329+
val dependencies1 = ref.dependencies.mapTo(mutableSetOf()) { dependencyIds[it.pkg] }
330330
val dependencies2 = dependencies.associateBy { dependencyHandler.identifierFor(it) }
331-
if (!dependencies2.keys.containsAll(dependencies1)) return false
331+
332+
if (dependencies1 == dependencies2.keys) {
333+
if (dependencyHandler.requiresDeepDependencyTreeComparison()) {
334+
return true
335+
}
336+
} else {
337+
return false
338+
}
332339

333340
return ref.dependencies.all { refDep ->
334341
dependencies2[dependencyIds[refDep.pkg]]?.let { dependencyTreeEquals(refDep, it) } == true

model/src/main/kotlin/utils/DependencyHandler.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,11 @@ interface DependencyHandler<D> {
6565
* implementation returns an empty collection.
6666
*/
6767
fun issuesForDependency(dependency: D): List<Issue> = emptyList()
68+
69+
/**
70+
* Does node comparison require a deep comparison of the whole dependency subtree or not? If the underlying
71+
* dependency management system, gives the guarantee for the latter, a costly comparison can be avoided and
72+
* speed up the analysis process.
73+
*/
74+
fun requiresDeepDependencyTreeComparison() = true
6875
}

plugins/package-managers/gradle-inspector/src/main/kotlin/GradleDependencyHandler.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ internal class GradleDependencyHandler(
128128
isMetadataOnly = hasNoArtifacts
129129
)
130130
}
131+
132+
/*
133+
* In case of Gradle, the costly deep comparison can be skipped, because if the direct dependencies are the same,
134+
* their children are also the same.
135+
*/
136+
override fun requiresDeepDependencyTreeComparison() = false
131137
}
132138

133139
// See http://maven.apache.org/pom.html#SCM.

plugins/package-managers/gradle-model/src/main/kotlin/GradleModel.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ interface OrtDependency {
4141
val groupId: String
4242
val artifactId: String
4343
val version: String
44+
val variants: Set<String>
4445
val classifier: String
4546
val extension: String
4647
val dependencies: List<OrtDependency>

plugins/package-managers/gradle-plugin/src/main/kotlin/OrtModelBuilder.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ internal class OrtModelBuilder : ToolingModelBuilder {
223223
groupId = id.group,
224224
artifactId = id.module,
225225
version = id.version,
226+
variants = selectedComponent.variants.map { it.displayName }.toSet(),
226227
classifier = "",
227228
extension = modelBuildingResult?.effectiveModel?.packaging.orEmpty(),
228229
dependencies = dependencies,
@@ -252,6 +253,7 @@ internal class OrtModelBuilder : ToolingModelBuilder {
252253
groupId = moduleId.group,
253254
artifactId = moduleId.name,
254255
version = moduleId.version.takeUnless { it == "unspecified" }.orEmpty(),
256+
variants = selectedComponent.variants.map { it.displayName }.toSet(),
255257
classifier = "",
256258
extension = "",
257259
dependencies = dependencies,

plugins/package-managers/gradle-plugin/src/main/kotlin/OrtModelImpl.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ internal class OrtDependencyImpl(
5050
override val artifactId: String,
5151
override val version: String,
5252
override val classifier: String,
53+
override val variants: Set<String>,
5354
override val extension: String,
5455
override val dependencies: List<OrtDependency>,
5556
override val error: String?,

plugins/package-managers/gradle/src/main/resources/init.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ interface OrtDependency {
7171
String getArtifactId()
7272
String getVersion()
7373
String getClassifier()
74+
Set<String> getVariants()
7475
String getExtension()
7576
List<OrtDependency> getDependencies()
7677
String getError()
@@ -104,6 +105,7 @@ class OrtDependencyImpl implements OrtDependency, Serializable {
104105
String groupId = ''
105106
String artifactId = ''
106107
String version = ''
108+
Set<String> variants = []
107109
String classifier = ''
108110
String extension = ''
109111
List<OrtDependency> dependencies = []
@@ -369,7 +371,7 @@ class AbstractOrtDependencyTreePlugin<T> implements Plugin<T> {
369371
def classifier = artifact?.classifier ?: ''
370372
def extension = artifact?.extension ?: ''
371373

372-
return new OrtDependencyImpl(id.group, id.module, id.version, classifier, extension, dependencies,
374+
return new OrtDependencyImpl(id.group, id.module, id.version, [] as Set, classifier, extension, dependencies,
373375
error, warning, pomFile, null)
374376
} else if (id instanceof ProjectComponentIdentifier) {
375377
if (id.build.isCurrentBuild() || !project.gradle.gradleVersion.isAtLeastVersion(7, 2)) {

0 commit comments

Comments
 (0)