Add API to get locations of collected script annotations

#KT-38404 fixed

also:
- Add wrapper class for the location combined with the location id
- Add source code location parameters to external dependency resolvers
- Add tests for locations in annotations
- Add tests for order of annotation resolution for dependencies resolvers
This commit is contained in:
Mathias Quintero
2020-05-20 13:42:08 +02:00
committed by Ilya Chernikov
parent 1539128c3f
commit 83087291df
19 changed files with 550 additions and 85 deletions
@@ -8,6 +8,8 @@ package kotlin.script.experimental.dependencies
import java.io.File
import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.script.experimental.api.ScriptDiagnostic
import kotlin.script.experimental.api.SourceCode
import kotlin.script.experimental.api.asSuccess
import kotlin.script.experimental.dependencies.impl.makeResolveFailureResult
class CompoundDependenciesResolver(private val resolvers: List<ExternalDependenciesResolver>) : ExternalDependenciesResolver {
@@ -22,25 +24,53 @@ class CompoundDependenciesResolver(private val resolvers: List<ExternalDependenc
return resolvers.any { it.acceptsRepository(repositoryCoordinates) }
}
override fun addRepository(repositoryCoordinates: RepositoryCoordinates) {
if (resolvers.count { it.tryAddRepository(repositoryCoordinates) } == 0)
throw Exception("Failed to detect repository type: $repositoryCoordinates")
override fun addRepository(
repositoryCoordinates: RepositoryCoordinates,
sourceCodeLocation: SourceCode.LocationWithId?
): ResultWithDiagnostics<Boolean> {
var success = false
var repositoryAdded = false
val reports = mutableListOf<ScriptDiagnostic>()
for (resolver in resolvers) {
if (resolver.acceptsRepository(repositoryCoordinates)) {
when (val resolveResult = resolver.addRepository(repositoryCoordinates, sourceCodeLocation)) {
is ResultWithDiagnostics.Success -> {
success = true
repositoryAdded = repositoryAdded || resolveResult.value
reports.addAll(resolveResult.reports)
}
is ResultWithDiagnostics.Failure -> reports.addAll(resolveResult.reports)
}
}
}
return when {
success -> repositoryAdded.asSuccess(reports)
reports.isEmpty() -> makeResolveFailureResult(
"No dependency resolver found that recognizes the repository coordinates '$repositoryCoordinates'",
sourceCodeLocation
)
else -> ResultWithDiagnostics.Failure(reports)
}
}
override suspend fun resolve(artifactCoordinates: String): ResultWithDiagnostics<List<File>> {
override suspend fun resolve(
artifactCoordinates: String,
sourceCodeLocation: SourceCode.LocationWithId?
): ResultWithDiagnostics<List<File>> {
val reports = mutableListOf<ScriptDiagnostic>()
for (resolver in resolvers) {
if (resolver.acceptsArtifact(artifactCoordinates)) {
when (val resolveResult = resolver.resolve(artifactCoordinates)) {
when (val resolveResult = resolver.resolve(artifactCoordinates, sourceCodeLocation)) {
is ResultWithDiagnostics.Failure -> reports.addAll(resolveResult.reports)
else -> return resolveResult
}
}
}
return if (reports.count() == 0) {
makeResolveFailureResult("No suitable dependency resolver found for artifact '$artifactCoordinates'")
makeResolveFailureResult("No suitable dependency resolver found for artifact '$artifactCoordinates'", sourceCodeLocation)
} else {
ResultWithDiagnostics.Failure(reports)
}
@@ -7,6 +7,7 @@ package kotlin.script.experimental.dependencies
import java.io.File
import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.script.experimental.api.SourceCode
import kotlin.script.experimental.api.valueOrNull
open class RepositoryCoordinates(val string: String)
@@ -16,23 +17,21 @@ interface ExternalDependenciesResolver {
fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates): Boolean
fun acceptsArtifact(artifactCoordinates: String): Boolean
suspend fun resolve(artifactCoordinates: String): ResultWithDiagnostics<List<File>>
fun addRepository(repositoryCoordinates: RepositoryCoordinates)
suspend fun resolve(
artifactCoordinates: String,
sourceCodeLocation: SourceCode.LocationWithId? = null
): ResultWithDiagnostics<List<File>>
fun addRepository(
repositoryCoordinates: RepositoryCoordinates,
sourceCodeLocation: SourceCode.LocationWithId? = null
): ResultWithDiagnostics<Boolean>
}
fun ExternalDependenciesResolver.acceptsRepository(repositoryCoordinates: String): Boolean =
acceptsRepository(RepositoryCoordinates(repositoryCoordinates))
fun ExternalDependenciesResolver.addRepository(repositoryCoordinates: String) = addRepository(RepositoryCoordinates(repositoryCoordinates))
suspend fun ExternalDependenciesResolver.tryResolve(artifactCoordinates: String): List<File>? =
if (acceptsArtifact(artifactCoordinates)) resolve(artifactCoordinates).valueOrNull() else null
fun ExternalDependenciesResolver.tryAddRepository(repositoryCoordinates: String) =
tryAddRepository(RepositoryCoordinates(repositoryCoordinates))
fun ExternalDependenciesResolver.tryAddRepository(repositoryCoordinates: RepositoryCoordinates) =
if (acceptsRepository(repositoryCoordinates)) {
addRepository(repositoryCoordinates)
true
} else false
fun ExternalDependenciesResolver.addRepository(
repositoryCoordinates: String,
sourceCodeLocation: SourceCode.LocationWithId? = null
) = addRepository(RepositoryCoordinates(repositoryCoordinates), sourceCodeLocation)
@@ -7,6 +7,8 @@ package kotlin.script.experimental.dependencies
import java.io.File
import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.script.experimental.api.SourceCode
import kotlin.script.experimental.api.asSuccess
import kotlin.script.experimental.dependencies.impl.makeResolveFailureResult
import kotlin.script.experimental.dependencies.impl.toRepositoryUrlOrNull
@@ -18,13 +20,24 @@ class FileSystemDependenciesResolver(vararg paths: File) : ExternalDependenciesR
private fun RepositoryCoordinates.toFilePath() =
(this.toRepositoryUrlOrNull()?.takeIf { it.protocol == "file" }?.path ?: string).toRepositoryFileOrNull()
override fun addRepository(repositoryCoordinates: RepositoryCoordinates) {
override fun addRepository(
repositoryCoordinates: RepositoryCoordinates,
sourceCodeLocation: SourceCode.LocationWithId?
): ResultWithDiagnostics<Boolean> {
if (!acceptsRepository(repositoryCoordinates)) return false.asSuccess()
val repoDir = repositoryCoordinates.toFilePath()
?: throw IllegalArgumentException("Invalid repository location: '${repositoryCoordinates}'")
?: return makeResolveFailureResult("Invalid repository location: '${repositoryCoordinates}'", sourceCodeLocation)
localRepos.add(repoDir)
return true.asSuccess()
}
override suspend fun resolve(artifactCoordinates: String): ResultWithDiagnostics<List<File>> {
override suspend fun resolve(
artifactCoordinates: String,
sourceCodeLocation: SourceCode.LocationWithId?
): ResultWithDiagnostics<List<File>> {
if (!acceptsArtifact(artifactCoordinates)) throw IllegalArgumentException("Path is invalid")
val messages = mutableListOf<String>()
@@ -38,7 +51,7 @@ class FileSystemDependenciesResolver(vararg paths: File) : ExternalDependenciesR
else -> return ResultWithDiagnostics.Success(listOf(file))
}
}
return makeResolveFailureResult(messages)
return makeResolveFailureResult(messages, sourceCodeLocation)
}
override fun acceptsArtifact(artifactCoordinates: String) =
@@ -6,9 +6,8 @@
package kotlin.script.experimental.dependencies
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.api.*
import kotlin.script.experimental.util.filterByAnnotationType
/**
* A common annotation that could be used in a script to denote a dependency
@@ -33,22 +32,44 @@ annotation class Repository(vararg val repositoriesCoordinates: String)
/**
* An extension function that configures repositories and resolves artifacts denoted by the [Repository] and [DependsOn] annotations
*/
suspend fun ExternalDependenciesResolver.resolveFromAnnotations(annotations: Iterable<Annotation>): ResultWithDiagnostics<List<File>> {
annotations.forEach { annotation ->
suspend fun ExternalDependenciesResolver.resolveFromScriptSourceAnnotations(
annotations: Iterable<ScriptSourceAnnotation<*>>
): ResultWithDiagnostics<List<File>> {
val reports = mutableListOf<ScriptDiagnostic>()
annotations.forEach { (annotation, locationWithId) ->
when (annotation) {
is Repository -> {
for (coordinates in annotation.repositoriesCoordinates) {
if (!tryAddRepository(coordinates))
return makeFailureResult("Unrecognized repository coordinates: $coordinates")
val added = addRepository(coordinates, locationWithId)
.also { reports.addAll(it.reports) }
.valueOr { return it }
if (!added)
return reports + makeFailureResult(
"Unrecognized repository coordinates: $coordinates",
locationWithId = locationWithId
)
}
}
is DependsOn -> {}
else -> return makeFailureResult("Unknown annotation ${annotation.javaClass}")
else -> return reports + makeFailureResult("Unknown annotation ${annotation.javaClass}", locationWithId = locationWithId)
}
}
return annotations.filterIsInstance(DependsOn::class.java)
.flatMap { it.artifactsCoordinates.asIterable() }
.flatMapSuccess { artifactCoordinates ->
resolve(artifactCoordinates)
return reports + annotations.filterByAnnotationType<DependsOn>()
.flatMapSuccess { (annotation, locationWithId) ->
annotation.artifactsCoordinates.asIterable().flatMapSuccess { artifactCoordinates ->
resolve(artifactCoordinates, locationWithId)
}
}
}
/**
* An extension function that configures repositories and resolves artifacts denoted by the [Repository] and [DependsOn] annotations
*/
suspend fun ExternalDependenciesResolver.resolveFromAnnotations(
annotations: Iterable<Annotation>
): ResultWithDiagnostics<List<File>> {
val scriptSourceAnnotations = annotations.map { ScriptSourceAnnotation(it, null) }
return resolveFromScriptSourceAnnotations(scriptSourceAnnotations)
}
@@ -10,11 +10,16 @@ import java.net.URL
import kotlin.script.experimental.dependencies.RepositoryCoordinates
import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.script.experimental.api.ScriptDiagnostic
import kotlin.script.experimental.api.SourceCode
fun makeResolveFailureResult(message: String) = makeResolveFailureResult(listOf(message))
fun makeResolveFailureResult(message: String, location: SourceCode.LocationWithId? = null) = makeResolveFailureResult(listOf(message), location)
fun makeResolveFailureResult(messages: Iterable<String>) =
ResultWithDiagnostics.Failure(messages.map { ScriptDiagnostic(ScriptDiagnostic.unspecifiedError, it, ScriptDiagnostic.Severity.WARNING) })
fun makeResolveFailureResult(messages: Iterable<String>, location: SourceCode.LocationWithId? = null) =
ResultWithDiagnostics.Failure(
messages.map {
ScriptDiagnostic(ScriptDiagnostic.unspecifiedError, it, ScriptDiagnostic.Severity.WARNING, location)
}
)
fun RepositoryCoordinates.toRepositoryUrlOrNull(): URL? =
try {
@@ -9,6 +9,8 @@ import java.io.File
import kotlin.contracts.ExperimentalContracts
import kotlin.script.experimental.dependencies.*
import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.script.experimental.api.SourceCode
import kotlin.script.experimental.api.asSuccess
import kotlin.script.experimental.dependencies.impl.makeResolveFailureResult
@ExperimentalContracts
@@ -83,14 +85,23 @@ class ResolversTest : ResolversTestBase() {
override fun acceptsArtifact(artifactCoordinates: String): Boolean = acceptsArt(artifactCoordinates)
override suspend fun resolve(artifactCoordinates: String): ResultWithDiagnostics<List<File>> {
override suspend fun resolve(
artifactCoordinates: String,
sourceCodeLocation: SourceCode.LocationWithId?
): ResultWithDiagnostics<List<File>> {
if (!acceptsArtifact(artifactCoordinates)) throw Exception("Path is invalid")
val file = doResolve(artifactCoordinates) ?: return makeResolveFailureResult("Failed to resolve '$artifactCoordinates'")
val file = doResolve(artifactCoordinates)
?: return makeResolveFailureResult("Failed to resolve '$artifactCoordinates'", sourceCodeLocation)
return ResultWithDiagnostics.Success(listOf(file))
}
override fun addRepository(repositoryCoordinates: RepositoryCoordinates) {
override fun addRepository(
repositoryCoordinates: RepositoryCoordinates,
sourceCodeLocation: SourceCode.LocationWithId?
): ResultWithDiagnostics<Boolean> {
if (!acceptsRepository(repositoryCoordinates)) return false.asSuccess()
addRepo(repositoryCoordinates.string)
return true.asSuccess()
}
override fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates): Boolean {