Simple ivy resolver with test

This commit is contained in:
Ilya Chernikov
2018-08-10 11:19:18 +02:00
parent d7f45253d5
commit 79bad24941
9 changed files with 184 additions and 21 deletions
@@ -17,10 +17,8 @@
package org.jetbrains.kotlin.script.util
import org.jetbrains.kotlin.script.InvalidScriptResolverAnnotation
import org.jetbrains.kotlin.script.util.resolvers.DirectResolver
import org.jetbrains.kotlin.script.util.resolvers.FlatLibDirectoryResolver
import org.jetbrains.kotlin.script.util.resolvers.MavenResolver
import org.jetbrains.kotlin.script.util.resolvers.Resolver
import org.jetbrains.kotlin.script.util.resolvers.*
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.File
import java.util.concurrent.Future
import kotlin.script.dependencies.KotlinScriptExternalDependencies
@@ -54,9 +52,14 @@ open class KotlinAnnotatedScriptDependenciesResolver(val baseClassPath: List<Fil
private fun resolveFromAnnotations(script: ScriptContents): List<File> {
script.annotations.forEach { annotation ->
when (annotation) {
is Repository -> FlatLibDirectoryResolver.tryCreate(annotation)?.apply { resolvers.add(this) }
?: resolvers.find { it is MavenResolver }?.takeIf { (it as MavenResolver).tryAddRepo(annotation) }
?: throw IllegalArgumentException("Illegal argument for Repository annotation: $annotation")
is Repository -> {
val isFlat: Boolean = resolvers.firstIsInstanceOrNull<FlatLibDirectoryResolver>()?.tryAddRepo(annotation)
?: (FlatLibDirectoryResolver.tryCreate(annotation)?.also { resolvers.add(it) } != null)
if (!isFlat) {
resolvers.find { it !is FlatLibDirectoryResolver && it.tryAddRepo(annotation) }
?: throw IllegalArgumentException("Illegal argument for Repository annotation: $annotation")
}
}
is DependsOn -> {}
is InvalidScriptResolverAnnotation -> throw Exception("Invalid annotation ${annotation.name}", annotation.error)
else -> throw Exception("Unknown annotation ${annotation.javaClass}")
@@ -74,3 +77,6 @@ class LocalFilesResolver :
class FilesAndMavenResolver :
KotlinAnnotatedScriptDependenciesResolver(emptyList(), arrayListOf(DirectResolver(), MavenResolver()))
class FilesAndIvyResolver :
KotlinAnnotatedScriptDependenciesResolver(emptyList(), arrayListOf(DirectResolver(), IvyResolver()))
@@ -19,28 +19,64 @@ package org.jetbrains.kotlin.script.util.resolvers
import org.jetbrains.kotlin.script.util.DependsOn
import org.jetbrains.kotlin.script.util.Repository
import java.io.File
import java.net.MalformedURLException
import java.net.URL
interface Resolver {
fun tryResolve(dependsOn: DependsOn): Iterable<File>?
fun tryAddRepo(annotation: Repository): Boolean
}
class DirectResolver : Resolver {
override fun tryResolve(dependsOn: DependsOn): Iterable<File>? =
dependsOn.value.takeUnless(String::isBlank)?.let(::File)?.takeIf { it.exists() && (it.isFile || it.isDirectory) }?.let { listOf(it) }
dependsOn.value.takeUnless(String::isBlank)?.let(::File)?.takeIf { it.exists() && (it.isFile || it.isDirectory) }?.let { listOf(it) }
override fun tryAddRepo(annotation: Repository): Boolean = false
}
class FlatLibDirectoryResolver(val path: File) : Resolver {
class FlatLibDirectoryResolver(vararg paths: File) : Resolver {
private val localRepos = arrayListOf<File>()
init {
if (!path.exists() || !path.isDirectory) throw IllegalArgumentException("Invalid flat lib directory repository path '$path'")
for (path in paths) {
if (!path.exists() || !path.isDirectory) throw IllegalArgumentException("Invalid flat lib directory repository path '$path'")
}
localRepos.addAll(paths)
}
override fun tryResolve(dependsOn: DependsOn): Iterable<File>? =
override fun tryResolve(dependsOn: DependsOn): Iterable<File>? {
for (path in localRepos) {
// TODO: add coordinates and wildcard matching
dependsOn.value.takeUnless(String::isBlank)?.let { File(path, it) }?.takeIf { it.exists() && (it.isFile || it.isDirectory) }?.let { listOf(it) }
val res = dependsOn.value.takeUnless(String::isBlank)
?.let { File(path, it) }
?.takeIf { it.exists() && (it.isFile || it.isDirectory) }
if (res != null) return listOf(res)
}
return null
}
override fun tryAddRepo(annotation: Repository): Boolean {
val urlStr = annotation.url.takeIf { it.isNotBlank() } ?: annotation.value.takeIf { it.isNotBlank() } ?: return false
val dirFromUrl = urlStr.toRepositoryUrlOrNull()?.takeIf { it.protocol == "file" }?.path
val repoDir = (dirFromUrl ?: urlStr).toRepositoryFileOrNull() ?: return false
localRepos.add(repoDir)
return true
}
companion object {
fun tryCreate(annotation: Repository): FlatLibDirectoryResolver? =
annotation.value.takeUnless(String::isBlank)?.let(::File)?.takeIf { it.exists() && it.isDirectory }?.let(::FlatLibDirectoryResolver)
annotation.value.takeUnless(String::isBlank)?.let(::File)?.takeIf { it.exists() && it.isDirectory }
?.let { FlatLibDirectoryResolver(it) }
}
}
internal fun String.toRepositoryUrlOrNull(): URL? =
try {
URL(this)
} catch (_: MalformedURLException) {
null
}
internal fun String.toRepositoryFileOrNull(): File? =
File(this).takeIf { it.exists() && it.isDirectory }
@@ -0,0 +1,102 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.script.util.resolvers
import org.apache.ivy.Ivy
import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor
import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
import org.apache.ivy.core.module.id.ModuleRevisionId
import org.apache.ivy.core.resolve.ResolveOptions
import org.apache.ivy.core.settings.IvySettings
import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorWriter
import org.apache.ivy.plugins.resolver.URLResolver
import org.jetbrains.kotlin.script.util.DependsOn
import org.jetbrains.kotlin.script.util.Repository
import java.io.File
class IvyResolver : Resolver {
private fun String.isValidParam() = isNotBlank()
override fun tryResolve(dependsOn: DependsOn): Iterable<File>? {
val artifactId = when {
dependsOn.groupId.isValidParam() || dependsOn.artifactId.isValidParam() -> {
listOf(dependsOn.groupId, dependsOn.artifactId, dependsOn.version)
}
dependsOn.value.isValidParam() && dependsOn.value.count { it == ':' } == 2 -> {
dependsOn.value.split(':')
}
else -> {
error("Unknown set of arguments to maven resolver: ${dependsOn.value}")
}
}
return resolveArtifact(artifactId)
}
private val ivyResolvers = arrayListOf<URLResolver>()
private fun resolveArtifact(artifactId: List<String>): List<File> {
if (ivyResolvers.isEmpty()) {
ivyResolvers.add(
URLResolver().apply {
isM2compatible = true
name = "central"
addArtifactPattern("http://repo1.maven.org/maven2/$DEFAULT_ARTIFACT_PATTERN")
}
)
}
val ivySettings = IvySettings().apply {
for (resolver in ivyResolvers) {
addResolver(resolver)
}
setDefaultResolver(ivyResolvers.first().name)
}
val ivy = Ivy.newInstance(ivySettings)
val ivyfile = File.createTempFile("ivy", ".xml")
ivyfile.deleteOnExit()
val moduleDescriptor = DefaultModuleDescriptor.newDefaultInstance(
ModuleRevisionId.newInstance(artifactId[0], artifactId[1] + "-caller", "working")
)
val depsDescriptor = DefaultDependencyDescriptor(
moduleDescriptor,
ModuleRevisionId.newInstance(artifactId[0], artifactId[1], artifactId[2]),
false, false, true
)
moduleDescriptor.addDependency(depsDescriptor)
//creates an ivy configuration file
XmlModuleDescriptorWriter.write(moduleDescriptor, ivyfile)
val resolveOptions = ResolveOptions().setConfs(arrayOf("default"))
//init resolve report
val report = ivy.resolve(ivyfile.toURI().toURL(), resolveOptions)
return report.allArtifactsReports.map { it.localFile }
}
override fun tryAddRepo(annotation: Repository): Boolean {
val urlStr = annotation.url.takeIf { it.isValidParam() } ?: annotation.value.takeIf { it.isValidParam() } ?: return false
val url = urlStr.toRepositoryUrlOrNull() ?: return false
ivyResolvers.add(
URLResolver().apply {
isM2compatible = true
name = annotation.id.takeIf { it.isValidParam() } ?: url.host
addArtifactPattern("${url.toString().let { if (it.endsWith('/')) it else "$it/" }}$DEFAULT_ARTIFACT_PATTERN")
}
)
return true
}
companion object {
const val DEFAULT_ARTIFACT_PATTERN = "[organisation]/[module]/[revision]/[artifact](-[revision]).[ext]"
}
}
@@ -26,8 +26,6 @@ import org.sonatype.aether.resolution.DependencyResolutionException
import org.sonatype.aether.util.artifact.DefaultArtifact
import org.sonatype.aether.util.artifact.JavaScopes
import java.io.File
import java.net.MalformedURLException
import java.net.URL
import java.util.*
val mavenCentral = RemoteRepository("maven-central", "default", "https://repo.maven.apache.org/maven2/")
@@ -78,13 +76,9 @@ class MavenResolver(val reportError: ((String) -> Unit)? = null): Resolver {
return null
}
fun tryAddRepo(annotation: Repository): Boolean {
override fun tryAddRepo(annotation: Repository): Boolean {
val urlStr = annotation.url.takeIf { it.isValidParam() } ?: annotation.value.takeIf { it.isValidParam() } ?: return false
try {
URL(urlStr)
} catch (_: MalformedURLException) {
return false
}
urlStr.toRepositoryUrlOrNull() ?: return false
repos.add(
RemoteRepository(
if (annotation.id.isValidParam()) annotation.id else "central",
@@ -18,6 +18,7 @@
package org.jetbrains.kotlin.script.util.templates
import org.jetbrains.kotlin.script.util.FilesAndIvyResolver
import org.jetbrains.kotlin.script.util.FilesAndMavenResolver
import org.jetbrains.kotlin.script.util.LocalFilesResolver
import kotlin.script.templates.ScriptTemplateDefinition
@@ -28,6 +29,9 @@ abstract class StandardArgsScriptTemplateWithLocalResolving(val args: Array<Stri
@ScriptTemplateDefinition(resolver = FilesAndMavenResolver::class, scriptFilePattern = ".*\\.kts")
abstract class StandardArgsScriptTemplateWithMavenResolving(val args: Array<String>)
@ScriptTemplateDefinition(resolver = FilesAndIvyResolver::class, scriptFilePattern = ".*\\.kts")
abstract class StandardArgsScriptTemplateWithIvyResolving(val args: Array<String>)
@ScriptTemplateDefinition(resolver = LocalFilesResolver::class, scriptFilePattern = ".*\\.kts")
abstract class BindingsScriptTemplateWithLocalResolving(val bindings: Map<String, Any?>)
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.script.util.templates.BindingsScriptTemplateWithLocalResolving
import org.jetbrains.kotlin.script.util.templates.StandardArgsScriptTemplateWithIvyResolving
import org.jetbrains.kotlin.script.util.templates.StandardArgsScriptTemplateWithLocalResolving
import org.jetbrains.kotlin.script.util.templates.StandardArgsScriptTemplateWithMavenResolving
import org.jetbrains.kotlin.utils.PathUtil.getResourcePathForClass
@@ -108,6 +109,20 @@ done
}
}
@Test
fun testIvyResolveStdJUnitHelloWorld() {
val scriptClass = compileScript("args-junit-hello-world.kts", StandardArgsScriptTemplateWithIvyResolving::class)
if (scriptClass == null) {
System.err.println(classpathFromClassloader(Thread.currentThread().contextClassLoader)?.takeIfContainsAll(KOTLIN_JAVA_RUNTIME_JAR)?.joinToString())
}
Assert.assertNotNull(scriptClass)
captureOut {
scriptClass!!.getConstructor(Array<String>::class.java)!!.newInstance(arrayOf("a1"))
}.let {
Assert.assertEquals(argsHelloWorldOutput.linesSplitTrim(), it.linesSplitTrim())
}
}
private fun compileScript(
scriptFileName: String,
scriptTemplate: KClass<out Any>,