mirror of
https://github.com/tadfisher/gradle2nix.git
synced 2026-01-12 07:50:53 -05:00
Redesign env hierarchy
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Artifact(
|
||||
val urls: List<String>,
|
||||
val hash: String,
|
||||
)
|
||||
@@ -9,6 +9,14 @@ class Logger(
|
||||
val stacktrace: Boolean = false
|
||||
) {
|
||||
|
||||
fun debug(message: String, error: Throwable? = null) {
|
||||
if (!stacktrace) return
|
||||
out.println(message)
|
||||
if (error == null) return
|
||||
error.message?.let { println(" Cause: $it") }
|
||||
error.printStackTrace(out)
|
||||
}
|
||||
|
||||
fun log(message: String, error: Throwable? = null) {
|
||||
if (!verbose) return
|
||||
out.println(message)
|
||||
|
||||
@@ -74,6 +74,9 @@ class Gradle2Nix : CliktCommand(
|
||||
help = "Prefix for environment files (.json and .nix)")
|
||||
.default("gradle-env")
|
||||
|
||||
private val debug: Boolean by option("--debug", help = "Enable debug logging")
|
||||
.flag(default = false)
|
||||
|
||||
private val quiet: Boolean by option("--quiet", "-q", help = "Disable logging")
|
||||
.flag(default = false)
|
||||
|
||||
@@ -114,7 +117,7 @@ class Gradle2Nix : CliktCommand(
|
||||
System.err.println("Error: could not locate the /share directory in the gradle2nix installation")
|
||||
}
|
||||
val gradleHome = System.getenv("GRADLE_USER_HOME")?.let(::File) ?: File("${System.getProperty("user.home")}/.gradle")
|
||||
val logger = Logger(verbose = !quiet)
|
||||
val logger = Logger(verbose = !quiet, stacktrace = debug)
|
||||
|
||||
val config = Config(
|
||||
File(appHome),
|
||||
@@ -146,17 +149,17 @@ class Gradle2Nix : CliktCommand(
|
||||
connection.build(config)
|
||||
}
|
||||
|
||||
val dependencies = try {
|
||||
val env = try {
|
||||
processDependencies(config)
|
||||
} catch (e: Throwable) {
|
||||
error("Dependency parsing failed: ${e.message}")
|
||||
logger.error("Dependency parsing failed", e)
|
||||
}
|
||||
|
||||
val outDir = outDir ?: projectDir
|
||||
val json = outDir.resolve("$envFile.json")
|
||||
logger.log("Writing environment to $json")
|
||||
json.outputStream().buffered().use { output ->
|
||||
JsonFormat.encodeToStream(dependencies, output)
|
||||
JsonFormat.encodeToStream(env, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,16 @@ import okio.HashingSource
|
||||
import okio.blackholeSink
|
||||
import okio.buffer
|
||||
import okio.source
|
||||
import org.nixos.gradle2nix.dependencygraph.model.DependencyCoordinates
|
||||
import org.nixos.gradle2nix.dependencygraph.model.Repository
|
||||
import org.nixos.gradle2nix.dependencygraph.model.ResolvedConfiguration
|
||||
import org.nixos.gradle2nix.dependencygraph.model.ResolvedDependency
|
||||
import org.nixos.gradle2nix.env.ArtifactFile
|
||||
import org.nixos.gradle2nix.env.ArtifactSet
|
||||
import org.nixos.gradle2nix.env.Env
|
||||
import org.nixos.gradle2nix.env.Module
|
||||
import org.nixos.gradle2nix.env.ModuleId
|
||||
import org.nixos.gradle2nix.env.ModuleVersionId
|
||||
import org.nixos.gradle2nix.env.Version
|
||||
import org.nixos.gradle2nix.metadata.Checksum
|
||||
import org.nixos.gradle2nix.metadata.Component
|
||||
import org.nixos.gradle2nix.metadata.Md5
|
||||
@@ -36,12 +43,12 @@ private fun shouldSkipRepository(repository: Repository): Boolean {
|
||||
repository.metadataResources.all { it.startsWith("file:") && (m2 == null || !it.startsWith(m2)) }
|
||||
}
|
||||
|
||||
fun processDependencies(config: Config): Map<String, Map<String, Artifact>> {
|
||||
fun processDependencies(config: Config): Env {
|
||||
val verificationMetadata = readVerificationMetadata(config)
|
||||
val verificationComponents = verificationMetadata?.components?.associateBy {
|
||||
DependencyCoordinates(it.group, it.name, it.version)
|
||||
ModuleVersionId(ModuleId(it.group, it.name), it.version)
|
||||
} ?: emptyMap()
|
||||
val moduleCache = mutableMapOf<DependencyCoordinates, GradleModule?>()
|
||||
val moduleCache = mutableMapOf<ModuleVersionId, GradleModule?>()
|
||||
val configurations = readDependencyGraph(config)
|
||||
|
||||
val repositories = configurations
|
||||
@@ -57,54 +64,53 @@ fun processDependencies(config: Config): Map<String, Map<String, Artifact>> {
|
||||
}
|
||||
if (repositories.isEmpty()) {
|
||||
config.logger.warn("no repositories found in any configuration")
|
||||
return emptyMap()
|
||||
return Env(emptyMap())
|
||||
}
|
||||
config.logger.debug("Repositories:\n ${repositories.values.joinToString("\n ")}")
|
||||
|
||||
return configurations.asSequence()
|
||||
val modules = configurations.asSequence()
|
||||
.flatMap { it.allDependencies.asSequence() }
|
||||
.groupBy { it.id }
|
||||
.mapNotNull { (id, dependencies) ->
|
||||
if (id.startsWith("project ")) return@mapNotNull null
|
||||
val deps = dependencies.toSet()
|
||||
if (deps.isEmpty()) {
|
||||
config.logger.warn("$id: no resolved dependencies in dependency graph")
|
||||
return@mapNotNull null
|
||||
}
|
||||
val coordinates = deps.first().coordinates
|
||||
val component = verificationComponents[coordinates]
|
||||
?: verifyComponentFilesInCache(config, coordinates)
|
||||
?: verifyComponentFilesInTestRepository(config, coordinates)
|
||||
if (component == null) {
|
||||
config.logger.warn("$id: not present in metadata or cache; skipping")
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
val repoIds = dependencies.mapNotNull { it.repository }
|
||||
if (repoIds.isEmpty()) {
|
||||
config.logger.warn("$id: no repository ids in dependency graph; skipping")
|
||||
return@mapNotNull null
|
||||
}
|
||||
val repos = repoIds.mapNotNull(repositories::get)
|
||||
if (repos.isEmpty()) {
|
||||
config.logger.warn("$id: no repositories found for repository ids $repoIds; skipping")
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
val gradleModule = moduleCache.getOrPut(coordinates) {
|
||||
maybeGetGradleModule(config.logger, coordinates, repos)
|
||||
}
|
||||
|
||||
id to component.artifacts.associate { meta ->
|
||||
meta.name to Artifact(
|
||||
urls = repos
|
||||
.flatMap { repository -> artifactUrls(coordinates, meta.name, repository, gradleModule) }
|
||||
.distinct(),
|
||||
hash = meta.checksums.first().toSri()
|
||||
)
|
||||
}
|
||||
.filterNot { it.id.startsWith("project ") || it.repository == null || it.repository !in repositories }
|
||||
.groupBy { ModuleId(it.coordinates.group, it.coordinates.module) }
|
||||
.mapValues { (id, deps) ->
|
||||
val versions = deps.groupBy { Version(it.coordinates.version) }
|
||||
.mapValues { (version, deps) ->
|
||||
val componentId = ModuleVersionId(id, version)
|
||||
val dep = MergedDependency(
|
||||
id = componentId,
|
||||
repositories = deps.mapNotNull { repositories[it.repository] }
|
||||
)
|
||||
val component = verificationComponents[componentId]
|
||||
?: verifyComponentFilesInCache(config, componentId)
|
||||
?: verifyComponentFilesInTestRepository(config, componentId)
|
||||
val gradleModule = moduleCache.getOrPut(componentId) {
|
||||
maybeGetGradleModule(config.logger, componentId, dep.repositories)
|
||||
}
|
||||
ArtifactSet(
|
||||
needsPomRedirect = repositories.values.any {
|
||||
"mavenPom" in it.metadataSources &&
|
||||
"ignoreGradleMetadataRedirection" !in it.metadataSources
|
||||
},
|
||||
needsIvyRedirect = repositories.values.any {
|
||||
"ivyDescriptor" in it.metadataSources &&
|
||||
"ignoreGradleMetadataRedirection" !in it.metadataSources
|
||||
},
|
||||
files = (component?.artifacts ?: emptyList()).associate { meta ->
|
||||
meta.name to ArtifactFile(
|
||||
urls = dep.repositories
|
||||
.flatMap { repository -> artifactUrls(componentId, meta.name, repository, gradleModule) }
|
||||
.distinct(),
|
||||
hash = meta.checksums.first().toSri()
|
||||
)
|
||||
}.toSortedMap()
|
||||
)
|
||||
}
|
||||
.toSortedMap(Version.Comparator.reversed())
|
||||
Module(versions)
|
||||
}
|
||||
.sortedBy { it.first }
|
||||
.toMap()
|
||||
.toSortedMap(compareBy(ModuleId::toString))
|
||||
|
||||
return Env(modules)
|
||||
}
|
||||
|
||||
private fun readVerificationMetadata(config: Config): VerificationMetadata? {
|
||||
@@ -121,52 +127,55 @@ private fun readDependencyGraph(config: Config): List<ResolvedConfiguration> {
|
||||
|
||||
private fun verifyComponentFilesInCache(
|
||||
config: Config,
|
||||
coordinates: DependencyCoordinates,
|
||||
id: ModuleVersionId,
|
||||
): Component? {
|
||||
val cacheDir = with(coordinates) { config.gradleHome.resolve("caches/modules-2/files-2.1/$group/$module/$version") }
|
||||
val cacheDir = with(id) { config.gradleHome.resolve("caches/modules-2/files-2.1/$group/$name/$version") }
|
||||
if (!cacheDir.exists()) {
|
||||
return null
|
||||
}
|
||||
val verifications = cacheDir.walk().filter { it.isFile }.map { f ->
|
||||
ArtifactMetadata(f.name, sha256 = Sha256(f.sha256()))
|
||||
}
|
||||
config.logger.log("$coordinates: obtained artifact hashes from Gradle cache.")
|
||||
return Component(coordinates, verifications.toList())
|
||||
config.logger.log("$id: obtained artifact hashes from Gradle cache.")
|
||||
return Component(id, verifications.toList())
|
||||
}
|
||||
|
||||
private fun verifyComponentFilesInTestRepository(
|
||||
config: Config,
|
||||
coordinates: DependencyCoordinates
|
||||
id: ModuleVersionId
|
||||
): Component? {
|
||||
if (m2 == null) return null
|
||||
val dir = with(coordinates) {
|
||||
File(URI.create(m2)).resolve("${group.replace(".", "/")}/$module/$version")
|
||||
val dir = with(id) {
|
||||
File(URI.create(m2)).resolve("${group.replace(".", "/")}/$name/$version")
|
||||
}
|
||||
if (!dir.exists()) {
|
||||
config.logger.log("$coordinates: not found in m2 repository; tried $dir")
|
||||
config.logger.log("$id: not found in m2 repository; tried $dir")
|
||||
return null
|
||||
}
|
||||
val verifications = dir.walk().filter { it.isFile && it.name.startsWith(coordinates.module) }.map { f ->
|
||||
val verifications = dir.walk().filter { it.isFile && it.name.startsWith(id.name) }.map { f ->
|
||||
ArtifactMetadata(
|
||||
f.name,
|
||||
sha256 = Sha256(f.sha256())
|
||||
)
|
||||
}
|
||||
config.logger.log("$coordinates: obtained artifact hashes from test Maven repository.")
|
||||
return Component(coordinates, verifications.toList())
|
||||
config.logger.log("$id: obtained artifact hashes from test Maven repository.")
|
||||
return Component(id, verifications.toList())
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
private fun maybeGetGradleModule(logger: Logger, coordinates: DependencyCoordinates, repos: List<Repository>): GradleModule? {
|
||||
val filename = with(coordinates) { "$module-$version.module" }
|
||||
private fun maybeGetGradleModule(logger: Logger, id: ModuleVersionId, repos: List<Repository>): GradleModule? {
|
||||
val filename = with(id) { "$name-$version.module" }
|
||||
val reposWithGradleMetadata = repos
|
||||
.filter { "gradleMetadata" in it.metadataSources }
|
||||
.flatMap { artifactUrls(id, filename, it, null)}
|
||||
|
||||
for (url in repos.flatMap { artifactUrls(coordinates, filename, it, null)}) {
|
||||
for (url in reposWithGradleMetadata) {
|
||||
try {
|
||||
return URL(url).openStream().buffered().use { input ->
|
||||
JsonFormat.decodeFromStream(input)
|
||||
}
|
||||
} catch (e: SerializationException) {
|
||||
logger.error("$coordinates: failed to parse Gradle module metadata ($url)", e)
|
||||
logger.error("$id: failed to parse Gradle module metadata ($url)", e)
|
||||
} catch (e: IOException) {
|
||||
// Pass
|
||||
}
|
||||
@@ -192,12 +201,12 @@ private fun Checksum.toSri(): String {
|
||||
}
|
||||
|
||||
private fun artifactUrls(
|
||||
coordinates: DependencyCoordinates,
|
||||
id: ModuleVersionId,
|
||||
filename: String,
|
||||
repository: Repository,
|
||||
module: GradleModule?
|
||||
): List<String> {
|
||||
val groupAsPath = coordinates.group.replace(".", "/")
|
||||
val groupAsPath = id.group.replace(".", "/")
|
||||
|
||||
val repoFilename = module?.let { m ->
|
||||
m.variants
|
||||
@@ -207,10 +216,10 @@ private fun artifactUrls(
|
||||
}?.url ?: filename
|
||||
|
||||
val attributes = mutableMapOf(
|
||||
"organisation" to if (repository.m2Compatible) groupAsPath else coordinates.group,
|
||||
"module" to coordinates.module,
|
||||
"revision" to coordinates.version,
|
||||
) + fileAttributes(repoFilename, coordinates.version)
|
||||
"organisation" to if (repository.m2Compatible) groupAsPath else id.group,
|
||||
"module" to id.name,
|
||||
"revision" to id.version.toString(),
|
||||
) + fileAttributes(repoFilename, id.version)
|
||||
|
||||
val resources = when (attributes["ext"]) {
|
||||
"pom" -> if ("mavenPom" in repository.metadataSources) repository.metadataResources else repository.artifactResources
|
||||
@@ -251,7 +260,7 @@ private fun fill(template: String, attributes: Map<String, String>): String {
|
||||
}
|
||||
|
||||
// Gradle persists artifacts with the Maven artifact pattern, which may not match the repository's pattern.
|
||||
private fun fileAttributes(file: String, version: String): Map<String, String> {
|
||||
private fun fileAttributes(file: String, version: Version): Map<String, String> {
|
||||
val parts = Regex("(.+)-$version(-([^.]+))?(\\.(.+))?").matchEntire(file) ?: return emptyMap()
|
||||
|
||||
val (artifact, _, classifier, _, ext) = parts.destructured
|
||||
@@ -262,3 +271,8 @@ private fun fileAttributes(file: String, version: String): Map<String, String> {
|
||||
put("ext", ext)
|
||||
}
|
||||
}
|
||||
|
||||
private data class MergedDependency(
|
||||
val id: ModuleVersionId,
|
||||
val repositories: List<Repository>
|
||||
)
|
||||
|
||||
233
app/src/main/kotlin/org/nixos/gradle2nix/env/Env.kt
vendored
Normal file
233
app/src/main/kotlin/org/nixos/gradle2nix/env/Env.kt
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
package org.nixos.gradle2nix.env
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import org.gradle.internal.impldep.com.google.common.collect.ImmutableMap
|
||||
import org.gradle.internal.impldep.com.google.common.primitives.Longs
|
||||
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class Env(
|
||||
val modules: Map<ModuleId, Module>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class Module(
|
||||
val versions: Map<Version, ArtifactSet>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ArtifactSet(
|
||||
val needsPomRedirect: Boolean,
|
||||
val needsIvyRedirect: Boolean,
|
||||
val files: Map<String, ArtifactFile>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ArtifactFile(
|
||||
val urls: List<String>,
|
||||
val hash: String,
|
||||
)
|
||||
|
||||
@Serializable(ModuleId.Serializer::class)
|
||||
data class ModuleId(
|
||||
val group: String,
|
||||
val name: String,
|
||||
) {
|
||||
|
||||
override fun toString(): String = "$group:$name"
|
||||
|
||||
companion object Serializer : KSerializer<ModuleId> {
|
||||
override val descriptor: SerialDescriptor get() = PrimitiveSerialDescriptor(
|
||||
ModuleId::class.qualifiedName!!,
|
||||
PrimitiveKind.STRING
|
||||
)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: ModuleId) {
|
||||
encoder.encodeString("${value.name}:${value.group}")
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): ModuleId {
|
||||
val encoded = decoder.decodeString()
|
||||
val parts = encoded.split(":")
|
||||
if (parts.size != 2 || parts.any(String::isBlank)) {
|
||||
throw SerializationException("invalid module id: $encoded")
|
||||
}
|
||||
return ModuleId(parts[0], parts[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ModuleVersionId(
|
||||
val moduleId: ModuleId,
|
||||
val version: Version
|
||||
) {
|
||||
val group: String get() = moduleId.group
|
||||
val name: String get() = moduleId.name
|
||||
|
||||
override fun toString(): String = "$moduleId:$version"
|
||||
}
|
||||
|
||||
@Serializable(Version.Serializer::class)
|
||||
class Version(val source: String, val parts: List<String>, base: Version?) : Comparable<Version> {
|
||||
|
||||
val base: Version
|
||||
val numericParts: List<Long?>
|
||||
|
||||
init {
|
||||
this.base = base ?: this
|
||||
this.numericParts = parts.map(Longs::tryParse)
|
||||
}
|
||||
|
||||
override fun compareTo(other: Version): Int = compare(this, other)
|
||||
|
||||
override fun toString(): String = source
|
||||
|
||||
override fun equals(other: Any?): Boolean = when {
|
||||
other === this -> true
|
||||
other == null || other !is Version -> false
|
||||
else -> source == other.source
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = source.hashCode()
|
||||
|
||||
object Comparator : kotlin.Comparator<Version> {
|
||||
override fun compare(o1: Version, o2: Version): Int =
|
||||
Version.compare(o1, o2)
|
||||
}
|
||||
|
||||
internal object Serializer : KSerializer<Version> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(
|
||||
Version::class.qualifiedName!!,
|
||||
PrimitiveKind.STRING
|
||||
)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Version) {
|
||||
encoder.encodeString(value.source)
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): Version {
|
||||
return Version(decoder.decodeString())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val SPECIAL_MEANINGS: Map<String, Int> = ImmutableMap.builderWithExpectedSize<String, Int>(7)
|
||||
.put("dev", -1)
|
||||
.put("rc", 1)
|
||||
.put("snapshot", 2)
|
||||
.put("final", 3).put("ga", 4).put("release", 5)
|
||||
.put("sp", 6).build()
|
||||
|
||||
private val cache = ConcurrentHashMap<String, Version>()
|
||||
|
||||
// From org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser
|
||||
operator fun invoke(original: String): Version = cache.getOrPut(original) {
|
||||
val parts = mutableListOf<String>()
|
||||
var digit = false
|
||||
var startPart = 0
|
||||
var pos = 0
|
||||
var endBase = 0
|
||||
var endBaseStr = 0
|
||||
while (pos < original.length) {
|
||||
val ch = original[pos]
|
||||
if (ch == '.' || ch == '_' || ch == '-' || ch == '+') {
|
||||
parts.add(original.substring(startPart, pos))
|
||||
startPart = pos + 1
|
||||
digit = false
|
||||
if (ch != '.' && endBaseStr == 0) {
|
||||
endBase = parts.size
|
||||
endBaseStr = pos
|
||||
}
|
||||
} else if (ch in '0'..'9') {
|
||||
if (!digit && pos > startPart) {
|
||||
if (endBaseStr == 0) {
|
||||
endBase = parts.size + 1
|
||||
endBaseStr = pos
|
||||
}
|
||||
parts.add(original.substring(startPart, pos))
|
||||
startPart = pos
|
||||
}
|
||||
digit = true
|
||||
} else {
|
||||
if (digit) {
|
||||
if (endBaseStr == 0) {
|
||||
endBase = parts.size + 1
|
||||
endBaseStr = pos
|
||||
}
|
||||
parts.add(original.substring(startPart, pos))
|
||||
startPart = pos
|
||||
}
|
||||
digit = false
|
||||
}
|
||||
pos++
|
||||
}
|
||||
if (pos > startPart) {
|
||||
parts.add(original.substring(startPart, pos))
|
||||
}
|
||||
var base: Version? = null
|
||||
if (endBaseStr > 0) {
|
||||
base = Version(original.substring(0, endBaseStr), parts.subList(0, endBase), null)
|
||||
}
|
||||
Version(original, parts, base)
|
||||
}
|
||||
|
||||
// From org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.StaticVersionComparator
|
||||
private fun compare(version1: Version, version2: Version): Int {
|
||||
if (version1 == version2) {
|
||||
return 0
|
||||
}
|
||||
|
||||
val parts1 = version1.parts
|
||||
val parts2 = version2.parts
|
||||
val numericParts1 = version1.numericParts
|
||||
val numericParts2 = version2.numericParts
|
||||
var lastIndex = -1
|
||||
|
||||
for (i in 0..<(minOf(parts1.size, parts2.size))) {
|
||||
lastIndex = i
|
||||
|
||||
val part1 = parts1[i]
|
||||
val part2 = parts2[i]
|
||||
|
||||
val numericPart1 = numericParts1[i]
|
||||
val numericPart2 = numericParts2[i]
|
||||
|
||||
when {
|
||||
part1 == part2 -> continue
|
||||
numericPart1 != null && numericPart2 == null -> return 1
|
||||
numericPart2 != null && numericPart1 == null -> return -1
|
||||
numericPart1 != null && numericPart2 != null -> {
|
||||
val result = numericPart1.compareTo(numericPart2)
|
||||
if (result == 0) continue
|
||||
return result
|
||||
}
|
||||
else -> {
|
||||
// both are strings, we compare them taking into account special meaning
|
||||
val sm1 = SPECIAL_MEANINGS[part1.lowercase()]
|
||||
val sm2 = SPECIAL_MEANINGS[part2.lowercase()]
|
||||
if (sm1 != null) return sm1 - (sm2 ?: 0)
|
||||
if (sm2 != null) return -sm2
|
||||
return part1.compareTo(part2)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lastIndex < parts1.size) {
|
||||
return if (numericParts1[lastIndex] == null) -1 else 1
|
||||
}
|
||||
if (lastIndex < parts2.size) {
|
||||
return if (numericParts2[lastIndex] == null) 1 else -1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,9 @@ import nl.adaptivity.xmlutil.serialization.XmlChildrenName
|
||||
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||
import org.nixos.gradle2nix.Logger
|
||||
import org.nixos.gradle2nix.dependencygraph.model.DependencyCoordinates
|
||||
import org.nixos.gradle2nix.DependencyCoordinates
|
||||
import org.nixos.gradle2nix.env.ModuleVersionId
|
||||
import org.nixos.gradle2nix.env.Version
|
||||
|
||||
sealed interface Coordinates {
|
||||
val group: String?
|
||||
@@ -103,13 +105,13 @@ data class Artifact(
|
||||
data class Component(
|
||||
val group: String,
|
||||
val name: String,
|
||||
val version: String,
|
||||
val version: Version,
|
||||
val artifacts: List<Artifact> = emptyList(),
|
||||
) {
|
||||
constructor(coordinates: DependencyCoordinates, artifacts: List<Artifact>) : this(
|
||||
coordinates.group,
|
||||
coordinates.module,
|
||||
coordinates.version,
|
||||
constructor(id: ModuleVersionId, artifacts: List<Artifact>) : this(
|
||||
id.group,
|
||||
id.name,
|
||||
id.version,
|
||||
artifacts
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,30 +5,24 @@ import io.kotest.common.ExperimentalKotest
|
||||
import io.kotest.common.KotestInternal
|
||||
import io.kotest.core.names.TestName
|
||||
import io.kotest.core.source.sourceRef
|
||||
import io.kotest.core.spec.style.scopes.ContainerScope
|
||||
import io.kotest.core.spec.style.scopes.RootScope
|
||||
import io.kotest.core.test.NestedTest
|
||||
import io.kotest.core.test.TestScope
|
||||
import io.kotest.core.test.TestType
|
||||
import io.kotest.extensions.system.withEnvironment
|
||||
import io.kotest.matchers.equals.beEqual
|
||||
import io.kotest.matchers.equals.shouldBeEqual
|
||||
import io.kotest.matchers.file.shouldBeAFile
|
||||
import io.kotest.matchers.paths.shouldBeAFile
|
||||
import io.kotest.matchers.should
|
||||
import java.io.File
|
||||
import java.io.FileFilter
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.createTempDirectory
|
||||
import kotlin.io.path.inputStream
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.decodeFromStream
|
||||
import kotlinx.serialization.json.encodeToStream
|
||||
import okio.use
|
||||
import org.nixos.gradle2nix.env.Env
|
||||
|
||||
private val app = Gradle2Nix()
|
||||
|
||||
@@ -48,7 +42,7 @@ fun fixture(path: String): File {
|
||||
suspend fun TestScope.fixture(
|
||||
project: String,
|
||||
vararg args: String,
|
||||
test: suspend TestScope.(Map<String, Map<String, Artifact>>) -> Unit
|
||||
test: suspend TestScope.(Env) -> Unit
|
||||
) {
|
||||
val tmp = Paths.get("build/tmp/gradle2nix").apply { toFile().mkdirs() }
|
||||
val baseDir = Paths.get("../fixtures", project).toFile()
|
||||
@@ -82,7 +76,7 @@ suspend fun TestScope.fixture(
|
||||
app.main(listOf("-d", tempDir.toString()) + args.withM2())
|
||||
val file = tempDir.resolve("${app.envFile}.json")
|
||||
file.shouldBeAFile()
|
||||
val env: Map<String, Map<String, Artifact>> = file.inputStream().buffered().use { input ->
|
||||
val env: Env = file.inputStream().buffered().use { input ->
|
||||
Json.decodeFromStream(input)
|
||||
}
|
||||
test(env)
|
||||
@@ -110,14 +104,12 @@ suspend fun TestScope.golden(
|
||||
if (!goldenFile.exists()) {
|
||||
fail("Golden file '$filename' doesn't exist. Run with --update-golden to generate.")
|
||||
}
|
||||
val goldenData: Map<String, Map<String, Artifact>> = try {
|
||||
goldenFile.inputStream().buffered().use { input ->
|
||||
json.decodeFromStream(input)
|
||||
}
|
||||
val goldenData = try {
|
||||
goldenFile.readText()
|
||||
} catch (e: SerializationException) {
|
||||
fail("Failed to load golden data from '$filename'. Run with --update-golden to regenerate.")
|
||||
}
|
||||
env should beEqual(goldenData)
|
||||
json.encodeToString(env) should beEqual(goldenData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user