mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-08-16 08:15:50 +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
|
||||
|
||||
import com.volmit.iris.core.scripting.kotlin.runner.resolver.CompoundDependenciesResolver
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.io.File
|
||||
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.jvm.updateClasspath
|
||||
import kotlin.script.experimental.jvm.util.classpathFromClassloader
|
||||
@ -24,37 +22,45 @@ internal fun ResultValue.valueOrNull(): Any? =
|
||||
}
|
||||
|
||||
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()
|
||||
internal fun configureMavenDepsOnAnnotations(context: ScriptConfigurationRefinementContext): ResultWithDiagnostics<ScriptCompilationConfiguration> {
|
||||
val annotations = context.collectedData?.get(ScriptCollectedData.collectedAnnotations)?.takeIf { it.isNotEmpty() }
|
||||
?: return context.compilationConfiguration.asSuccess()
|
||||
|
||||
val reports = mutableListOf<ScriptDiagnostic>()
|
||||
val resolver = context.compilationConfiguration[ScriptCompilationConfiguration.dependencyResolver] ?: resolver
|
||||
val packDirectory = context.compilationConfiguration[ScriptCompilationConfiguration.packDirectory] ?: context.script.locationId?.let(::File)?.takeIf { it.exists() }?.run {
|
||||
val parts = normalize().absolutePath.split(File.separatorChar)
|
||||
|
||||
var packDir: File? = null
|
||||
for (i in parts.size - 1 downTo 1) {
|
||||
if (parts[i] != "scripts") continue
|
||||
val pack = File(parts.subList(0, i).joinToString(File.separator))
|
||||
if (!File(pack, "dimensions${File.separator}${parts[i - 1]}.json").exists())
|
||||
continue
|
||||
packDir = pack
|
||||
break
|
||||
}
|
||||
packDir
|
||||
} ?: workDir
|
||||
context.compilationConfiguration[ScriptCompilationConfiguration.packDirectory]
|
||||
?.addPack(resolver)
|
||||
?: context.script.locationId
|
||||
?.let(::File)
|
||||
?.takeIf { it.exists() }
|
||||
?.run {
|
||||
val location = SourceCode.LocationWithId(context.script.locationId!!, SourceCode.Location(SourceCode.Position(0, 0)))
|
||||
val parts = normalize().absolutePath.split(File.separatorChar)
|
||||
for (i in parts.size - 1 downTo 1) {
|
||||
if (parts[i] != "scripts") continue
|
||||
val pack = File(parts.subList(0, i).joinToString(File.separator))
|
||||
if (!File(pack, "dimensions${File.separator}${parts[i - 1]}.json").exists())
|
||||
continue
|
||||
pack.addPack(resolver)
|
||||
reports.add(ScriptDiagnostic(
|
||||
ScriptDiagnostic.unspecifiedInfo,
|
||||
"Adding pack \"$pack\"",
|
||||
ScriptDiagnostic.Severity.INFO,
|
||||
location
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
return runBlocking {
|
||||
resolver.addRepository(packDirectory.toURI().toURL().toString())
|
||||
resolver.resolveFromScriptSourceAnnotations(annotations)
|
||||
}.onSuccess {
|
||||
context.compilationConfiguration.with {
|
||||
updateClasspath(it)
|
||||
}.asSuccess()
|
||||
}
|
||||
}.appendReports(reports)
|
||||
}
|
||||
|
||||
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.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.util.concurrent.ConcurrentHashMap
|
||||
@ -12,7 +12,7 @@ import kotlin.script.experimental.dependencies.impl.toRepositoryUrlOrNull
|
||||
|
||||
class FileDependenciesResolver(
|
||||
private val baseDir: File,
|
||||
) : ExternalDependenciesResolver {
|
||||
) : DependenciesResolver {
|
||||
private val localRepos = ConcurrentHashMap.newKeySet<File>(1).also { it.add(baseDir) }
|
||||
|
||||
private fun String.toRepositoryFileOrNull(): File? =
|
||||
@ -62,4 +62,8 @@ class FileDependenciesResolver(
|
||||
|
||||
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