diff --git a/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/MavenDependenciesResolver.kt b/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/MavenDependenciesResolver.kt index 3b9a0221893..f7e52326c1e 100644 --- a/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/MavenDependenciesResolver.kt +++ b/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/MavenDependenciesResolver.kt @@ -5,14 +5,13 @@ package kotlin.script.experimental.dependencies.maven +import org.eclipse.aether.RepositoryException import org.eclipse.aether.artifact.DefaultArtifact import org.eclipse.aether.repository.RemoteRepository import org.eclipse.aether.resolution.ArtifactResolutionException -import org.eclipse.aether.resolution.DependencyResolutionException import org.eclipse.aether.util.artifact.JavaScopes import org.eclipse.aether.util.repository.AuthenticationBuilder import java.io.File -import java.util.* import kotlin.script.experimental.api.* import kotlin.script.experimental.dependencies.ExternalDependenciesResolver import kotlin.script.experimental.dependencies.RepositoryCoordinates @@ -63,13 +62,15 @@ class MavenDependenciesResolver : ExternalDependenciesResolver { return try { val dependencyScopes = options.dependencyScopes ?: listOf(JavaScopes.COMPILE, JavaScopes.RUNTIME) val transitive = options.transitive ?: true + val classifier = options.classifier + val extension = options.extension val deps = AetherResolveSession( localRepo, remoteRepositories() ).resolve( - artifactId, dependencyScopes.joinToString(","), transitive, null + artifactId, dependencyScopes.joinToString(","), transitive, null, classifier, extension ) ResultWithDiagnostics.Success(deps.map { it.file }) - } catch (e: DependencyResolutionException) { + } catch (e: RepositoryException) { makeResolveFailureResult(e, sourceCodeLocation) } } @@ -160,7 +161,7 @@ class MavenDependenciesResolver : ExternalDependenciesResolver { private val FORBIDDEN_CHARS = Regex("[/\\\\:<>\"|?*]") private fun makeResolveFailureResult( - exception: DependencyResolutionException, + exception: Throwable, location: SourceCode.LocationWithId? ): ResultWithDiagnostics.Failure { val allCauses = generateSequence(exception) { e: Throwable -> e.cause }.toList() diff --git a/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/impl/ArtifactRequestBuilder.kt b/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/impl/ArtifactRequestBuilder.kt new file mode 100644 index 00000000000..70ed3eb376f --- /dev/null +++ b/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/impl/ArtifactRequestBuilder.kt @@ -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 = 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 + 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() + } +} diff --git a/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/impl/aether.kt b/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/impl/aether.kt index 268bd784649..7c3d7236057 100644 --- a/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/impl/aether.kt +++ b/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/impl/aether.kt @@ -19,6 +19,8 @@ import org.eclipse.aether.RepositorySystem import org.eclipse.aether.RepositorySystemSession import org.eclipse.aether.artifact.Artifact 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.graph.Dependency 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.WagonTransporterFactory 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.DefaultMirrorSelector 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 { - return if (transitive) resolveDependencies(root, scope, filter) + fun resolve( + root: Artifact, + scope: String, + transitive: Boolean, + filter: DependencyFilter?, + classifier: String? = null, + extension: String? = null, + ): List { + return if (transitive) resolveDependencies(root, scope, filter, classifier, extension) else resolveArtifact(root) } - private fun resolveDependencies(root: Artifact, scope: String, filter: DependencyFilter? = null): List { + private fun resolveDependencies( + root: Artifact, + scope: String, + filter: DependencyFilter?, + classifier: String?, + extension: String?, + ): List { return fetch( - DependencyRequest( - request(Dependency(root, scope)), - filter ?: DependencyFilterUtils.classpathFilter(scope) - ), - { req -> repositorySystem.resolveDependencies(repositorySystemSession, req).artifactResults }, - { req, ex -> - DependencyResolutionException( - DependencyResult(req), - IllegalArgumentException( //Logger.format( - // "failed to load '%s' from %[list]s into %s", - // req.getCollectRequest().getRoot(), - // Aether.reps(req.getCollectRequest().getRepositories()), - // session.getLocalRepositoryManager() - // .getRepository() - // .getBasedir() - //), - ex + request(Dependency(root, scope)), + { req -> + val requestsBuilder = ArtifactRequestBuilder(classifier, extension) + val collectionResult = repositorySystem.collectDependencies(repositorySystemSession, req) + collectionResult.root.accept( + TreeDependencyVisitor( + FilteringDependencyVisitor( + requestsBuilder, + filter ?: DependencyFilterUtils.classpathFilter(scope) + ) ) ) + + val requests = requestsBuilder.requests + repositorySystem.resolveArtifacts(repositorySystemSession, requests) + }, + { req, ex -> + DependencyCollectionException( + CollectResult(req), + ex.message, + ex + ) } ) } diff --git a/libraries/scripting/dependencies-maven/test/kotlin/script/experimental/test/MavenResolverTest.kt b/libraries/scripting/dependencies-maven/test/kotlin/script/experimental/test/MavenResolverTest.kt index c0dba2de024..dceea989107 100644 --- a/libraries/scripting/dependencies-maven/test/kotlin/script/experimental/test/MavenResolverTest.kt +++ b/libraries/scripting/dependencies-maven/test/kotlin/script/experimental/test/MavenResolverTest.kt @@ -51,6 +51,8 @@ class MavenResolverTest : ResolversTestBase() { }) } + private fun parseOptions(options: String) = SimpleExternalDependenciesResolverOptionsParser(options).valueOrThrow() + private val resolvedKotlinVersion = "1.5.31" fun testResolveSimple() { @@ -77,7 +79,6 @@ class MavenResolverTest : ResolversTestBase() { val dependency = "junit:junit:4.11" var transitiveFiles: Iterable - fun parseOptions(options: String) = SimpleExternalDependenciesResolverOptionsParser(options).valueOrThrow() resolveAndCheck(dependency, options = parseOptions("transitive=true")) { files -> transitiveFiles = files @@ -98,6 +99,16 @@ class MavenResolverTest : ResolversTestBase() { 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() { resolveAndCheck("org.jetbrains.kotlin:kotlin-annotations-jvm:(1.3.40,$resolvedKotlinVersion)") } diff --git a/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/resolverNamedOptions.kt b/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/resolverNamedOptions.kt index ed1a14bb95d..731e0812e61 100644 --- a/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/resolverNamedOptions.kt +++ b/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/resolverNamedOptions.kt @@ -27,7 +27,9 @@ enum class DependenciesResolverOptionsName(optionName: String? = null) { USERNAME, PASSWORD, KEY_FILE, - KEY_PASSPHRASE; + KEY_PASSPHRASE, + CLASSIFIER, + EXTENSION; val key = optionName ?: name.lowercase() } @@ -61,3 +63,15 @@ val ExternalDependenciesResolver.Options.privateKeyFile */ val ExternalDependenciesResolver.Options.privateKeyPassphrase 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)