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 dfc374d753e..a5a31dae052 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 @@ -49,8 +49,6 @@ class MavenDependenciesResolver : ExternalDependenciesResolver { private fun remoteRepositories() = if (repos.isEmpty()) arrayListOf(mavenCentral) else repos - private fun allRepositories() = remoteRepositories() + localRepo - private fun String.toMavenArtifact(): DefaultArtifact? = if (this.isNotBlank() && this.count { it == ':' } >= 2) DefaultArtifact(this) else null @@ -63,19 +61,18 @@ class MavenDependenciesResolver : ExternalDependenciesResolver { val artifactId = artifactCoordinates.toMavenArtifact()!! - try { + return try { val dependencyScopes = options.dependencyScopes ?: listOf(JavaScopes.COMPILE, JavaScopes.RUNTIME) + val transitive = options.transitive ?: true val deps = AetherResolveSession( localRepo, remoteRepositories() ).resolve( - artifactId, dependencyScopes.joinToString(",") + artifactId, dependencyScopes.joinToString(","), transitive, null ) - if (deps != null) - return ResultWithDiagnostics.Success(deps.map { it.file }) + ResultWithDiagnostics.Success(deps.map { it.file }) } catch (e: DependencyResolutionException) { - return makeResolveFailureResult(e.message ?: "unknown error", sourceCodeLocation) + makeResolveFailureResult(e.message ?: "unknown error", sourceCodeLocation) } - return makeResolveFailureResult(allRepositories().map { "$it: $artifactId not found" }, sourceCodeLocation) } private fun tryResolveEnvironmentVariable(str: String) = 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 9a3e6398252..588b1cff386 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 @@ -25,10 +25,7 @@ import org.eclipse.aether.internal.transport.wagon.PlexusWagonProvider import org.eclipse.aether.repository.LocalRepository import org.eclipse.aether.repository.Proxy import org.eclipse.aether.repository.RemoteRepository -import org.eclipse.aether.resolution.ArtifactResult -import org.eclipse.aether.resolution.DependencyRequest -import org.eclipse.aether.resolution.DependencyResolutionException -import org.eclipse.aether.resolution.DependencyResult +import org.eclipse.aether.resolution.* import org.eclipse.aether.spi.connector.RepositoryConnectorFactory import org.eclipse.aether.spi.connector.transport.TransporterFactory import org.eclipse.aether.transport.file.FileTransporterFactory @@ -123,22 +120,54 @@ class AetherResolveSession( } } - fun resolve(coordinates: String, scope: String, filter: DependencyFilter? = null): List? = + fun resolve(coordinates: String, scope: String, filter: DependencyFilter? = null): List = resolve(DefaultArtifact(coordinates), scope, filter) - fun resolve(root: Artifact, scope: String, filter: DependencyFilter? = null): List? { + fun resolve(root: Artifact, scope: String, transitive: Boolean, filter: DependencyFilter?): List { + return if (transitive) resolve(root, scope, filter) + else resolveArtifact(root) + } + fun resolve(root: Artifact, scope: String, filter: DependencyFilter? = null): List { return fetch( - repositorySystem, - repositorySystemSession, 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 + ) + ) + } ) } - private fun request(root: Dependency): CollectRequest? { + private fun resolveArtifact(artifact: Artifact): List { + val request = ArtifactRequest() + request.artifact = artifact + for (repo in remotes) { + request.addRepository(repo) + } + + return fetch( + request, + { req -> listOf(repositorySystem.resolveArtifact(repositorySystemSession, req)) }, + { req, ex -> ArtifactResolutionException(listOf(ArtifactResult(req)), ex.message, IllegalArgumentException(ex)) } + ) + } + + private fun request(root: Dependency): CollectRequest { val request = CollectRequest() request.root = root for (repo in remotes) { @@ -147,32 +176,23 @@ class AetherResolveSession( return request } - private fun fetch(system: RepositorySystem, session: RepositorySystemSession, dreq: DependencyRequest): List? { + private fun fetch( + request: RequestT, + fetchBody: (RequestT) -> Collection, + wrapException: (RequestT, Exception) -> Exception + ): List { val deps: MutableList = LinkedList() try { var results: Collection synchronized(this) { - results = system.resolveDependencies(session, dreq) - .artifactResults + results = fetchBody(request) } for (res in results) { deps.add(res.artifact) } // @checkstyle IllegalCatch (1 line) } catch (ex: Exception) { - throw DependencyResolutionException( - DependencyResult(dreq), - IllegalArgumentException( //Logger.format( - // "failed to load '%s' from %[list]s into %s", - // dreq.getCollectRequest().getRoot(), - // Aether.reps(dreq.getCollectRequest().getRepositories()), - // session.getLocalRepositoryManager() - // .getRepository() - // .getBasedir() - //), - ex - ) - ) + throw wrapException(request, ex) } return deps } 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 e3d7b9e8d58..09c526c58b0 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 @@ -10,12 +10,15 @@ import org.junit.Assert import org.junit.Ignore import java.io.File import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.reflect.full.primaryConstructor import kotlin.script.experimental.dependencies.maven.MavenDependenciesResolver import kotlin.script.experimental.api.ResultWithDiagnostics import kotlin.script.experimental.api.valueOrThrow import kotlin.script.experimental.dependencies.* import kotlin.script.experimental.dependencies.impl.DependenciesResolverOptionsName +import kotlin.script.experimental.dependencies.impl.SimpleExternalDependenciesResolverOptionsParser import kotlin.script.experimental.dependencies.impl.makeExternalDependenciesResolverOptions import kotlin.script.experimental.dependencies.impl.set @@ -27,6 +30,9 @@ class MavenResolverTest : ResolversTestBase() { options: ExternalDependenciesResolver.Options = ExternalDependenciesResolver.Options.Empty, checkBody: (Iterable) -> Boolean = { true } ) { + contract { + callsInPlace(checkBody, InvocationKind.EXACTLY_ONCE) + } val resolver = MavenDependenciesResolver() val result = runBlocking { resolver.resolve(coordinates, options) } if (result is ResultWithDiagnostics.Failure) { @@ -74,6 +80,31 @@ class MavenResolverTest : ResolversTestBase() { ) } + fun testTransitiveOption() { + 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 + true + } + + var nonTransitiveFiles: Iterable + resolveAndCheck(dependency, options = parseOptions("transitive=false")) { files -> + nonTransitiveFiles = files + true + } + + val tCount = transitiveFiles.count() + val ntCount = nonTransitiveFiles.count() + val artifact = nonTransitiveFiles.single() + + assertTrue(ntCount < tCount) + assertEquals("jar", artifact.extension) + } + fun testResolveVersionsRange() { resolveAndCheck("org.jetbrains.kotlin:kotlin-annotations-jvm:(1.3.40,1.3.60)") } diff --git a/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/SimpleExternalDependenciesResolverOptionsParser.kt b/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/SimpleExternalDependenciesResolverOptionsParser.kt index da88bf9783a..994638e809c 100644 --- a/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/SimpleExternalDependenciesResolverOptionsParser.kt +++ b/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/SimpleExternalDependenciesResolverOptionsParser.kt @@ -21,7 +21,7 @@ private val equalsRegex = Regex("^[^\\S\\r\\n]*=") * And additionally supports flags without any equality statement: * `foo bar` */ -internal object SimpleExternalDependenciesResolverOptionsParser { +object SimpleExternalDependenciesResolverOptionsParser { private sealed class Token { data class Name(val name: String) : Token() data class Value(val value: String) : Token()