[Scripting] Allow transitive resolution of non-JAR artifacts

It allows to resolve libraries sources which is crucial for
Kotlin Notebook plugin.
This commit is contained in:
Ilya Muradyan
2022-05-13 02:35:18 +03:00
committed by teamcity
parent a58e2459c6
commit 46563e93ea
5 changed files with 132 additions and 27 deletions
@@ -5,14 +5,13 @@
package kotlin.script.experimental.dependencies.maven package kotlin.script.experimental.dependencies.maven
import org.eclipse.aether.RepositoryException
import org.eclipse.aether.artifact.DefaultArtifact import org.eclipse.aether.artifact.DefaultArtifact
import org.eclipse.aether.repository.RemoteRepository import org.eclipse.aether.repository.RemoteRepository
import org.eclipse.aether.resolution.ArtifactResolutionException import org.eclipse.aether.resolution.ArtifactResolutionException
import org.eclipse.aether.resolution.DependencyResolutionException
import org.eclipse.aether.util.artifact.JavaScopes import org.eclipse.aether.util.artifact.JavaScopes
import org.eclipse.aether.util.repository.AuthenticationBuilder import org.eclipse.aether.util.repository.AuthenticationBuilder
import java.io.File import java.io.File
import java.util.*
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.*
import kotlin.script.experimental.dependencies.ExternalDependenciesResolver import kotlin.script.experimental.dependencies.ExternalDependenciesResolver
import kotlin.script.experimental.dependencies.RepositoryCoordinates import kotlin.script.experimental.dependencies.RepositoryCoordinates
@@ -63,13 +62,15 @@ 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 transitive = options.transitive ?: true
val classifier = options.classifier
val extension = options.extension
val deps = AetherResolveSession( val deps = AetherResolveSession(
localRepo, remoteRepositories() localRepo, remoteRepositories()
).resolve( ).resolve(
artifactId, dependencyScopes.joinToString(","), transitive, null artifactId, dependencyScopes.joinToString(","), transitive, null, classifier, extension
) )
ResultWithDiagnostics.Success(deps.map { it.file }) ResultWithDiagnostics.Success(deps.map { it.file })
} catch (e: DependencyResolutionException) { } catch (e: RepositoryException) {
makeResolveFailureResult(e, sourceCodeLocation) makeResolveFailureResult(e, sourceCodeLocation)
} }
} }
@@ -160,7 +161,7 @@ class MavenDependenciesResolver : ExternalDependenciesResolver {
private val FORBIDDEN_CHARS = Regex("[/\\\\:<>\"|?*]") private val FORBIDDEN_CHARS = Regex("[/\\\\:<>\"|?*]")
private fun makeResolveFailureResult( private fun makeResolveFailureResult(
exception: DependencyResolutionException, exception: Throwable,
location: SourceCode.LocationWithId? location: SourceCode.LocationWithId?
): ResultWithDiagnostics.Failure { ): ResultWithDiagnostics.Failure {
val allCauses = generateSequence(exception) { e: Throwable -> e.cause }.toList() val allCauses = generateSequence(exception) { e: Throwable -> e.cause }.toList()
@@ -0,0 +1,59 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.script.experimental.dependencies.maven.impl
import org.eclipse.aether.artifact.Artifact
import org.eclipse.aether.graph.DependencyNode
import org.eclipse.aether.graph.DependencyVisitor
import org.eclipse.aether.resolution.ArtifactRequest
import org.eclipse.aether.util.artifact.DelegatingArtifact
internal class ArtifactRequestBuilder(
private val classifier: String?,
private val extension: String?,
) : DependencyVisitor {
private val result: MutableList<ArtifactRequest> = ArrayList()
override fun visitEnter(node: DependencyNode): Boolean {
val dep = node.dependency
if (dep != null) {
val artifact = dep.artifact
result.add(
ArtifactRequest(
ArtifactWithAnotherKind(artifact, classifier, extension),
node.repositories,
node.requestContext
)
)
}
return true
}
override fun visitLeave(node: DependencyNode): Boolean {
return true
}
val requests: List<ArtifactRequest>
get() = result
}
private class ArtifactWithAnotherKind(
artifact: Artifact,
private val myClassifier: String?,
private val myExtension: String?,
) : DelegatingArtifact(artifact) {
override fun newInstance(artifact: Artifact): DelegatingArtifact {
return ArtifactWithAnotherKind(artifact, myClassifier, myExtension)
}
override fun getClassifier(): String {
return myClassifier ?: super.getClassifier()
}
override fun getExtension(): String {
return myExtension ?: super.getExtension()
}
}
@@ -19,6 +19,8 @@ import org.eclipse.aether.RepositorySystem
import org.eclipse.aether.RepositorySystemSession import org.eclipse.aether.RepositorySystemSession
import org.eclipse.aether.artifact.Artifact import org.eclipse.aether.artifact.Artifact
import org.eclipse.aether.collection.CollectRequest import org.eclipse.aether.collection.CollectRequest
import org.eclipse.aether.collection.CollectResult
import org.eclipse.aether.collection.DependencyCollectionException
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory
import org.eclipse.aether.graph.Dependency import org.eclipse.aether.graph.Dependency
import org.eclipse.aether.graph.DependencyFilter import org.eclipse.aether.graph.DependencyFilter
@@ -35,6 +37,8 @@ import org.eclipse.aether.transport.wagon.WagonConfigurator
import org.eclipse.aether.transport.wagon.WagonProvider import org.eclipse.aether.transport.wagon.WagonProvider
import org.eclipse.aether.transport.wagon.WagonTransporterFactory import org.eclipse.aether.transport.wagon.WagonTransporterFactory
import org.eclipse.aether.util.filter.DependencyFilterUtils import org.eclipse.aether.util.filter.DependencyFilterUtils
import org.eclipse.aether.util.graph.visitor.FilteringDependencyVisitor
import org.eclipse.aether.util.graph.visitor.TreeDependencyVisitor
import org.eclipse.aether.util.repository.AuthenticationBuilder 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
@@ -127,32 +131,48 @@ internal class AetherResolveSession(
} }
} }
fun resolve(root: Artifact, scope: String, transitive: Boolean, filter: DependencyFilter?): List<Artifact> { fun resolve(
return if (transitive) resolveDependencies(root, scope, filter) root: Artifact,
scope: String,
transitive: Boolean,
filter: DependencyFilter?,
classifier: String? = null,
extension: String? = null,
): List<Artifact> {
return if (transitive) resolveDependencies(root, scope, filter, classifier, extension)
else resolveArtifact(root) else resolveArtifact(root)
} }
private fun resolveDependencies(root: Artifact, scope: String, filter: DependencyFilter? = null): List<Artifact> { private fun resolveDependencies(
root: Artifact,
scope: String,
filter: DependencyFilter?,
classifier: String?,
extension: String?,
): List<Artifact> {
return fetch( return fetch(
DependencyRequest( request(Dependency(root, scope)),
request(Dependency(root, scope)), { req ->
filter ?: DependencyFilterUtils.classpathFilter(scope) val requestsBuilder = ArtifactRequestBuilder(classifier, extension)
), val collectionResult = repositorySystem.collectDependencies(repositorySystemSession, req)
{ req -> repositorySystem.resolveDependencies(repositorySystemSession, req).artifactResults }, collectionResult.root.accept(
{ req, ex -> TreeDependencyVisitor(
DependencyResolutionException( FilteringDependencyVisitor(
DependencyResult(req), requestsBuilder,
IllegalArgumentException( //Logger.format( filter ?: DependencyFilterUtils.classpathFilter(scope)
// "failed to load '%s' from %[list]s into %s", )
// req.getCollectRequest().getRoot(),
// Aether.reps(req.getCollectRequest().getRepositories()),
// session.getLocalRepositoryManager()
// .getRepository()
// .getBasedir()
//),
ex
) )
) )
val requests = requestsBuilder.requests
repositorySystem.resolveArtifacts(repositorySystemSession, requests)
},
{ req, ex ->
DependencyCollectionException(
CollectResult(req),
ex.message,
ex
)
} }
) )
} }
@@ -51,6 +51,8 @@ class MavenResolverTest : ResolversTestBase() {
}) })
} }
private fun parseOptions(options: String) = SimpleExternalDependenciesResolverOptionsParser(options).valueOrThrow()
private val resolvedKotlinVersion = "1.5.31" private val resolvedKotlinVersion = "1.5.31"
fun testResolveSimple() { fun testResolveSimple() {
@@ -77,7 +79,6 @@ class MavenResolverTest : ResolversTestBase() {
val dependency = "junit:junit:4.11" val dependency = "junit:junit:4.11"
var transitiveFiles: Iterable<File> var transitiveFiles: Iterable<File>
fun parseOptions(options: String) = SimpleExternalDependenciesResolverOptionsParser(options).valueOrThrow()
resolveAndCheck(dependency, options = parseOptions("transitive=true")) { files -> resolveAndCheck(dependency, options = parseOptions("transitive=true")) { files ->
transitiveFiles = files transitiveFiles = files
@@ -98,6 +99,16 @@ class MavenResolverTest : ResolversTestBase() {
assertEquals("jar", artifact.extension) assertEquals("jar", artifact.extension)
} }
fun testSourcesResolution() {
resolveAndCheck("junit:junit:4.11", options = parseOptions("classifier=sources extension=jar")) { files ->
assertEquals(2, files.count())
files.forEach {
assertTrue(it.name.endsWith("-sources.jar"))
}
true
}
}
fun testResolveVersionsRange() { fun testResolveVersionsRange() {
resolveAndCheck("org.jetbrains.kotlin:kotlin-annotations-jvm:(1.3.40,$resolvedKotlinVersion)") resolveAndCheck("org.jetbrains.kotlin:kotlin-annotations-jvm:(1.3.40,$resolvedKotlinVersion)")
} }
@@ -27,7 +27,9 @@ enum class DependenciesResolverOptionsName(optionName: String? = null) {
USERNAME, USERNAME,
PASSWORD, PASSWORD,
KEY_FILE, KEY_FILE,
KEY_PASSPHRASE; KEY_PASSPHRASE,
CLASSIFIER,
EXTENSION;
val key = optionName ?: name.lowercase() val key = optionName ?: name.lowercase()
} }
@@ -61,3 +63,15 @@ val ExternalDependenciesResolver.Options.privateKeyFile
*/ */
val ExternalDependenciesResolver.Options.privateKeyPassphrase val ExternalDependenciesResolver.Options.privateKeyPassphrase
get() = value(DependenciesResolverOptionsName.KEY_PASSPHRASE) get() = value(DependenciesResolverOptionsName.KEY_PASSPHRASE)
/**
* Classifier of all resolved artifacts
*/
val ExternalDependenciesResolver.Options.classifier
get() = value(DependenciesResolverOptionsName.CLASSIFIER)
/**
* Extension of all resolved artifacts
*/
val ExternalDependenciesResolver.Options.extension
get() = value(DependenciesResolverOptionsName.EXTENSION)