Skip to content

Commit

Permalink
Take the forced version of dependency artifact.
Browse files Browse the repository at this point in the history
Improve the de-duplication of our third-party dependencies by avoiding the _originally declared_ dependency versions in such "forceful" cases.
  • Loading branch information
armiol committed May 21, 2024
1 parent e751ac3 commit 340d54b
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023, TeamDev. All rights reserved.
* Copyright 2024, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -31,6 +31,7 @@ import java.io.Writer
import java.util.*
import kotlin.reflect.full.isSubclassOf
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.internal.artifacts.dependencies.AbstractExternalModuleDependency
import org.gradle.kotlin.dsl.withGroovyBuilder
Expand Down Expand Up @@ -126,13 +127,34 @@ private fun Project.depsFromAllConfigurations(): Set<ModuleDependency> {
}
configuration.dependencies.filter { it.isExternal() }
.forEach { dependency ->
val moduleDependency = ModuleDependency(project, configuration, dependency)
val forcedVersion = configuration.forcedVersionOf(dependency)
val moduleDependency =
if (forcedVersion != null) {
ModuleDependency(project, configuration, dependency, forcedVersion)
} else {
ModuleDependency(project, configuration, dependency)
}
result.add(moduleDependency)
}
}
return result
}

/**
* Searches for a forced version of given [dependency] in this [Configuration].
*
* Returns `null`, if it wasn't forced.
*/
private fun Configuration.forcedVersionOf(dependency: Dependency): String? {
val forcedModules = resolutionStrategy.forcedModules
val maybeForced = forcedModules.firstOrNull {
it.group == dependency.group
&& it.name == dependency.name
&& it.version != null
}
return maybeForced?.version
}

/**
* Tells whether the dependency is an external module dependency.
*/
Expand Down Expand Up @@ -160,7 +182,7 @@ private fun Project.deduplicate(dependencies: Set<ModuleDependency>): List<Modul
logDuplicates(groups)

val filtered = groups.map { group ->
group.value.maxByOrNull { dep -> dep.version!! }!!
group.value.maxByOrNull { dep -> dep.version }!!
}
return filtered
}
Expand All @@ -177,9 +199,9 @@ private fun Project.logDuplicate(dependency: String, versions: List<ModuleDepend
versions.forEach {
logger.lifecycle(
"module: {}, configuration: {}, version: {}",
it.module.name,
it.project.name,
it.configuration.name,
it.version
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023, TeamDev. All rights reserved.
* Copyright 2024, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -37,36 +37,39 @@ import org.gradle.api.artifacts.Dependency
* the dependency comes.
*/
internal class ModuleDependency(
val module: Project,
val project: Project,
val configuration: Configuration,
private val dependency: Dependency,
private val factualVersion: String = dependency.version!!

) : Dependency by dependency, Comparable<ModuleDependency> {

companion object {
private val COMPARATOR = compareBy<ModuleDependency> { it.module }
private val COMPARATOR = compareBy<ModuleDependency> { it.project }
.thenBy { it.configuration.name }
.thenBy { it.group }
.thenBy { it.name }
.thenBy { it.version }
.thenBy { it.factualVersion }
}

override fun getVersion(): String = factualVersion

/**
* A project dependency with its [scope][DependencyScope].
*
* Doesn't contain any info about an origin module and configuration.
*/
val scoped = ScopedDependency.of(dependency, configuration)
val scoped = ScopedDependency.of(this, configuration)

/**
* GAV coordinates of this dependency.
*
* Gradle's [Dependency] is a mutable object. Its properties can change their
* values with time. In parcticular, the version can be changed as more
* values with time. In particular, the version can be changed as more
* configurations are getting resolved. This is why this property is calculated.
*/
val gav: String
get() = "$group:$name:$version"
get() = "$group:$name:$factualVersion"

override fun compareTo(other: ModuleDependency): Int = COMPARATOR.compare(this, other)

Expand All @@ -76,17 +79,17 @@ internal class ModuleDependency(

other as ModuleDependency

if (module != other.module) return false
if (project != other.project) return false
if (configuration != other.configuration) return false
if (dependency != other.dependency) return false
if (gav != other.gav) return false

return true
}

override fun hashCode(): Int {
var result = module.hashCode()
var result = project.hashCode()
result = 31 * result + configuration.hashCode()
result = 31 * result + dependency.hashCode()
result = 31 * result + gav.hashCode()
return result
}
}
}

0 comments on commit 340d54b

Please sign in to comment.