[Scripting] Add transitive support for Maven Resolver

This commit is contained in:
Ilya Muradyan
2021-05-14 21:00:00 +03:00
parent b77a822822
commit a0cc88ab5a
4 changed files with 83 additions and 35 deletions
@@ -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) =
@@ -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<Artifact>? =
fun resolve(coordinates: String, scope: String, filter: DependencyFilter? = null): List<Artifact> =
resolve(DefaultArtifact(coordinates), scope, filter)
fun resolve(root: Artifact, scope: String, filter: DependencyFilter? = null): List<Artifact>? {
fun resolve(root: Artifact, scope: String, transitive: Boolean, filter: DependencyFilter?): List<Artifact> {
return if (transitive) resolve(root, scope, filter)
else resolveArtifact(root)
}
fun resolve(root: Artifact, scope: String, filter: DependencyFilter? = null): List<Artifact> {
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<Artifact> {
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<Artifact>? {
private fun <RequestT> fetch(
request: RequestT,
fetchBody: (RequestT) -> Collection<ArtifactResult>,
wrapException: (RequestT, Exception) -> Exception
): List<Artifact> {
val deps: MutableList<Artifact> = LinkedList()
try {
var results: Collection<ArtifactResult>
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
}
@@ -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<File>) -> 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<File>
fun parseOptions(options: String) = SimpleExternalDependenciesResolverOptionsParser(options).valueOrThrow()
resolveAndCheck(dependency, options = parseOptions("transitive=true")) { files ->
transitiveFiles = files
true
}
var nonTransitiveFiles: Iterable<File>
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)")
}
@@ -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()