mirror of
https://github.com/tadfisher/gradle2nix.git
synced 2026-01-11 23:40:37 -05:00
Separate plugins for different Gradle APIs
This commit is contained in:
9
plugin/common/build.gradle.kts
Normal file
9
plugin/common/build.gradle.kts
Normal file
@@ -0,0 +1,9 @@
|
||||
plugins {
|
||||
`gradle-kotlin-conventions`
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.gradle.api.get69())
|
||||
api(project(":model"))
|
||||
implementation(libs.serialization.json)
|
||||
}
|
||||
138
plugin/common/src/main/kotlin/DependencyExtractor.kt
Normal file
138
plugin/common/src/main/kotlin/DependencyExtractor.kt
Normal file
@@ -0,0 +1,138 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.decodeFromStream
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import org.gradle.api.internal.artifacts.ivyservice.modulecache.FileStoreAndIndexProvider
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.gradle.internal.hash.ChecksumService
|
||||
import org.gradle.internal.operations.BuildOperationDescriptor
|
||||
import org.gradle.internal.operations.BuildOperationListener
|
||||
import org.gradle.internal.operations.OperationFinishEvent
|
||||
import org.gradle.internal.operations.OperationIdentifier
|
||||
import org.gradle.internal.operations.OperationProgressEvent
|
||||
import org.gradle.internal.operations.OperationStartEvent
|
||||
import org.gradle.internal.resource.ExternalResourceReadBuildOperationType
|
||||
import org.gradle.internal.resource.ExternalResourceReadMetadataBuildOperationType
|
||||
import org.nixos.gradle2nix.model.DependencyCoordinates
|
||||
import org.nixos.gradle2nix.model.DependencySet
|
||||
import org.nixos.gradle2nix.model.impl.DefaultDependencyCoordinates
|
||||
import org.nixos.gradle2nix.model.impl.DefaultDependencySet
|
||||
import org.nixos.gradle2nix.model.impl.DefaultResolvedArtifact
|
||||
import org.nixos.gradle2nix.model.impl.DefaultResolvedDependency
|
||||
import java.io.File
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
interface DependencyExtractorApplier {
|
||||
fun apply(
|
||||
gradle: Gradle,
|
||||
extractor: DependencyExtractor,
|
||||
)
|
||||
}
|
||||
|
||||
class DependencyExtractor : BuildOperationListener {
|
||||
private val urls = ConcurrentHashMap<String, Unit>()
|
||||
|
||||
override fun started(
|
||||
buildOperation: BuildOperationDescriptor,
|
||||
startEvent: OperationStartEvent,
|
||||
) {}
|
||||
|
||||
override fun progress(
|
||||
operationIdentifier: OperationIdentifier,
|
||||
progressEvent: OperationProgressEvent,
|
||||
) {}
|
||||
|
||||
override fun finished(
|
||||
buildOperation: BuildOperationDescriptor,
|
||||
finishEvent: OperationFinishEvent,
|
||||
) {
|
||||
when (val details = buildOperation.details) {
|
||||
is ExternalResourceReadBuildOperationType.Details -> urls.computeIfAbsent(details.location) { Unit }
|
||||
is ExternalResourceReadMetadataBuildOperationType.Details -> urls.computeIfAbsent(details.location) { Unit }
|
||||
else -> null
|
||||
} ?: return
|
||||
}
|
||||
|
||||
fun buildDependencySet(
|
||||
cacheAccess: GradleCacheAccess,
|
||||
checksumService: ChecksumService,
|
||||
fileStoreAndIndexProvider: FileStoreAndIndexProvider,
|
||||
): DependencySet {
|
||||
val files = mutableMapOf<DependencyCoordinates, MutableMap<File, String>>()
|
||||
val mappings = mutableMapOf<DependencyCoordinates, Map<String, String>>()
|
||||
|
||||
cacheAccess.useCache {
|
||||
for ((url, _) in urls) {
|
||||
fileStoreAndIndexProvider.externalResourceIndex.lookup(url)?.let { cached ->
|
||||
cached.cachedFile?.let { file ->
|
||||
cachedComponentId(file)?.let { componentId ->
|
||||
files.getOrPut(componentId, ::mutableMapOf)[file] = url
|
||||
if (file.extension == "module") {
|
||||
parseFileMappings(file)?.let {
|
||||
mappings[componentId] = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DefaultDependencySet(
|
||||
dependencies =
|
||||
buildList {
|
||||
for ((componentId, componentFiles) in files) {
|
||||
add(
|
||||
DefaultResolvedDependency(
|
||||
componentId,
|
||||
buildList {
|
||||
val remoteMappings = mappings[componentId]
|
||||
for ((file, url) in componentFiles) {
|
||||
add(
|
||||
DefaultResolvedArtifact(
|
||||
remoteMappings?.get(file.name) ?: file.name,
|
||||
checksumService.sha256(file).toString(),
|
||||
url,
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> buildList(block: MutableList<T>.() -> Unit): List<T> = mutableListOf<T>().apply(block).toList()
|
||||
|
||||
private fun cachedComponentId(file: File): DependencyCoordinates? {
|
||||
val parts = file.invariantSeparatorsPath.split('/')
|
||||
if (parts.size < 6) return null
|
||||
if (parts[parts.size - 6] != "files-2.1") return null
|
||||
return parts.dropLast(2).takeLast(3).joinToString(":").let(DefaultDependencyCoordinates::parse)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
private fun parseFileMappings(file: File): Map<String, String>? =
|
||||
try {
|
||||
Json.decodeFromStream<JsonObject>(file.inputStream())
|
||||
.jsonObject["variants"]?.jsonArray
|
||||
?.flatMap { it.jsonObject["files"]?.jsonArray ?: emptyList() }
|
||||
?.map { it.jsonObject }
|
||||
?.mapNotNull {
|
||||
val name = it["name"]?.jsonPrimitive?.content ?: return@mapNotNull null
|
||||
val url = it["url"]?.jsonPrimitive?.content ?: return@mapNotNull null
|
||||
if (name != url) name to url else null
|
||||
}
|
||||
?.toMap()
|
||||
?.takeUnless { it.isEmpty() }
|
||||
} catch (e: Throwable) {
|
||||
null
|
||||
}
|
||||
27
plugin/common/src/main/kotlin/DependencySetModelBuilder.kt
Normal file
27
plugin/common/src/main/kotlin/DependencySetModelBuilder.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.internal.artifacts.ivyservice.modulecache.FileStoreAndIndexProvider
|
||||
import org.gradle.internal.hash.ChecksumService
|
||||
import org.gradle.tooling.provider.model.ToolingModelBuilder
|
||||
import org.nixos.gradle2nix.model.DependencySet
|
||||
|
||||
class DependencySetModelBuilder(
|
||||
private val dependencyExtractor: DependencyExtractor,
|
||||
private val cacheAccess: GradleCacheAccess,
|
||||
private val checksumService: ChecksumService,
|
||||
private val fileStoreAndIndexProvider: FileStoreAndIndexProvider,
|
||||
) : ToolingModelBuilder {
|
||||
override fun canBuild(modelName: String): Boolean = modelName == DependencySet::class.qualifiedName
|
||||
|
||||
override fun buildAll(
|
||||
modelName: String,
|
||||
project: Project,
|
||||
): DependencySet {
|
||||
return dependencyExtractor.buildDependencySet(
|
||||
cacheAccess,
|
||||
checksumService,
|
||||
fileStoreAndIndexProvider,
|
||||
)
|
||||
}
|
||||
}
|
||||
32
plugin/common/src/main/kotlin/Gradle2NixPlugin.kt
Normal file
32
plugin/common/src/main/kotlin/Gradle2NixPlugin.kt
Normal file
@@ -0,0 +1,32 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.internal.artifacts.ivyservice.modulecache.FileStoreAndIndexProvider
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.gradle.internal.hash.ChecksumService
|
||||
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
|
||||
|
||||
abstract class AbstractGradle2NixPlugin(
|
||||
private val cacheAccessFactory: GradleCacheAccessFactory,
|
||||
private val dependencyExtractorApplier: DependencyExtractorApplier,
|
||||
private val resolveAllArtifactsApplier: ResolveAllArtifactsApplier,
|
||||
) : Plugin<Gradle> {
|
||||
override fun apply(gradle: Gradle) {
|
||||
val extractor = DependencyExtractor()
|
||||
|
||||
gradle.service<ToolingModelBuilderRegistry>().register(
|
||||
DependencySetModelBuilder(
|
||||
extractor,
|
||||
cacheAccessFactory.create(gradle),
|
||||
gradle.service<ChecksumService>(),
|
||||
gradle.service<FileStoreAndIndexProvider>(),
|
||||
),
|
||||
)
|
||||
|
||||
dependencyExtractorApplier.apply(gradle, extractor)
|
||||
|
||||
gradle.projectsEvaluated {
|
||||
resolveAllArtifactsApplier.apply(gradle)
|
||||
}
|
||||
}
|
||||
}
|
||||
11
plugin/common/src/main/kotlin/GradleCacheAccess.kt
Normal file
11
plugin/common/src/main/kotlin/GradleCacheAccess.kt
Normal file
@@ -0,0 +1,11 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
import org.gradle.api.invocation.Gradle
|
||||
|
||||
fun interface GradleCacheAccessFactory {
|
||||
fun create(gradle: Gradle): GradleCacheAccess
|
||||
}
|
||||
|
||||
interface GradleCacheAccess {
|
||||
fun useCache(block: () -> Unit)
|
||||
}
|
||||
6
plugin/common/src/main/kotlin/GradleExtensions.kt
Normal file
6
plugin/common/src/main/kotlin/GradleExtensions.kt
Normal file
@@ -0,0 +1,6 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
import org.gradle.api.internal.GradleInternal
|
||||
import org.gradle.api.invocation.Gradle
|
||||
|
||||
inline fun <reified T> Gradle.service(): T = (this as GradleInternal).services.get(T::class.java)
|
||||
50
plugin/common/src/main/kotlin/ResolveAllArtifacts.kt
Normal file
50
plugin/common/src/main/kotlin/ResolveAllArtifacts.kt
Normal file
@@ -0,0 +1,50 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.internal.deprecation.DeprecatableConfiguration
|
||||
import org.nixos.gradle2nix.model.RESOLVE_ALL_TASK
|
||||
|
||||
fun interface ResolveAllArtifactsApplier {
|
||||
fun apply(gradle: Gradle)
|
||||
}
|
||||
|
||||
abstract class AbstractResolveAllArtifactsApplier : ResolveAllArtifactsApplier {
|
||||
abstract fun Project.registerProjectTask(): TaskProvider<*>
|
||||
|
||||
final override fun apply(gradle: Gradle) {
|
||||
val resolveAll = gradle.rootProject.tasks.register(RESOLVE_ALL_TASK)
|
||||
|
||||
// Depend on "dependencies" task in all projects
|
||||
gradle.allprojects { project ->
|
||||
val resolveProject = project.registerProjectTask()
|
||||
resolveAll.configure { it.dependsOn(resolveProject) }
|
||||
}
|
||||
|
||||
// Depend on all 'resolveBuildDependencies' task in each included build
|
||||
gradle.includedBuilds.forEach { includedBuild ->
|
||||
resolveAll.configure {
|
||||
it.dependsOn(includedBuild.task(":$RESOLVE_ALL_TASK"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ResolveProjectDependenciesTask : DefaultTask() {
|
||||
@Internal
|
||||
protected fun getReportableConfigurations(): List<Configuration> {
|
||||
return project.configurations.filter { (it as? DeprecatableConfiguration)?.canSafelyBeResolved() ?: true }
|
||||
}
|
||||
|
||||
protected fun Configuration.artifactFiles(): FileCollection {
|
||||
return incoming.artifactView { viewConfiguration ->
|
||||
viewConfiguration.componentFilter { it is ModuleComponentIdentifier }
|
||||
}.files
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user