[Scripting] Add partial transitive resolution to Maven resolver
This commit is contained in:
+10
-4
@@ -17,6 +17,7 @@ import kotlin.script.experimental.dependencies.ExternalDependenciesResolver
|
|||||||
import kotlin.script.experimental.dependencies.RepositoryCoordinates
|
import kotlin.script.experimental.dependencies.RepositoryCoordinates
|
||||||
import kotlin.script.experimental.dependencies.impl.*
|
import kotlin.script.experimental.dependencies.impl.*
|
||||||
import kotlin.script.experimental.dependencies.maven.impl.AetherResolveSession
|
import kotlin.script.experimental.dependencies.maven.impl.AetherResolveSession
|
||||||
|
import kotlin.script.experimental.dependencies.maven.impl.ResolutionKind
|
||||||
import kotlin.script.experimental.dependencies.maven.impl.mavenCentral
|
import kotlin.script.experimental.dependencies.maven.impl.mavenCentral
|
||||||
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
@@ -58,15 +59,20 @@ class MavenDependenciesResolver : ExternalDependenciesResolver {
|
|||||||
|
|
||||||
return try {
|
return try {
|
||||||
val dependencyScopes = options.dependencyScopes ?: listOf(JavaScopes.COMPILE, JavaScopes.RUNTIME)
|
val dependencyScopes = options.dependencyScopes ?: listOf(JavaScopes.COMPILE, JavaScopes.RUNTIME)
|
||||||
val transitive = options.transitive ?: true
|
val kind = when (options.partialResolution) {
|
||||||
|
true -> ResolutionKind.TRANSITIVE_PARTIAL
|
||||||
|
false, null -> when(options.transitive) {
|
||||||
|
true, null -> ResolutionKind.TRANSITIVE
|
||||||
|
false -> ResolutionKind.NON_TRANSITIVE
|
||||||
|
}
|
||||||
|
}
|
||||||
val classifier = options.classifier
|
val classifier = options.classifier
|
||||||
val extension = options.extension
|
val extension = options.extension
|
||||||
val deps = AetherResolveSession(
|
AetherResolveSession(
|
||||||
null, remoteRepositories()
|
null, remoteRepositories()
|
||||||
).resolve(
|
).resolve(
|
||||||
artifactId, dependencyScopes.joinToString(","), transitive, null, classifier, extension
|
artifactId, dependencyScopes.joinToString(","), kind, null, classifier, extension
|
||||||
)
|
)
|
||||||
ResultWithDiagnostics.Success(deps.map { it.file })
|
|
||||||
} catch (e: RepositoryException) {
|
} catch (e: RepositoryException) {
|
||||||
makeResolveFailureResult(e, sourceCodeLocation)
|
makeResolveFailureResult(e, sourceCodeLocation)
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-22
@@ -39,10 +39,23 @@ import org.eclipse.aether.util.repository.AuthenticationBuilder
|
|||||||
import org.eclipse.aether.util.repository.DefaultMirrorSelector
|
import org.eclipse.aether.util.repository.DefaultMirrorSelector
|
||||||
import org.eclipse.aether.util.repository.DefaultProxySelector
|
import org.eclipse.aether.util.repository.DefaultProxySelector
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||||
|
import kotlin.script.experimental.api.ScriptDiagnostic
|
||||||
|
import kotlin.script.experimental.api.asSuccess
|
||||||
|
|
||||||
val mavenCentral: RemoteRepository = RemoteRepository.Builder("maven central", "default", "https://repo.maven.apache.org/maven2/").build()
|
val mavenCentral: RemoteRepository = RemoteRepository.Builder("maven central", "default", "https://repo.maven.apache.org/maven2/").build()
|
||||||
|
|
||||||
|
internal enum class ResolutionKind {
|
||||||
|
NON_TRANSITIVE,
|
||||||
|
TRANSITIVE,
|
||||||
|
|
||||||
|
// Partial resolution is successful in case if dependency tree was built,
|
||||||
|
// but may return non-complete list of dependencies - i.e. while requesting sources, some libraries may lack sources artifacts.
|
||||||
|
// Resolution errors will be attached as reports.
|
||||||
|
// Also, might be slightly slower than usual transitive resolution.
|
||||||
|
TRANSITIVE_PARTIAL
|
||||||
|
}
|
||||||
|
|
||||||
internal class AetherResolveSession(
|
internal class AetherResolveSession(
|
||||||
localRepoDirectory: File? = null,
|
localRepoDirectory: File? = null,
|
||||||
remoteRepos: List<RemoteRepository> = listOf(mavenCentral)
|
remoteRepos: List<RemoteRepository> = listOf(mavenCentral)
|
||||||
@@ -133,22 +146,63 @@ internal class AetherResolveSession(
|
|||||||
fun resolve(
|
fun resolve(
|
||||||
root: Artifact,
|
root: Artifact,
|
||||||
scope: String,
|
scope: String,
|
||||||
transitive: Boolean,
|
kind: ResolutionKind,
|
||||||
filter: DependencyFilter?,
|
filter: DependencyFilter?,
|
||||||
classifier: String? = null,
|
classifier: String? = null,
|
||||||
extension: String? = null,
|
extension: String? = null,
|
||||||
): List<Artifact> {
|
): ResultWithDiagnostics<List<File>> {
|
||||||
return if (transitive) resolveDependencies(root, scope, filter, classifier, extension)
|
if (kind == ResolutionKind.NON_TRANSITIVE) return resolveArtifact(root).asSuccess()
|
||||||
else resolveArtifact(root)
|
|
||||||
|
val requests = resolveTree(root, scope, filter, classifier, extension)
|
||||||
|
|
||||||
|
@Suppress("KotlinConstantConditions")
|
||||||
|
return when (kind) {
|
||||||
|
ResolutionKind.TRANSITIVE -> resolveDependencies(requests) {
|
||||||
|
repositorySystem.resolveArtifacts(
|
||||||
|
repositorySystemSession,
|
||||||
|
requests
|
||||||
|
).toFiles().asSuccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolutionKind.TRANSITIVE_PARTIAL -> resolveDependencies(requests) {
|
||||||
|
val reports = mutableListOf<ScriptDiagnostic>()
|
||||||
|
val results = mutableListOf<File>()
|
||||||
|
for (req in requests) {
|
||||||
|
try {
|
||||||
|
results.add(
|
||||||
|
repositorySystem.resolveArtifact(
|
||||||
|
repositorySystemSession,
|
||||||
|
req
|
||||||
|
).artifact.file
|
||||||
|
)
|
||||||
|
} catch (e: ArtifactResolutionException) {
|
||||||
|
reports.add(
|
||||||
|
ScriptDiagnostic(
|
||||||
|
ScriptDiagnostic.unspecifiedError,
|
||||||
|
e.message.orEmpty(),
|
||||||
|
ScriptDiagnostic.Severity.WARNING,
|
||||||
|
exception = e
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultWithDiagnostics.Success(results, reports)
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolutionKind.NON_TRANSITIVE -> {
|
||||||
|
error("This statement is not reachable")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveDependencies(
|
private fun resolveTree(
|
||||||
root: Artifact,
|
root: Artifact,
|
||||||
scope: String,
|
scope: String,
|
||||||
filter: DependencyFilter?,
|
filter: DependencyFilter?,
|
||||||
classifier: String?,
|
classifier: String?,
|
||||||
extension: String?,
|
extension: String?,
|
||||||
): List<Artifact> {
|
): Collection<ArtifactRequest> {
|
||||||
return fetch(
|
return fetch(
|
||||||
request(Dependency(root, scope)),
|
request(Dependency(root, scope)),
|
||||||
{ req ->
|
{ req ->
|
||||||
@@ -163,8 +217,7 @@ internal class AetherResolveSession(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val requests = requestsBuilder.requests
|
requestsBuilder.requests
|
||||||
repositorySystem.resolveArtifacts(repositorySystemSession, requests)
|
|
||||||
},
|
},
|
||||||
{ req, ex ->
|
{ req, ex ->
|
||||||
DependencyCollectionException(
|
DependencyCollectionException(
|
||||||
@@ -176,7 +229,25 @@ internal class AetherResolveSession(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveArtifact(artifact: Artifact): List<Artifact> {
|
private fun Collection<ArtifactResult>.toFiles() = map { it.artifact.file }
|
||||||
|
|
||||||
|
private fun resolveDependencies(
|
||||||
|
requests: Collection<ArtifactRequest>,
|
||||||
|
resolveAction: (Collection<ArtifactRequest>) -> ResultWithDiagnostics<List<File>>
|
||||||
|
): ResultWithDiagnostics<List<File>> {
|
||||||
|
return fetch(
|
||||||
|
requests,
|
||||||
|
resolveAction
|
||||||
|
) { _, ex ->
|
||||||
|
DependencyCollectionException(
|
||||||
|
null,
|
||||||
|
ex.message,
|
||||||
|
ex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveArtifact(artifact: Artifact): List<File> {
|
||||||
val request = ArtifactRequest()
|
val request = ArtifactRequest()
|
||||||
request.artifact = artifact
|
request.artifact = artifact
|
||||||
for (repo in remotes) {
|
for (repo in remotes) {
|
||||||
@@ -187,7 +258,7 @@ internal class AetherResolveSession(
|
|||||||
request,
|
request,
|
||||||
{ req -> listOf(repositorySystem.resolveArtifact(repositorySystemSession, req)) },
|
{ req -> listOf(repositorySystem.resolveArtifact(repositorySystemSession, req)) },
|
||||||
{ req, ex -> ArtifactResolutionException(listOf(ArtifactResult(req)), ex.message, IllegalArgumentException(ex)) }
|
{ req, ex -> ArtifactResolutionException(listOf(ArtifactResult(req)), ex.message, IllegalArgumentException(ex)) }
|
||||||
)
|
).toFiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun request(root: Dependency): CollectRequest {
|
private fun request(root: Dependency): CollectRequest {
|
||||||
@@ -199,25 +270,19 @@ internal class AetherResolveSession(
|
|||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <RequestT> fetch(
|
private fun <RequestT, ResultT> fetch(
|
||||||
request: RequestT,
|
request: RequestT,
|
||||||
fetchBody: (RequestT) -> Collection<ArtifactResult>,
|
fetchBody: (RequestT) -> ResultT,
|
||||||
wrapException: (RequestT, Exception) -> Exception
|
wrapException: (RequestT, Exception) -> Exception
|
||||||
): List<Artifact> {
|
): ResultT {
|
||||||
val deps: MutableList<Artifact> = LinkedList()
|
return try {
|
||||||
try {
|
|
||||||
var results: Collection<ArtifactResult>
|
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
results = fetchBody(request)
|
fetchBody(request)
|
||||||
}
|
|
||||||
for (res in results) {
|
|
||||||
deps.add(res.artifact)
|
|
||||||
}
|
}
|
||||||
// @checkstyle IllegalCatch (1 line)
|
// @checkstyle IllegalCatch (1 line)
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
throw wrapException(request, ex)
|
throw wrapException(request, ex)
|
||||||
}
|
}
|
||||||
return deps
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getMirrorSelector(): DefaultMirrorSelector {
|
private fun getMirrorSelector(): DefaultMirrorSelector {
|
||||||
|
|||||||
+18
@@ -183,6 +183,24 @@ class MavenResolverTest : ResolversTestBase() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore("ignored because spark is a very heavy dependency")
|
||||||
|
fun ignore_testPartialResolution() {
|
||||||
|
val resolver = MavenDependenciesResolver()
|
||||||
|
val options = buildOptions(
|
||||||
|
DependenciesResolverOptionsName.PARTIAL_RESOLUTION to "true",
|
||||||
|
DependenciesResolverOptionsName.CLASSIFIER to "sources",
|
||||||
|
DependenciesResolverOptionsName.EXTENSION to "jar",
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = runBlocking {
|
||||||
|
resolver.resolve("org.jetbrains.kotlinx.spark:kotlin-spark-api_3.3.0_2.13:1.2.1", options)
|
||||||
|
}
|
||||||
|
|
||||||
|
result as ResultWithDiagnostics.Success
|
||||||
|
assertTrue(result.reports.isNotEmpty())
|
||||||
|
assertTrue(result.value.isNotEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
// Ignored - tests with custom repos often break the CI due to the caching issues
|
// Ignored - tests with custom repos often break the CI due to the caching issues
|
||||||
// TODO: find a way to enable it back
|
// TODO: find a way to enable it back
|
||||||
@Ignore
|
@Ignore
|
||||||
|
|||||||
+8
@@ -23,6 +23,7 @@ operator fun MutableMap<String, String>.set(key: DependenciesResolverOptionsName
|
|||||||
*/
|
*/
|
||||||
enum class DependenciesResolverOptionsName(optionName: String? = null) {
|
enum class DependenciesResolverOptionsName(optionName: String? = null) {
|
||||||
TRANSITIVE,
|
TRANSITIVE,
|
||||||
|
PARTIAL_RESOLUTION,
|
||||||
SCOPE,
|
SCOPE,
|
||||||
USERNAME,
|
USERNAME,
|
||||||
PASSWORD,
|
PASSWORD,
|
||||||
@@ -37,6 +38,13 @@ enum class DependenciesResolverOptionsName(optionName: String? = null) {
|
|||||||
val ExternalDependenciesResolver.Options.transitive
|
val ExternalDependenciesResolver.Options.transitive
|
||||||
get() = flag(DependenciesResolverOptionsName.TRANSITIVE)
|
get() = flag(DependenciesResolverOptionsName.TRANSITIVE)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables partial resolution of transitive dependencies.
|
||||||
|
* When this flag is enabled, resolver ignores [transitive] flag.
|
||||||
|
*/
|
||||||
|
val ExternalDependenciesResolver.Options.partialResolution
|
||||||
|
get() = flag(DependenciesResolverOptionsName.PARTIAL_RESOLUTION)
|
||||||
|
|
||||||
val ExternalDependenciesResolver.Options.dependencyScopes
|
val ExternalDependenciesResolver.Options.dependencyScopes
|
||||||
get() = value(DependenciesResolverOptionsName.SCOPE)?.split(",")
|
get() = value(DependenciesResolverOptionsName.SCOPE)?.split(",")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user