Switch main-kts to the updated resolvers infrastructure
This commit is contained in:
@@ -123,15 +123,30 @@ inline fun <R1, R2> ResultWithDiagnostics<R1>.onSuccess(body: (R1) -> ResultWith
|
|||||||
* return failure with merged diagnostics after first failed transformation
|
* return failure with merged diagnostics after first failed transformation
|
||||||
* and success with merged diagnostics and list of results if all transformations succeeded
|
* and success with merged diagnostics and list of results if all transformations succeeded
|
||||||
*/
|
*/
|
||||||
inline fun<T, R> Iterable<T>.mapSuccess(body: (T) -> ResultWithDiagnostics<R>): ResultWithDiagnostics<List<R>> {
|
inline fun<T, R> Iterable<T>.mapSuccess(body: (T) -> ResultWithDiagnostics<R>): ResultWithDiagnostics<List<R>> =
|
||||||
|
mapSuccessImpl(body) { results, r ->
|
||||||
|
results.add(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* maps transformation ([body]) over iterable merging diagnostics and flatten the results
|
||||||
|
* return failure with merged diagnostics after first failed transformation
|
||||||
|
* and success with merged diagnostics and list of results if all transformations succeeded
|
||||||
|
*/
|
||||||
|
inline fun<T, R> Iterable<T>.flatMapSuccess(body: (T) -> ResultWithDiagnostics<Collection<R>>): ResultWithDiagnostics<List<R>> =
|
||||||
|
mapSuccessImpl(body) { results, r ->
|
||||||
|
results.addAll(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun<T, R1, R2> Iterable<T>.mapSuccessImpl(body: (T) -> ResultWithDiagnostics<R1>, updateResults: (MutableList<R2>, R1) -> Unit): ResultWithDiagnostics<List<R2>> {
|
||||||
val reports = ArrayList<ScriptDiagnostic>()
|
val reports = ArrayList<ScriptDiagnostic>()
|
||||||
val results = ArrayList<R>()
|
val results = ArrayList<R2>()
|
||||||
for (it in this) {
|
for (it in this) {
|
||||||
val result = body(it)
|
val result = body(it)
|
||||||
reports.addAll(result.reports)
|
reports.addAll(result.reports)
|
||||||
when (result) {
|
when (result) {
|
||||||
is ResultWithDiagnostics.Success -> {
|
is ResultWithDiagnostics.Success -> {
|
||||||
results.add(result.value)
|
updateResults(results, result.value)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
return ResultWithDiagnostics.Failure(reports)
|
return ResultWithDiagnostics.Failure(reports)
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ class FileSystemDependenciesResolver(vararg paths: File) : ExternalDependenciesR
|
|||||||
// TODO: add coordinates and wildcard matching
|
// TODO: add coordinates and wildcard matching
|
||||||
val file = if (repo == null) File(artifactCoordinates) else File(repo, artifactCoordinates)
|
val file = if (repo == null) File(artifactCoordinates) else File(repo, artifactCoordinates)
|
||||||
when {
|
when {
|
||||||
!file.exists() -> messages.add("File '$file' does not exists")
|
!file.exists() -> messages.add("File '$file' not found")
|
||||||
!file.isFile && !file.isDirectory -> messages.add("Path '$file' is neither file nor directory")
|
!file.isFile && !file.isDirectory -> messages.add("Path '$file' is neither file nor directory")
|
||||||
else -> return ResultWithDiagnostics.Success(listOf(file))
|
else -> return ResultWithDiagnostics.Success(listOf(file))
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -68,7 +68,7 @@ class MainKtsTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testResolveError() {
|
fun testResolveError() {
|
||||||
val res = evalFile(File("$TEST_DATA_ROOT/hello-resolve-error.main.kts"))
|
val res = evalFile(File("$TEST_DATA_ROOT/hello-resolve-error.main.kts"))
|
||||||
assertFailed("Unrecognized set of arguments to ivy resolver: abracadabra", res)
|
assertFailed("File 'abracadabra' not found", res)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ dependencies {
|
|||||||
compileOnly("org.apache.ivy:ivy:2.5.0")
|
compileOnly("org.apache.ivy:ivy:2.5.0")
|
||||||
compileOnly(project(":compiler:cli-common"))
|
compileOnly(project(":compiler:cli-common"))
|
||||||
compileOnly(project(":kotlin-scripting-jvm-host"))
|
compileOnly(project(":kotlin-scripting-jvm-host"))
|
||||||
|
compileOnly(project(":kotlin-scripting-dependencies"))
|
||||||
compileOnly(project(":kotlin-script-util"))
|
compileOnly(project(":kotlin-script-util"))
|
||||||
testCompile(project(":kotlin-scripting-jvm-host"))
|
testCompile(project(":kotlin-scripting-jvm-host"))
|
||||||
testCompile(project(":kotlin-script-util"))
|
testCompile(project(":kotlin-script-util"))
|
||||||
@@ -26,6 +27,7 @@ dependencies {
|
|||||||
embedded(project(":kotlin-scripting-common")) { isTransitive = false }
|
embedded(project(":kotlin-scripting-common")) { isTransitive = false }
|
||||||
embedded(project(":kotlin-scripting-jvm")) { isTransitive = false }
|
embedded(project(":kotlin-scripting-jvm")) { isTransitive = false }
|
||||||
embedded(project(":kotlin-scripting-jvm-host")) { isTransitive = false }
|
embedded(project(":kotlin-scripting-jvm-host")) { isTransitive = false }
|
||||||
|
embedded(project(":kotlin-scripting-dependencies")) { isTransitive = false }
|
||||||
embedded(project(":kotlin-script-util")) { isTransitive = false }
|
embedded(project(":kotlin-script-util")) { isTransitive = false }
|
||||||
embedded("org.apache.ivy:ivy:2.5.0")
|
embedded("org.apache.ivy:ivy:2.5.0")
|
||||||
embedded(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false }
|
embedded(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false }
|
||||||
|
|||||||
@@ -19,34 +19,40 @@ import org.apache.ivy.plugins.resolver.IBiblioResolver
|
|||||||
import org.apache.ivy.plugins.resolver.URLResolver
|
import org.apache.ivy.plugins.resolver.URLResolver
|
||||||
import org.apache.ivy.util.DefaultMessageLogger
|
import org.apache.ivy.util.DefaultMessageLogger
|
||||||
import org.apache.ivy.util.Message
|
import org.apache.ivy.util.Message
|
||||||
import org.jetbrains.kotlin.script.util.KotlinAnnotatedScriptDependenciesResolver
|
|
||||||
import org.jetbrains.kotlin.script.util.resolvers.DirectResolver
|
|
||||||
import org.jetbrains.kotlin.script.util.resolvers.experimental.GenericArtifactCoordinates
|
|
||||||
import org.jetbrains.kotlin.script.util.resolvers.experimental.GenericRepositoryCoordinates
|
|
||||||
import org.jetbrains.kotlin.script.util.resolvers.experimental.GenericRepositoryWithBridge
|
|
||||||
import org.jetbrains.kotlin.script.util.resolvers.experimental.MavenArtifactCoordinates
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import kotlin.script.experimental.api.*
|
||||||
|
import kotlin.script.experimental.dependencies.ExternalDependenciesResolver
|
||||||
|
import kotlin.script.experimental.dependencies.RepositoryCoordinates
|
||||||
|
import kotlin.script.experimental.dependencies.impl.toRepositoryUrlOrNull
|
||||||
|
|
||||||
class IvyResolver : GenericRepositoryWithBridge {
|
class IvyResolver : ExternalDependenciesResolver {
|
||||||
|
|
||||||
private fun String?.isValidParam() = this?.isNotBlank() ?: false
|
private fun String?.isValidParam() = this?.isNotBlank() ?: false
|
||||||
|
|
||||||
override fun tryResolve(artifactCoordinates: GenericArtifactCoordinates): Iterable<File>? = with (artifactCoordinates) {
|
override fun acceptsArtifact(artifactCoordinates: String): Boolean = with(artifactCoordinates) {
|
||||||
if (this is MavenArtifactCoordinates && (groupId.isValidParam() || artifactId.isValidParam())) {
|
isValidParam() && count { it == ':' }.let { it == 2 || it == 3 }
|
||||||
resolveArtifact(groupId.orEmpty(), artifactId.orEmpty(), version.orEmpty())
|
}
|
||||||
} else {
|
|
||||||
val artifactType = string.substringAfterLast('@', "").trim()
|
override fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates): Boolean =
|
||||||
val stringCoordinates = if (artifactType.isNotEmpty()) string.removeSuffix("@$artifactType") else string
|
repositoryCoordinates.toRepositoryUrlOrNull() != null
|
||||||
if (stringCoordinates.isValidParam() && stringCoordinates.count { it == ':' }.let { it == 2 || it == 3 }) {
|
|
||||||
val artifactId = stringCoordinates.split(':')
|
override suspend fun resolve(artifactCoordinates: String): ResultWithDiagnostics<List<File>> {
|
||||||
|
|
||||||
|
val artifactType = artifactCoordinates.substringAfterLast('@', "").trim()
|
||||||
|
val stringCoordinates = if (artifactType.isNotEmpty()) artifactCoordinates.removeSuffix("@$artifactType") else artifactCoordinates
|
||||||
|
return if (acceptsArtifact(stringCoordinates)) {
|
||||||
|
val artifactId = stringCoordinates.split(':')
|
||||||
|
try {
|
||||||
resolveArtifact(
|
resolveArtifact(
|
||||||
artifactId[0], artifactId[1], artifactId[2],
|
artifactId[0], artifactId[1], artifactId[2],
|
||||||
if (artifactId.size > 3) artifactId[3] else null,
|
if (artifactId.size > 3) artifactId[3] else null,
|
||||||
if (artifactType.isNotEmpty()) artifactType else null
|
if (artifactType.isNotEmpty()) artifactType else null
|
||||||
)
|
)
|
||||||
} else {
|
} catch (e: Exception) {
|
||||||
error("Unrecognized set of arguments to ivy resolver: $stringCoordinates")
|
makeFailureResult(e.asDiagnostics())
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
makeFailureResult("Unrecognized set of arguments to ivy resolver: $stringCoordinates")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +60,7 @@ class IvyResolver : GenericRepositoryWithBridge {
|
|||||||
|
|
||||||
private fun resolveArtifact(
|
private fun resolveArtifact(
|
||||||
groupId: String, artifactName: String, revision: String, conf: String? = null, type: String? = null
|
groupId: String, artifactName: String, revision: String, conf: String? = null, type: String? = null
|
||||||
): List<File> {
|
): ResultWithDiagnostics<List<File>> {
|
||||||
|
|
||||||
if (ivyResolvers.isEmpty() || ivyResolvers.none { it.name == "central" }) {
|
if (ivyResolvers.isEmpty() || ivyResolvers.none { it.name == "central" }) {
|
||||||
ivyResolvers.add(
|
ivyResolvers.add(
|
||||||
@@ -111,22 +117,23 @@ class IvyResolver : GenericRepositoryWithBridge {
|
|||||||
XmlModuleDescriptorWriter.write(moduleDescriptor, ivyFile)
|
XmlModuleDescriptorWriter.write(moduleDescriptor, ivyFile)
|
||||||
val report = ivy.resolve(ivyFile.toURI().toURL(), resolveOptions)
|
val report = ivy.resolve(ivyFile.toURI().toURL(), resolveOptions)
|
||||||
|
|
||||||
return report.allArtifactsReports.map { it.localFile }
|
val diagnostics = report.allProblemMessages.map { it.asErrorDiagnostics() }
|
||||||
|
|
||||||
|
return if (report.hasError()) makeFailureResult(diagnostics)
|
||||||
|
else report.allArtifactsReports.map { it.localFile }.asSuccess(diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun tryAddRepository(repositoryCoordinates: GenericRepositoryCoordinates): Boolean {
|
override fun addRepository(repositoryCoordinates: RepositoryCoordinates) {
|
||||||
val url = repositoryCoordinates.url
|
val url = repositoryCoordinates.toRepositoryUrlOrNull()
|
||||||
if (url != null) {
|
if (url != null) {
|
||||||
ivyResolvers.add(
|
ivyResolvers.add(
|
||||||
IBiblioResolver().apply {
|
IBiblioResolver().apply {
|
||||||
isM2compatible = true
|
isM2compatible = true
|
||||||
name = repositoryCoordinates.name.takeIf { it.isValidParam() } ?: url.host
|
name = url.host
|
||||||
root = url.toExternalForm()
|
root = url.toExternalForm()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -135,6 +142,3 @@ class IvyResolver : GenericRepositoryWithBridge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FilesAndIvyResolver :
|
|
||||||
KotlinAnnotatedScriptDependenciesResolver(emptyList(), arrayListOf(DirectResolver(), IvyResolver()).asIterable())
|
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 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 org.jetbrains.kotlin.mainKts.impl
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.script.util.DependsOn
|
||||||
|
import org.jetbrains.kotlin.script.util.Repository
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||||
|
import kotlin.script.experimental.api.flatMapSuccess
|
||||||
|
import kotlin.script.experimental.api.makeFailureResult
|
||||||
|
import kotlin.script.experimental.dependencies.ExternalDependenciesResolver
|
||||||
|
import kotlin.script.experimental.dependencies.tryAddRepository
|
||||||
|
|
||||||
|
suspend fun resolveFromAnnotations(resolver: ExternalDependenciesResolver, annotations: Iterable<Annotation>): ResultWithDiagnostics<List<File>> {
|
||||||
|
annotations.forEach { annotation ->
|
||||||
|
when (annotation) {
|
||||||
|
is Repository -> {
|
||||||
|
val repositoryCoordinates = with(annotation) { value.takeIf { it.isNotBlank() } ?: url }
|
||||||
|
if (!resolver.tryAddRepository(repositoryCoordinates))
|
||||||
|
return makeFailureResult("Unrecognized repository coordinates: $repositoryCoordinates")
|
||||||
|
}
|
||||||
|
is DependsOn -> {}
|
||||||
|
else -> return makeFailureResult("Unknown annotation ${annotation.javaClass}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return annotations.filterIsInstance(DependsOn::class.java).flatMapSuccess { dep ->
|
||||||
|
val artifactCoordinates =
|
||||||
|
if (dep.value.isNotBlank()) dep.value
|
||||||
|
else listOf(dep.groupId, dep.artifactId, dep.version).filter { it?.isNotBlank() ?: false }.joinToString(":")
|
||||||
|
resolver.resolve(artifactCoordinates)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -5,7 +5,9 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.mainKts
|
package org.jetbrains.kotlin.mainKts
|
||||||
|
|
||||||
import org.jetbrains.kotlin.mainKts.impl.FilesAndIvyResolver
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.jetbrains.kotlin.mainKts.impl.IvyResolver
|
||||||
|
import org.jetbrains.kotlin.mainKts.impl.resolveFromAnnotations
|
||||||
import org.jetbrains.kotlin.script.util.CompilerOptions
|
import org.jetbrains.kotlin.script.util.CompilerOptions
|
||||||
import org.jetbrains.kotlin.script.util.DependsOn
|
import org.jetbrains.kotlin.script.util.DependsOn
|
||||||
import org.jetbrains.kotlin.script.util.Import
|
import org.jetbrains.kotlin.script.util.Import
|
||||||
@@ -15,6 +17,8 @@ import kotlin.script.dependencies.ScriptContents
|
|||||||
import kotlin.script.dependencies.ScriptDependenciesResolver
|
import kotlin.script.dependencies.ScriptDependenciesResolver
|
||||||
import kotlin.script.experimental.annotations.KotlinScript
|
import kotlin.script.experimental.annotations.KotlinScript
|
||||||
import kotlin.script.experimental.api.*
|
import kotlin.script.experimental.api.*
|
||||||
|
import kotlin.script.experimental.dependencies.CompoundDependenciesResolver
|
||||||
|
import kotlin.script.experimental.dependencies.FileSystemDependenciesResolver
|
||||||
import kotlin.script.experimental.host.FileBasedScriptSource
|
import kotlin.script.experimental.host.FileBasedScriptSource
|
||||||
import kotlin.script.experimental.host.FileScriptSource
|
import kotlin.script.experimental.host.FileScriptSource
|
||||||
import kotlin.script.experimental.jvm.compat.mapLegacyDiagnosticSeverity
|
import kotlin.script.experimental.jvm.compat.mapLegacyDiagnosticSeverity
|
||||||
@@ -60,7 +64,7 @@ object MainKtsEvaluationConfiguration : ScriptEvaluationConfiguration(
|
|||||||
)
|
)
|
||||||
|
|
||||||
class MainKtsConfigurator : RefineScriptCompilationConfigurationHandler {
|
class MainKtsConfigurator : RefineScriptCompilationConfigurationHandler {
|
||||||
private val resolver = FilesAndIvyResolver()
|
private val resolver = CompoundDependenciesResolver(FileSystemDependenciesResolver(), IvyResolver())
|
||||||
|
|
||||||
override operator fun invoke(context: ScriptConfigurationRefinementContext): ResultWithDiagnostics<ScriptCompilationConfiguration> =
|
override operator fun invoke(context: ScriptConfigurationRefinementContext): ResultWithDiagnostics<ScriptCompilationConfiguration> =
|
||||||
processAnnotations(context)
|
processAnnotations(context)
|
||||||
@@ -92,23 +96,21 @@ class MainKtsConfigurator : RefineScriptCompilationConfigurationHandler {
|
|||||||
(it as? CompilerOptions)?.options?.toList() ?: emptyList()
|
(it as? CompilerOptions)?.options?.toList() ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
val resolvedClassPath = try {
|
val resolveResult = try {
|
||||||
val scriptContents = object : ScriptContents {
|
runBlocking {
|
||||||
override val annotations: Iterable<Annotation> = annotations.filter { it is DependsOn || it is Repository }
|
resolveFromAnnotations(resolver, annotations.filter { it is DependsOn || it is Repository })
|
||||||
override val file: File? = null
|
|
||||||
override val text: CharSequence? = null
|
|
||||||
}
|
}
|
||||||
resolver.resolve(scriptContents, emptyMap(), ::report, null).get()?.classpath?.toList()
|
|
||||||
// TODO: add diagnostics
|
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
return ResultWithDiagnostics.Failure(*diagnostics.toTypedArray(), e.asDiagnostics(path = context.script.locationId))
|
ResultWithDiagnostics.Failure(*diagnostics.toTypedArray(), e.asDiagnostics(path = context.script.locationId))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ScriptCompilationConfiguration(context.compilationConfiguration) {
|
return resolveResult.onSuccess { resolvedClassPath ->
|
||||||
if (resolvedClassPath != null) updateClasspath(resolvedClassPath)
|
ScriptCompilationConfiguration(context.compilationConfiguration) {
|
||||||
if (importedSources.isNotEmpty()) importScripts.append(importedSources)
|
if (resolvedClassPath != null) updateClasspath(resolvedClassPath)
|
||||||
if (compileOptions.isNotEmpty()) compilerOptions.append(compileOptions)
|
if (importedSources.isNotEmpty()) importScripts.append(importedSources)
|
||||||
}.asSuccess(diagnostics)
|
if (compileOptions.isNotEmpty()) compilerOptions.append(compileOptions)
|
||||||
|
}.asSuccess()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user