Scripting, KT-61727: Make it possible to resolve multiple artifacts at once

For MavenDependenciesResolver it improves performance drastically
depending on how strong transitive dependencies of the roots interfere

^KT-61727 Fixed
This commit is contained in:
Ilya Muradyan
2023-09-01 01:30:33 +02:00
committed by Space Team
parent 0d628c9e59
commit 841cc8eb43
7 changed files with 153 additions and 45 deletions
@@ -6,10 +6,7 @@
package kotlin.script.experimental.dependencies
import java.io.File
import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.script.experimental.api.ScriptDiagnostic
import kotlin.script.experimental.api.SourceCode
import kotlin.script.experimental.api.asSuccess
import kotlin.script.experimental.api.*
import kotlin.script.experimental.dependencies.impl.makeResolveFailureResult
class CompoundDependenciesResolver(private val resolvers: List<ExternalDependenciesResolver>) : ExternalDependenciesResolver {
@@ -57,25 +54,57 @@ class CompoundDependenciesResolver(private val resolvers: List<ExternalDependenc
}
override suspend fun resolve(
artifactCoordinates: String,
options: ExternalDependenciesResolver.Options,
sourceCodeLocation: SourceCode.LocationWithId?
artifactsWithLocations: List<ArtifactWithLocation>,
options: ExternalDependenciesResolver.Options
): ResultWithDiagnostics<List<File>> {
val reports = mutableListOf<ScriptDiagnostic>()
val resultsCollector = IterableResultsCollector<File>()
for (resolver in resolvers) {
if (resolver.acceptsArtifact(artifactCoordinates)) {
when (val resolveResult = resolver.resolve(artifactCoordinates, options, sourceCodeLocation)) {
is ResultWithDiagnostics.Failure -> reports.addAll(resolveResult.reports)
else -> return resolveResult
val artifactToResolverIndex = mutableMapOf<ArtifactWithLocation, Int>().apply {
for (artifactWithLocation in artifactsWithLocations) {
put(artifactWithLocation, -1)
}
}
while (artifactToResolverIndex.isNotEmpty()) {
val resolverGroups = mutableMapOf<Int, MutableList<ArtifactWithLocation>>()
for ((artifactWithLocation, resolverIndex) in artifactToResolverIndex) {
val (artifact, sourceCodeLocation) = artifactWithLocation
var currentIndex = resolverIndex + 1
while (currentIndex < resolvers.size) {
if (resolvers[currentIndex].acceptsArtifact(artifact)) break
++currentIndex
}
if (currentIndex == resolvers.size) {
if (resolverIndex == -1) {
// We add this diagnostic only if there were no resolution attempts made
resultsCollector.addDiagnostic(
"No suitable dependency resolver found for artifact '$artifact'"
.asErrorDiagnostics(locationWithId = sourceCodeLocation)
)
}
} else {
resolverGroups
.getOrPut(currentIndex) { mutableListOf() }
.add(artifactWithLocation)
}
}
artifactToResolverIndex.clear()
for ((resolverIndex, artifacts) in resolverGroups) {
val resolver = resolvers[resolverIndex]
val resolveResult = resolver.resolve(artifacts, options)
resultsCollector.add(resolveResult)
if (resolveResult.reports.isNotEmpty()) {
for (artifact in artifacts) {
artifactToResolverIndex[artifact] = resolverIndex
}
}
}
}
return if (reports.count() == 0) {
makeResolveFailureResult("No suitable dependency resolver found for artifact '$artifactCoordinates'", sourceCodeLocation)
} else {
ResultWithDiagnostics.Failure(reports)
}
return resultsCollector.getResult()
}
}
}
@@ -11,6 +11,8 @@ import kotlin.script.experimental.dependencies.ExternalDependenciesResolver.Opti
open class RepositoryCoordinates(val string: String)
data class ArtifactWithLocation(val artifact: String, val sourceCodeLocation: SourceCode.LocationWithId?)
interface ExternalDependenciesResolver {
interface Options {
object Empty : Options {
@@ -25,11 +27,18 @@ interface ExternalDependenciesResolver {
fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates): Boolean
fun acceptsArtifact(artifactCoordinates: String): Boolean
// Override one of the following methods
suspend fun resolve(
artifactCoordinates: String,
options: Options = Options.Empty,
sourceCodeLocation: SourceCode.LocationWithId? = null
): ResultWithDiagnostics<List<File>>
): ResultWithDiagnostics<List<File>> = resolve(listOf(ArtifactWithLocation(artifactCoordinates, sourceCodeLocation)), options)
suspend fun resolve(
artifactsWithLocations: List<ArtifactWithLocation>,
options: Options = Options.Empty,
): ResultWithDiagnostics<List<File>> =
artifactsWithLocations.map { (artifact, location) -> resolve(artifact, options, location) }.asSuccessIfAny()
fun addRepository(
repositoryCoordinates: RepositoryCoordinates,
@@ -46,4 +55,3 @@ fun ExternalDependenciesResolver.addRepository(
options: Options = Options.Empty,
sourceCodeLocation: SourceCode.LocationWithId? = null
) = addRepository(RepositoryCoordinates(repositoryCoordinates), options, sourceCodeLocation)
@@ -39,7 +39,7 @@ abstract class ResolversTestBase : TestCase() {
fun ExternalDependenciesResolver.assertNotResolve(expectedReportsCount: Int, path: String) {
val result = runBlocking { resolve(path) }
assertIsFailure(result)
assertEquals(expectedReportsCount, result.reports.count())
assertEquals("Actual reports:\n${result.reports.joinToString("\n")}", expectedReportsCount, result.reports.count())
}
fun ExternalDependenciesResolver.assertAcceptsArtifact(path: String) = assertTrue(acceptsArtifact(path))