mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-08-16 16:26:12 +00:00
make depencency resolvers more predictable
This commit is contained in:
parent
96efc15c36
commit
05193bd0d9
@ -1,11 +1,9 @@
|
|||||||
package com.volmit.iris.core.scripting.kotlin.runner
|
package com.volmit.iris.core.scripting.kotlin.runner
|
||||||
|
|
||||||
|
import com.volmit.iris.core.scripting.kotlin.runner.resolver.CompoundDependenciesResolver
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.script.experimental.api.*
|
import kotlin.script.experimental.api.*
|
||||||
import kotlin.script.experimental.dependencies.CompoundDependenciesResolver
|
|
||||||
import kotlin.script.experimental.dependencies.addRepository
|
|
||||||
import kotlin.script.experimental.dependencies.maven.MavenDependenciesResolver
|
|
||||||
import kotlin.script.experimental.dependencies.resolveFromScriptSourceAnnotations
|
import kotlin.script.experimental.dependencies.resolveFromScriptSourceAnnotations
|
||||||
import kotlin.script.experimental.jvm.updateClasspath
|
import kotlin.script.experimental.jvm.updateClasspath
|
||||||
import kotlin.script.experimental.jvm.util.classpathFromClassloader
|
import kotlin.script.experimental.jvm.util.classpathFromClassloader
|
||||||
@ -24,37 +22,45 @@ internal fun ResultValue.valueOrNull(): Any? =
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val workDir = File(".").normalize()
|
private val workDir = File(".").normalize()
|
||||||
internal fun createResolver(baseDir: File = workDir) = CompoundDependenciesResolver(FileDependenciesResolver(baseDir), MavenDependenciesResolver())
|
internal fun createResolver(baseDir: File = workDir) = CompoundDependenciesResolver(baseDir)
|
||||||
|
|
||||||
private val resolver = createResolver()
|
private val resolver = createResolver()
|
||||||
internal fun configureMavenDepsOnAnnotations(context: ScriptConfigurationRefinementContext): ResultWithDiagnostics<ScriptCompilationConfiguration> {
|
internal fun configureMavenDepsOnAnnotations(context: ScriptConfigurationRefinementContext): ResultWithDiagnostics<ScriptCompilationConfiguration> {
|
||||||
val annotations = context.collectedData?.get(ScriptCollectedData.collectedAnnotations)?.takeIf { it.isNotEmpty() }
|
val annotations = context.collectedData?.get(ScriptCollectedData.collectedAnnotations)?.takeIf { it.isNotEmpty() }
|
||||||
?: return context.compilationConfiguration.asSuccess()
|
?: return context.compilationConfiguration.asSuccess()
|
||||||
|
|
||||||
|
val reports = mutableListOf<ScriptDiagnostic>()
|
||||||
val resolver = context.compilationConfiguration[ScriptCompilationConfiguration.dependencyResolver] ?: resolver
|
val resolver = context.compilationConfiguration[ScriptCompilationConfiguration.dependencyResolver] ?: resolver
|
||||||
val packDirectory = context.compilationConfiguration[ScriptCompilationConfiguration.packDirectory] ?: context.script.locationId?.let(::File)?.takeIf { it.exists() }?.run {
|
context.compilationConfiguration[ScriptCompilationConfiguration.packDirectory]
|
||||||
val parts = normalize().absolutePath.split(File.separatorChar)
|
?.addPack(resolver)
|
||||||
|
?: context.script.locationId
|
||||||
var packDir: File? = null
|
?.let(::File)
|
||||||
for (i in parts.size - 1 downTo 1) {
|
?.takeIf { it.exists() }
|
||||||
if (parts[i] != "scripts") continue
|
?.run {
|
||||||
val pack = File(parts.subList(0, i).joinToString(File.separator))
|
val location = SourceCode.LocationWithId(context.script.locationId!!, SourceCode.Location(SourceCode.Position(0, 0)))
|
||||||
if (!File(pack, "dimensions${File.separator}${parts[i - 1]}.json").exists())
|
val parts = normalize().absolutePath.split(File.separatorChar)
|
||||||
continue
|
for (i in parts.size - 1 downTo 1) {
|
||||||
packDir = pack
|
if (parts[i] != "scripts") continue
|
||||||
break
|
val pack = File(parts.subList(0, i).joinToString(File.separator))
|
||||||
}
|
if (!File(pack, "dimensions${File.separator}${parts[i - 1]}.json").exists())
|
||||||
packDir
|
continue
|
||||||
} ?: workDir
|
pack.addPack(resolver)
|
||||||
|
reports.add(ScriptDiagnostic(
|
||||||
|
ScriptDiagnostic.unspecifiedInfo,
|
||||||
|
"Adding pack \"$pack\"",
|
||||||
|
ScriptDiagnostic.Severity.INFO,
|
||||||
|
location
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return runBlocking {
|
return runBlocking {
|
||||||
resolver.addRepository(packDirectory.toURI().toURL().toString())
|
|
||||||
resolver.resolveFromScriptSourceAnnotations(annotations)
|
resolver.resolveFromScriptSourceAnnotations(annotations)
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
context.compilationConfiguration.with {
|
context.compilationConfiguration.with {
|
||||||
updateClasspath(it)
|
updateClasspath(it)
|
||||||
}.asSuccess()
|
}.asSuccess()
|
||||||
}
|
}.appendReports(reports)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val ClassLoader.classpath get() = classpathFromClassloader(this) ?: emptyList()
|
internal val ClassLoader.classpath get() = classpathFromClassloader(this) ?: emptyList()
|
||||||
@ -64,4 +70,12 @@ fun <R> ResultWithDiagnostics<R>.valueOrThrow(message: CharSequence): R = valueO
|
|||||||
}
|
}
|
||||||
|
|
||||||
val ScriptCompilationConfigurationKeys.dependencyResolver by PropertiesCollection.key(resolver)
|
val ScriptCompilationConfigurationKeys.dependencyResolver by PropertiesCollection.key(resolver)
|
||||||
val ScriptCompilationConfigurationKeys.packDirectory by PropertiesCollection.key<File>()
|
val ScriptCompilationConfigurationKeys.packDirectory by PropertiesCollection.key<File>()
|
||||||
|
|
||||||
|
private fun File.addPack(resolver: CompoundDependenciesResolver) = resolver.addPack(this)
|
||||||
|
private fun <R> ResultWithDiagnostics<R>.appendReports(reports : Collection<ScriptDiagnostic>) =
|
||||||
|
if (reports.isEmpty()) this
|
||||||
|
else when (this) {
|
||||||
|
is ResultWithDiagnostics.Success -> ResultWithDiagnostics.Success(value, this.reports + reports)
|
||||||
|
is ResultWithDiagnostics.Failure -> ResultWithDiagnostics.Failure(this.reports + reports)
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
package com.volmit.iris.core.scripting.kotlin.runner.resolver
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.collections.component1
|
||||||
|
import kotlin.collections.component2
|
||||||
|
import kotlin.collections.iterator
|
||||||
|
import kotlin.collections.set
|
||||||
|
import kotlin.script.experimental.api.IterableResultsCollector
|
||||||
|
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||||
|
import kotlin.script.experimental.api.ScriptDiagnostic
|
||||||
|
import kotlin.script.experimental.api.SourceCode
|
||||||
|
import kotlin.script.experimental.api.asErrorDiagnostics
|
||||||
|
import kotlin.script.experimental.api.asSuccess
|
||||||
|
import kotlin.script.experimental.dependencies.ArtifactWithLocation
|
||||||
|
import kotlin.script.experimental.dependencies.ExternalDependenciesResolver
|
||||||
|
import kotlin.script.experimental.dependencies.RepositoryCoordinates
|
||||||
|
import kotlin.script.experimental.dependencies.impl.makeResolveFailureResult
|
||||||
|
|
||||||
|
class CompoundDependenciesResolver(
|
||||||
|
private val baseDir: File
|
||||||
|
) : DependenciesResolver {
|
||||||
|
private val resolvers = listOf(FileDependenciesResolver(baseDir), LocalMavenDependenciesResolver())
|
||||||
|
|
||||||
|
override fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates): Boolean {
|
||||||
|
return resolvers.any { it.acceptsRepository(repositoryCoordinates) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun acceptsArtifact(artifactCoordinates: String): Boolean {
|
||||||
|
return resolvers.any { it.acceptsArtifact(artifactCoordinates) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addRepository(
|
||||||
|
repositoryCoordinates: RepositoryCoordinates,
|
||||||
|
options: ExternalDependenciesResolver.Options,
|
||||||
|
sourceCodeLocation: SourceCode.LocationWithId?
|
||||||
|
): ResultWithDiagnostics<Boolean> {
|
||||||
|
var success = false
|
||||||
|
var repositoryAdded = false
|
||||||
|
val reports = mutableListOf<ScriptDiagnostic>()
|
||||||
|
|
||||||
|
for (resolver in resolvers) {
|
||||||
|
when (val result = resolver.addRepository(repositoryCoordinates, options, sourceCodeLocation)) {
|
||||||
|
is ResultWithDiagnostics.Success -> {
|
||||||
|
success = true
|
||||||
|
repositoryAdded = repositoryAdded || result.value
|
||||||
|
reports.addAll(result.reports)
|
||||||
|
}
|
||||||
|
is ResultWithDiagnostics.Failure -> reports.addAll(result.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(
|
||||||
|
artifactsWithLocations: List<ArtifactWithLocation>,
|
||||||
|
options: ExternalDependenciesResolver.Options
|
||||||
|
): ResultWithDiagnostics<List<File>> {
|
||||||
|
val resultsCollector = IterableResultsCollector<File>()
|
||||||
|
|
||||||
|
val artifactToResolverIndex = mutableMapOf<ArtifactWithLocation, Int>().apply {
|
||||||
|
for (artifactWithLocation in artifactsWithLocations) {
|
||||||
|
put(artifactWithLocation, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (artifactToResolverIndex.isNotEmpty()) {
|
||||||
|
val resolverGroups = mutableMapOf<Int, MutableList<ArtifactWithLocation>>()
|
||||||
|
|
||||||
|
for ((artifactWithLocation, resolverIndex) in artifactToResolverIndex) {
|
||||||
|
val (artifact, sourceCodeLocation) = artifactWithLocation
|
||||||
|
|
||||||
|
var currentIndex = resolverIndex + 1
|
||||||
|
while (currentIndex < resolvers.size) {
|
||||||
|
if (resolvers[currentIndex].acceptsArtifact(artifact)) break
|
||||||
|
++currentIndex
|
||||||
|
}
|
||||||
|
if (currentIndex == resolvers.size) {
|
||||||
|
if (resolverIndex == -1) {
|
||||||
|
resultsCollector.addDiagnostic(
|
||||||
|
"No suitable dependency resolver found for artifact '$artifact'"
|
||||||
|
.asErrorDiagnostics(locationWithId = sourceCodeLocation)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resolverGroups
|
||||||
|
.getOrPut(currentIndex) { mutableListOf() }
|
||||||
|
.add(artifactWithLocation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
artifactToResolverIndex.clear()
|
||||||
|
for ((resolverIndex, artifacts) in resolverGroups) {
|
||||||
|
val resolver = resolvers[resolverIndex]
|
||||||
|
val resolveResult = resolver.resolve(artifacts, options)
|
||||||
|
resultsCollector.add(resolveResult)
|
||||||
|
if (resolveResult.reports.isNotEmpty()) {
|
||||||
|
for (artifact in artifacts) {
|
||||||
|
artifactToResolverIndex[artifact] = resolverIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultsCollector.getResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addPack(directory: File) {
|
||||||
|
if (!directory.normalize().absolutePath.startsWith(baseDir.normalize().absolutePath))
|
||||||
|
return
|
||||||
|
resolvers.forEach { it.addPack(directory) }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.volmit.iris.core.scripting.kotlin.runner.resolver
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.script.experimental.dependencies.ExternalDependenciesResolver
|
||||||
|
|
||||||
|
interface DependenciesResolver : ExternalDependenciesResolver {
|
||||||
|
fun addPack(directory: File)
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.volmit.iris.core.scripting.kotlin.runner
|
package com.volmit.iris.core.scripting.kotlin.runner.resolver
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
@ -12,7 +12,7 @@ import kotlin.script.experimental.dependencies.impl.toRepositoryUrlOrNull
|
|||||||
|
|
||||||
class FileDependenciesResolver(
|
class FileDependenciesResolver(
|
||||||
private val baseDir: File,
|
private val baseDir: File,
|
||||||
) : ExternalDependenciesResolver {
|
) : DependenciesResolver {
|
||||||
private val localRepos = ConcurrentHashMap.newKeySet<File>(1).also { it.add(baseDir) }
|
private val localRepos = ConcurrentHashMap.newKeySet<File>(1).also { it.add(baseDir) }
|
||||||
|
|
||||||
private fun String.toRepositoryFileOrNull(): File? =
|
private fun String.toRepositoryFileOrNull(): File? =
|
||||||
@ -62,4 +62,8 @@ class FileDependenciesResolver(
|
|||||||
|
|
||||||
override fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates): Boolean = repositoryCoordinates.toFilePath() != null
|
override fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates): Boolean = repositoryCoordinates.toFilePath() != null
|
||||||
|
|
||||||
|
override fun addPack(directory: File) {
|
||||||
|
localRepos.add(directory)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package com.volmit.iris.core.scripting.kotlin.runner.resolver
|
||||||
|
|
||||||
|
import com.volmit.iris.util.io.IO
|
||||||
|
import org.dom4j.Document
|
||||||
|
import org.dom4j.DocumentFactory
|
||||||
|
import org.dom4j.io.SAXReader
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||||
|
import kotlin.script.experimental.api.SourceCode
|
||||||
|
import kotlin.script.experimental.dependencies.ArtifactWithLocation
|
||||||
|
import kotlin.script.experimental.dependencies.ExternalDependenciesResolver
|
||||||
|
import kotlin.script.experimental.dependencies.RepositoryCoordinates
|
||||||
|
import kotlin.script.experimental.dependencies.maven.MavenDependenciesResolver
|
||||||
|
|
||||||
|
class LocalMavenDependenciesResolver : DependenciesResolver {
|
||||||
|
private lateinit var localRepo: File
|
||||||
|
private val maven = MavenDependenciesResolver(true)
|
||||||
|
|
||||||
|
override fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates) = maven.acceptsRepository(repositoryCoordinates)
|
||||||
|
override fun acceptsArtifact(artifactCoordinates: String) = maven.acceptsArtifact(artifactCoordinates)
|
||||||
|
|
||||||
|
override fun addRepository(
|
||||||
|
repositoryCoordinates: RepositoryCoordinates,
|
||||||
|
options: ExternalDependenciesResolver.Options,
|
||||||
|
sourceCodeLocation: SourceCode.LocationWithId?
|
||||||
|
) = maven.addRepository(repositoryCoordinates, options, sourceCodeLocation)
|
||||||
|
|
||||||
|
override suspend fun resolve(
|
||||||
|
artifactsWithLocations: List<ArtifactWithLocation>,
|
||||||
|
options: ExternalDependenciesResolver.Options
|
||||||
|
): ResultWithDiagnostics<List<File>> {
|
||||||
|
val userOld: String? = System.getProperty("org.apache.maven.user-settings")
|
||||||
|
val globalOld: String? = System.getProperty("org.apache.maven.global-settings")
|
||||||
|
|
||||||
|
try {
|
||||||
|
System.setProperty("org.apache.maven.user-settings", createSettings(userOld))
|
||||||
|
System.clearProperty("org.apache.maven.global-settings")
|
||||||
|
|
||||||
|
return maven.resolve(artifactsWithLocations, options)
|
||||||
|
} finally {
|
||||||
|
setProperty("org.apache.maven.user-settings", userOld)
|
||||||
|
setProperty("org.apache.maven.global-settings", globalOld)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createSettings(user: String?): String {
|
||||||
|
val settingsFile = File(localRepo, "settings.xml")
|
||||||
|
val document = readSettings(user)
|
||||||
|
val node = document.selectSingleNode("//localRepository")
|
||||||
|
?: document.rootElement.addElement("localRepository")
|
||||||
|
|
||||||
|
if (node.text != localRepo.absolutePath) {
|
||||||
|
node.text = localRepo.absolutePath
|
||||||
|
|
||||||
|
IO.write(settingsFile, document)
|
||||||
|
}
|
||||||
|
return settingsFile.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun readSettings(user: String?): Document {
|
||||||
|
val baseFile = user?.let(::File)?.takeIf { it.exists() } ?: File(
|
||||||
|
System.getProperty("user.home"),
|
||||||
|
".m2/settings.xml"
|
||||||
|
).takeIf { it.exists() }?.let { return SAXReader().read(it) }
|
||||||
|
return if (baseFile != null) SAXReader().read(baseFile) else DocumentFactory.getInstance().createDocument().also {
|
||||||
|
it.addElement("settings")
|
||||||
|
.addAttribute("xmlns", "http://maven.apache.org/SETTINGS/1.0.0")
|
||||||
|
.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
|
||||||
|
.addAttribute("xsi:schemaLocation", "http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setProperty(name: String, value: String?) {
|
||||||
|
when(value) {
|
||||||
|
null -> System.clearProperty(name)
|
||||||
|
else -> System.setProperty(name, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addPack(directory: File) {
|
||||||
|
if (!::localRepo.isInitialized) {
|
||||||
|
localRepo = directory.resolve(".libs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user