mirror of
https://github.com/tadfisher/gradle2nix.git
synced 2026-01-11 15:30:38 -05:00
First stab
This commit is contained in:
34
plugin/build.gradle.kts
Normal file
34
plugin/build.gradle.kts
Normal file
@@ -0,0 +1,34 @@
|
||||
plugins {
|
||||
id("com.github.johnrengelman.shadow") version "4.0.0"
|
||||
`java-gradle-plugin`
|
||||
`kotlin-dsl`
|
||||
`maven-publish`
|
||||
kotlin("kapt") version embeddedKotlinVersion
|
||||
}
|
||||
|
||||
group = "org.nixos"
|
||||
version = "1.0.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
implementation("org.apache.maven:maven-model:3.5.4")
|
||||
implementation("org.apache.maven:maven-model-builder:3.5.4")
|
||||
implementation("com.squareup.okio:okio:2.2.2")
|
||||
implementation("com.squareup.moshi:moshi:1.8.0")
|
||||
kapt("com.squareup.moshi:moshi-kotlin-codegen:1.8.0")
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
register("gradle2nix") {
|
||||
id = "org.nixos.gradle2nix"
|
||||
displayName = "gradle2nix"
|
||||
description = "Create Nix configurations suitable for reproducible packaging"
|
||||
implementationClass = "org.nixos.gradle2nix.Gradle2NixPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
137
plugin/src/main/kotlin/org/nixos/gradle2nix/Gradle2NixPlugin.kt
Normal file
137
plugin/src/main/kotlin/org/nixos/gradle2nix/Gradle2NixPlugin.kt
Normal file
@@ -0,0 +1,137 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import okio.buffer
|
||||
import okio.source
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.RegularFile
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.internal.DocumentationRegistry
|
||||
import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme
|
||||
import org.gradle.api.internal.plugins.PluginRegistry
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.api.tasks.wrapper.Wrapper
|
||||
import org.gradle.kotlin.dsl.create
|
||||
import org.gradle.kotlin.dsl.named
|
||||
import org.gradle.kotlin.dsl.property
|
||||
import org.gradle.kotlin.dsl.register
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.gradle.plugin.management.PluginRequest
|
||||
import org.gradle.plugin.use.internal.PluginDependencyResolutionServices
|
||||
import org.gradle.plugin.use.internal.PluginResolverFactory
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
open class Gradle2NixPlugin : Plugin<Gradle> {
|
||||
override fun apply(gradle: Gradle) {
|
||||
val configurationNames: List<String> =
|
||||
System.getProperty("org.nixos.gradle2nix.configurations")?.split(",") ?: emptyList()
|
||||
|
||||
val pluginRequests = collectPlugins(gradle)
|
||||
|
||||
gradle.projectsLoaded {
|
||||
val extension = rootProject.extensions.create<Gradle2NixExtension>(
|
||||
"gradle2nix",
|
||||
rootProject,
|
||||
configurationNames
|
||||
)
|
||||
|
||||
val gradleEnv = rootProject.tasks.register("nixGradleEnv", NixGradleEnv::class) {
|
||||
outputDir.set(extension.outputDir)
|
||||
}
|
||||
|
||||
val pluginEnv =
|
||||
rootProject.tasks.register("nixPluginEnv", NixPluginEnv::class, pluginRequests)
|
||||
gradleEnv.configure {
|
||||
inputEnvs.from(pluginEnv)
|
||||
}
|
||||
|
||||
allprojects {
|
||||
val buildscriptEnv = tasks.register("nixBuildscriptEnv", NixBuildscriptEnv::class) {
|
||||
pluginEnvFile.set(pluginEnv.flatMap { it.outputFile })
|
||||
}
|
||||
val projectEnv = tasks.register("nixProjectEnv", NixProjectEnv::class) {
|
||||
configurations.addAll(extension.configurations)
|
||||
}
|
||||
gradleEnv.configure {
|
||||
inputEnvs.from(buildscriptEnv)
|
||||
inputEnvs.from(projectEnv)
|
||||
}
|
||||
}
|
||||
|
||||
resolveGradleDist(gradle, extension, gradleEnv)
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectPlugins(gradle: Gradle): List<PluginRequest> {
|
||||
val pluginRequests = mutableListOf<PluginRequest>()
|
||||
gradle.settingsEvaluated {
|
||||
pluginManagement.resolutionStrategy.eachPlugin {
|
||||
if (requested.id.namespace != null && requested.id.namespace != "org.gradle") {
|
||||
pluginRequests.add(target)
|
||||
}
|
||||
}
|
||||
}
|
||||
return pluginRequests
|
||||
}
|
||||
|
||||
private fun resolveGradleDist(
|
||||
gradle: Gradle,
|
||||
extension: Gradle2NixExtension,
|
||||
gradleEnv: TaskProvider<NixGradleEnv>
|
||||
) {
|
||||
gradle.projectsEvaluated {
|
||||
val gradleDist = rootProject.tasks.named("wrapper", Wrapper::class).map {
|
||||
GradleDist(
|
||||
version = it.gradleVersion,
|
||||
type = it.distributionType.name.toLowerCase(Locale.US),
|
||||
url = it.distributionUrl,
|
||||
sha256 = it.distributionSha256Sum ?: fetchDistSha256(it.distributionUrl),
|
||||
nativeVersion = gradle.gradleHomeDir?.resolve("lib")?.listFiles()
|
||||
?.firstOrNull { f -> f.name.matches(nativePlatformJarRegex) }?.let { nf ->
|
||||
nativePlatformJarRegex.find(nf.name)?.groupValues?.get(1)
|
||||
}
|
||||
?: throw IllegalStateException("""
|
||||
Failed to find native-platform jar in ${gradle.gradleHomeDir}.
|
||||
|
||||
Ask Tad to fix this.
|
||||
""".trimIndent())
|
||||
)
|
||||
}
|
||||
val gradleDistTask =
|
||||
gradle.rootProject.tasks.register("nixGradleDist", NixGradleDist::class) {
|
||||
this.gradleDist.set(gradleDist)
|
||||
outputDir.set(extension.outputDir)
|
||||
}
|
||||
gradleEnv.configure {
|
||||
dependsOn(gradleDistTask)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal val moshi by lazy { Moshi.Builder().build() }
|
||||
|
||||
open class Gradle2NixExtension(project: Project, defaultConfigurations: List<String>) {
|
||||
var outputDir: File = project.projectDir.resolve("gradle/nix")
|
||||
var configurations: MutableList<String> = mutableListOf<String>().apply {
|
||||
addAll(defaultConfigurations)
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchDistSha256(url: String): String {
|
||||
return URL("$url.sha256").openConnection().run {
|
||||
connect()
|
||||
getInputStream().source().buffer().use { source ->
|
||||
source.readUtf8()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val nativePlatformJarRegex = Regex("""native-platform-(\d\.\d+)\.jar""")
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
import okio.buffer
|
||||
import okio.source
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import java.lang.IllegalStateException
|
||||
import java.net.URI
|
||||
|
||||
open class NixBuildscriptEnv : NixEnv() {
|
||||
@InputFile
|
||||
val pluginEnvFile = project.objects.fileProperty()
|
||||
|
||||
private val pluginEnv: BuildEnv by lazy {
|
||||
pluginEnvFile.get().asFile.source().buffer().use { src ->
|
||||
moshi.adapter(BuildEnv::class.java).fromJson(src)
|
||||
?: throw IllegalStateException(
|
||||
"Cannot load plugin env from ${pluginEnvFile.get().asFile.path}")
|
||||
}
|
||||
}
|
||||
|
||||
private val resolver by lazy {
|
||||
Resolver(project.buildscript.configurations,
|
||||
project.buildscript.dependencies,
|
||||
logger
|
||||
)
|
||||
}
|
||||
|
||||
override fun environment(): String = "buildscript"
|
||||
|
||||
override fun repositories(): List<String> =
|
||||
project.buildscript.repositories.flatMap { it.repositoryUrls() }.map(URI::toString)
|
||||
|
||||
override fun artifacts(): List<Artifact> {
|
||||
return project.buildscript.configurations
|
||||
.filter { it.isCanBeResolved }
|
||||
.flatMap { resolver.resolveDependencies(it) + resolver.resolvePoms(it) }
|
||||
.minus(pluginEnv.artifacts)
|
||||
.sorted()
|
||||
.distinct()
|
||||
}
|
||||
|
||||
override fun filename(): String = "buildscript.json"
|
||||
}
|
||||
73
plugin/src/main/kotlin/org/nixos/gradle2nix/NixEnv.kt
Normal file
73
plugin/src/main/kotlin/org/nixos/gradle2nix/NixEnv.kt
Normal file
@@ -0,0 +1,73 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.Moshi
|
||||
import okio.buffer
|
||||
import okio.sink
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.artifacts.repositories.ArtifactRepository
|
||||
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import java.net.URI
|
||||
|
||||
abstract class NixEnv : DefaultTask() {
|
||||
abstract fun environment(): String
|
||||
abstract fun repositories(): List<String>
|
||||
abstract fun artifacts(): List<Artifact>
|
||||
abstract fun filename(): String
|
||||
|
||||
@Internal
|
||||
val outputDir = project.objects.directoryProperty()
|
||||
.convention(project.layout.buildDirectory.dir("nix"))
|
||||
|
||||
@OutputFile
|
||||
val outputFile = project.objects.fileProperty()
|
||||
.convention(outputDir.map { it.file(filename()) })
|
||||
|
||||
@TaskAction
|
||||
open fun run() {
|
||||
val outFile = outputFile.get().asFile
|
||||
outFile.parentFile.mkdirs()
|
||||
|
||||
val buildEnv = BuildEnv(project.path, environment(), repositories(), artifacts())
|
||||
outFile.sink().buffer().use { out ->
|
||||
moshi.adapter(BuildEnv::class.java)
|
||||
.indent(" ")
|
||||
.toJson(out, buildEnv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class BuildEnv(
|
||||
val path: String,
|
||||
val env: String,
|
||||
val repositories: List<String>,
|
||||
val artifacts: List<Artifact>
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Artifact(
|
||||
val groupId: String,
|
||||
val artifactId: String,
|
||||
val version: String,
|
||||
val classifier: String,
|
||||
val extension: String,
|
||||
val sha256: String
|
||||
) : Comparable<Artifact> {
|
||||
override fun toString() = "$groupId:$artifactId:$version:$classifier:$extension"
|
||||
|
||||
override fun compareTo(other: Artifact): Int {
|
||||
return toString().compareTo(other.toString())
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ArtifactRepository.repositoryUrls(): Set<URI> {
|
||||
return when (this) {
|
||||
is MavenArtifactRepository -> setOf(url) + artifactUrls
|
||||
else -> emptySet()
|
||||
}.filterNotTo(mutableSetOf()) { it.scheme == "file" }
|
||||
}
|
||||
44
plugin/src/main/kotlin/org/nixos/gradle2nix/NixGradleDist.kt
Normal file
44
plugin/src/main/kotlin/org/nixos/gradle2nix/NixGradleDist.kt
Normal file
@@ -0,0 +1,44 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import okio.buffer
|
||||
import okio.sink
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.kotlin.dsl.property
|
||||
import java.io.Serializable
|
||||
|
||||
open class NixGradleDist : DefaultTask() {
|
||||
@Input
|
||||
internal val gradleDist = project.objects.property<GradleDist>()
|
||||
|
||||
@OutputDirectory
|
||||
val outputDir = project.objects.directoryProperty()
|
||||
|
||||
@OutputFile
|
||||
val outputFile = project.objects.fileProperty()
|
||||
.convention(outputDir.file("gradle-dist.json"))
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
if (gradleDist.isPresent) {
|
||||
outputFile.asFile.get().also { it.parentFile.mkdirs() }.sink().buffer().use { out ->
|
||||
moshi.adapter(GradleDist::class.java)
|
||||
.indent(" ")
|
||||
.toJson(out, gradleDist.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class GradleDist(
|
||||
val version: String,
|
||||
val type: String,
|
||||
val url: String,
|
||||
val sha256: String,
|
||||
val nativeVersion: String
|
||||
) : Serializable
|
||||
58
plugin/src/main/kotlin/org/nixos/gradle2nix/NixGradleEnv.kt
Normal file
58
plugin/src/main/kotlin/org/nixos/gradle2nix/NixGradleEnv.kt
Normal file
@@ -0,0 +1,58 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
import com.squareup.moshi.JsonWriter
|
||||
import okio.buffer
|
||||
import okio.sink
|
||||
import okio.source
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.RegularFile
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.kotlin.dsl.listProperty
|
||||
|
||||
open class NixGradleEnv : DefaultTask() {
|
||||
|
||||
@InputFiles
|
||||
val inputEnvs = project.objects.fileCollection()
|
||||
|
||||
@Internal
|
||||
val outputDir = project.objects.directoryProperty()
|
||||
|
||||
@OutputFile
|
||||
val outputFile = project.objects.fileProperty()
|
||||
.convention(outputDir.file("gradle-env.json"))
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val envsByPath = inputEnvs.map { file ->
|
||||
file.source().buffer().use {
|
||||
moshi.adapter(BuildEnv::class.java).fromJson(it)
|
||||
?: throw IllegalStateException(
|
||||
"Failed to load build env from ${file.path}."
|
||||
)
|
||||
}
|
||||
}.groupBy(BuildEnv::path)
|
||||
|
||||
val outFile = outputFile.get().asFile.also { it.parentFile.mkdirs() }
|
||||
|
||||
JsonWriter.of(outFile.sink().buffer()).use { writer ->
|
||||
val adapter = moshi.adapter(BuildEnv::class.java).indent(" ")
|
||||
writer.indent = " "
|
||||
writer.beginObject()
|
||||
for ((path, envs) in envsByPath) {
|
||||
writer.name(path)
|
||||
writer.beginObject()
|
||||
for (env in envs) {
|
||||
writer.name(env.env)
|
||||
adapter.toJson(writer, env)
|
||||
}
|
||||
writer.endObject()
|
||||
}
|
||||
writer.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
110
plugin/src/main/kotlin/org/nixos/gradle2nix/NixPluginEnv.kt
Normal file
110
plugin/src/main/kotlin/org/nixos/gradle2nix/NixPluginEnv.kt
Normal file
@@ -0,0 +1,110 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
import org.gradle.api.artifacts.ExternalModuleDependency
|
||||
import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme
|
||||
import org.gradle.api.internal.plugins.PluginImplementation
|
||||
import org.gradle.plugin.management.PluginRequest
|
||||
import org.gradle.plugin.management.internal.PluginRequestInternal
|
||||
import org.gradle.plugin.use.PluginId
|
||||
import org.gradle.plugin.use.internal.PluginDependencyResolutionServices
|
||||
import org.gradle.plugin.use.resolve.internal.ArtifactRepositoriesPluginResolver
|
||||
import org.gradle.plugin.use.resolve.internal.PluginResolution
|
||||
import org.gradle.plugin.use.resolve.internal.PluginResolutionResult
|
||||
import org.gradle.plugin.use.resolve.internal.PluginResolveContext
|
||||
import java.net.URI
|
||||
import javax.inject.Inject
|
||||
|
||||
open class NixPluginEnv @Inject constructor(
|
||||
private val pluginDependencyResolutionServices: PluginDependencyResolutionServices,
|
||||
versionSelectorScheme: VersionSelectorScheme,
|
||||
private val pluginRequests: Collection<PluginRequest>
|
||||
) : NixEnv() {
|
||||
private val repositories by lazy {
|
||||
pluginDependencyResolutionServices.resolveRepositoryHandler
|
||||
}
|
||||
|
||||
private val artifactRepositoriesPluginResolver = ArtifactRepositoriesPluginResolver(
|
||||
pluginDependencyResolutionServices,
|
||||
versionSelectorScheme
|
||||
)
|
||||
|
||||
private val resolver by lazy {
|
||||
Resolver(
|
||||
pluginDependencyResolutionServices.configurationContainer,
|
||||
pluginDependencyResolutionServices.dependencyHandler,
|
||||
logger
|
||||
)
|
||||
}
|
||||
|
||||
private val pluginResult by lazy {
|
||||
PluginResult().apply {
|
||||
for (request in pluginRequests.filterIsInstance<PluginRequestInternal>()) {
|
||||
artifactRepositoriesPluginResolver.resolve(request, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val pluginContext by lazy {
|
||||
PluginContext().apply {
|
||||
for (result in pluginResult.found) result.execute(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun environment(): String = "plugins"
|
||||
|
||||
override fun repositories(): List<String> {
|
||||
return repositories.flatMap { it.repositoryUrls() }.map(URI::toString) +
|
||||
pluginContext.repositories.toList()
|
||||
}
|
||||
|
||||
override fun artifacts(): List<Artifact> {
|
||||
return (resolver.resolveDependencies(pluginContext.dependencies, true) +
|
||||
resolver.resolvePoms(pluginContext.dependencies, true))
|
||||
.sorted()
|
||||
.distinct()
|
||||
}
|
||||
|
||||
override fun filename(): String = "plugins.json"
|
||||
}
|
||||
|
||||
private class PluginResult : PluginResolutionResult {
|
||||
val found = mutableSetOf<PluginResolution>()
|
||||
|
||||
override fun notFound(sourceDescription: String?, notFoundMessage: String?) {}
|
||||
|
||||
override fun notFound(
|
||||
sourceDescription: String?,
|
||||
notFoundMessage: String?,
|
||||
notFoundDetail: String?
|
||||
) {
|
||||
}
|
||||
|
||||
override fun isFound(): Boolean = true
|
||||
|
||||
override fun found(sourceDescription: String, pluginResolution: PluginResolution) {
|
||||
found.add(pluginResolution)
|
||||
}
|
||||
}
|
||||
|
||||
private class PluginContext : PluginResolveContext {
|
||||
val dependencies = mutableSetOf<ExternalModuleDependency>()
|
||||
val repositories = mutableSetOf<String>()
|
||||
|
||||
override fun add(plugin: PluginImplementation<*>) {
|
||||
println("add: $plugin")
|
||||
}
|
||||
|
||||
override fun addFromDifferentLoader(plugin: PluginImplementation<*>) {
|
||||
println("addFromDifferentLoader: $plugin")
|
||||
}
|
||||
|
||||
override fun addLegacy(pluginId: PluginId, m2RepoUrl: String, dependencyNotation: Any) {
|
||||
repositories.add(m2RepoUrl)
|
||||
}
|
||||
|
||||
override fun addLegacy(pluginId: PluginId, dependencyNotation: Any) {
|
||||
if (dependencyNotation is ExternalModuleDependency) {
|
||||
dependencies.add(dependencyNotation)
|
||||
}
|
||||
}
|
||||
}
|
||||
41
plugin/src/main/kotlin/org/nixos/gradle2nix/NixProjectEnv.kt
Normal file
41
plugin/src/main/kotlin/org/nixos/gradle2nix/NixProjectEnv.kt
Normal file
@@ -0,0 +1,41 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.kotlin.dsl.setProperty
|
||||
import java.net.URI
|
||||
|
||||
open class NixProjectEnv : NixEnv() {
|
||||
@Input @Optional
|
||||
val configurations = project.objects.setProperty<String>()
|
||||
|
||||
private val resolveConfigurations by lazy {
|
||||
val configs = configurations.get()
|
||||
if (configs.isEmpty()) {
|
||||
project.configurations.filter { it.isCanBeResolved }
|
||||
} else {
|
||||
project.configurations.filter { it.name in configs }
|
||||
}
|
||||
}
|
||||
|
||||
private val resolver by lazy {
|
||||
Resolver(project.configurations,
|
||||
project.dependencies,
|
||||
logger
|
||||
)
|
||||
}
|
||||
|
||||
override fun environment(): String = "project"
|
||||
|
||||
override fun repositories(): List<String> =
|
||||
project.repositories.flatMap { it.repositoryUrls() }.map(URI::toString)
|
||||
|
||||
override fun artifacts(): List<Artifact> {
|
||||
return resolveConfigurations
|
||||
.flatMap { resolver.resolveDependencies(it) + resolver.resolvePoms(it) }
|
||||
.sorted()
|
||||
.distinct()
|
||||
}
|
||||
|
||||
override fun filename(): String = "project.json"
|
||||
}
|
||||
166
plugin/src/main/kotlin/org/nixos/gradle2nix/Resolver.kt
Normal file
166
plugin/src/main/kotlin/org/nixos/gradle2nix/Resolver.kt
Normal file
@@ -0,0 +1,166 @@
|
||||
package org.nixos.gradle2nix
|
||||
|
||||
import okio.ByteString
|
||||
import okio.HashingSource
|
||||
import okio.blackholeSink
|
||||
import okio.buffer
|
||||
import okio.source
|
||||
import org.apache.maven.model.Parent
|
||||
import org.apache.maven.model.Repository
|
||||
import org.apache.maven.model.building.DefaultModelBuilderFactory
|
||||
import org.apache.maven.model.building.DefaultModelBuildingRequest
|
||||
import org.apache.maven.model.building.ModelBuildingRequest
|
||||
import org.apache.maven.model.building.ModelSource2
|
||||
import org.apache.maven.model.resolution.ModelResolver
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.ConfigurationContainer
|
||||
import org.gradle.api.artifacts.Dependency
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||
import org.gradle.api.artifacts.result.ResolvedArtifactResult
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.gradle.maven.MavenModule
|
||||
import org.gradle.maven.MavenPomArtifact
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.net.URI
|
||||
|
||||
internal class Resolver(
|
||||
private val configurations: ConfigurationContainer,
|
||||
private val dependencies: DependencyHandler,
|
||||
private val logger: Logger
|
||||
) {
|
||||
private val mavenPomResolver = MavenPomResolver(configurations, dependencies)
|
||||
|
||||
fun resolveDependencies(configuration: Configuration): Set<Artifact> {
|
||||
if (!configuration.isCanBeResolved) {
|
||||
logger.warn("Cannot resolve configuration ${configuration.name}; ignoring.")
|
||||
return emptySet()
|
||||
}
|
||||
return configuration.resolvedConfiguration.resolvedArtifacts.mapTo(sortedSetOf()) {
|
||||
with (it) {
|
||||
Artifact(
|
||||
groupId = moduleVersion.id.group,
|
||||
artifactId = moduleVersion.id.name,
|
||||
version = moduleVersion.id.version,
|
||||
classifier = classifier ?: "",
|
||||
extension = extension,
|
||||
sha256 = sha256(file)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun resolveDependencies(
|
||||
dependencies: Collection<Dependency>,
|
||||
includeTransitive: Boolean = false
|
||||
): Set<Artifact> {
|
||||
val configuration = configurations.detachedConfiguration(*(dependencies.toTypedArray()))
|
||||
configuration.isTransitive = includeTransitive
|
||||
return resolveDependencies(configuration)
|
||||
}
|
||||
|
||||
fun resolvePoms(configuration: Configuration): Set<Artifact> {
|
||||
return dependencies.createArtifactResolutionQuery()
|
||||
.forComponents(configuration.incoming.resolutionResult.allComponents.map { it.id })
|
||||
.withArtifacts(MavenModule::class.java, MavenPomArtifact::class.java)
|
||||
.execute()
|
||||
.resolvedComponents.asSequence()
|
||||
.flatMap { component ->
|
||||
val id = component.id
|
||||
if (id !is ModuleComponentIdentifier) {
|
||||
emptySequence()
|
||||
} else {
|
||||
component.getArtifacts(MavenPomArtifact::class.java).asSequence()
|
||||
.filterIsInstance<ResolvedArtifactResult>()
|
||||
.map { id to it }
|
||||
}
|
||||
}
|
||||
.flatMapTo(sortedSetOf()) { (id, artifact) ->
|
||||
sequenceOf(Artifact(
|
||||
groupId = id.group,
|
||||
artifactId = id.module,
|
||||
version = id.version,
|
||||
classifier = "",
|
||||
extension = artifact.file.extension,
|
||||
sha256 = sha256(artifact.file)
|
||||
)) + mavenPomResolver.resolve(artifact.file).asSequence()
|
||||
}
|
||||
}
|
||||
|
||||
fun resolvePoms(
|
||||
dependencies: Collection<Dependency>,
|
||||
includeTransitive: Boolean = false
|
||||
): Set<Artifact> {
|
||||
val configuration = configurations.detachedConfiguration(*(dependencies.toTypedArray()))
|
||||
configuration.isTransitive = includeTransitive
|
||||
return resolvePoms(configuration)
|
||||
}
|
||||
}
|
||||
|
||||
private class MavenPomResolver(
|
||||
private val configurations: ConfigurationContainer,
|
||||
private val dependencies: DependencyHandler
|
||||
) : ModelResolver {
|
||||
private val modelBuilder = DefaultModelBuilderFactory().newInstance()
|
||||
private val resolvedDependencies = mutableSetOf<Artifact>()
|
||||
|
||||
@Synchronized
|
||||
fun resolve(pom: File): Set<Artifact> {
|
||||
resolvedDependencies.clear()
|
||||
modelBuilder.build(
|
||||
DefaultModelBuildingRequest()
|
||||
.setModelResolver(this)
|
||||
.setPomFile(pom)
|
||||
.setSystemProperties(System.getProperties())
|
||||
.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL)
|
||||
).effectiveModel
|
||||
return resolvedDependencies.toSet()
|
||||
}
|
||||
|
||||
override fun newCopy() = this
|
||||
|
||||
override fun resolveModel(
|
||||
groupId: String,
|
||||
artifactId: String,
|
||||
version: String
|
||||
): ModelSource2 {
|
||||
val file = configurations
|
||||
.detachedConfiguration(dependencies.create("$groupId:$artifactId:$version@pom"))
|
||||
.singleFile
|
||||
resolvedDependencies.add(Artifact(
|
||||
groupId = groupId,
|
||||
artifactId = artifactId,
|
||||
version = version,
|
||||
classifier = "",
|
||||
extension = file.extension,
|
||||
sha256 = sha256(file)
|
||||
))
|
||||
|
||||
return object : ModelSource2 {
|
||||
override fun getLocation(): String = file.absolutePath
|
||||
override fun getLocationURI(): URI = file.absoluteFile.toURI()
|
||||
override fun getRelatedSource(relPath: String?): ModelSource2? = null
|
||||
override fun getInputStream(): InputStream = file.inputStream()
|
||||
}
|
||||
}
|
||||
|
||||
override fun resolveModel(parent: Parent): ModelSource2 =
|
||||
resolveModel(parent.groupId, parent.artifactId, parent.version)
|
||||
|
||||
override fun resolveModel(dependency: org.apache.maven.model.Dependency): ModelSource2 =
|
||||
resolveModel(dependency.groupId, dependency.artifactId, dependency.version)
|
||||
|
||||
override fun addRepository(repository: Repository) {}
|
||||
|
||||
override fun addRepository(repository: Repository, replace: Boolean) {}
|
||||
}
|
||||
|
||||
fun sha256(file: File): String {
|
||||
val hashSource = HashingSource.sha256(file.source())
|
||||
val hash: ByteString = hashSource.buffer().use { source ->
|
||||
source.readAll(blackholeSink())
|
||||
hashSource.hash
|
||||
}
|
||||
return hash.base64()
|
||||
}
|
||||
51
plugin/src/main/resources/gradle-env.nix
Normal file
51
plugin/src/main/resources/gradle-env.nix
Normal file
@@ -0,0 +1,51 @@
|
||||
# This file is generated by gradle2nix.
|
||||
|
||||
{ stdenvNoCC, lib, buildEnv, fetchurl }:
|
||||
|
||||
{ name, repositories, dependencies }:
|
||||
|
||||
let
|
||||
mkPath = artifact: with artifact; lib.concatStringsSep "/" [
|
||||
(lib.replaceChars ["."] ["/"] artifact.groupId)
|
||||
artifact.artifactId
|
||||
artifact.version
|
||||
];
|
||||
|
||||
mkFilename = artifact: with artifact;
|
||||
"${artifactId}-${version}${lib.optionalString (classifier != "") "-${classifier}"}.${extension}";
|
||||
|
||||
mkArtifactUrl = base: artifact:
|
||||
"${lib.removeSuffix "/" base}/${mkPath artifact}/${mkFilename artifact}";
|
||||
|
||||
fetchArtifact = artifact:
|
||||
let
|
||||
artifactPath = mkPath artifact;
|
||||
artifactName = mkFilename artifact;
|
||||
in stdenvNoCC.mkDerivation rec {
|
||||
name = with artifact; lib.concatStrings [
|
||||
(lib.replaceChars ["."] ["_"] groupId) "-"
|
||||
(lib.replaceChars ["."] ["_"] artifactId) "-"
|
||||
version
|
||||
(lib.optionalString (classifier != "") "-${classifier}")
|
||||
"-" type
|
||||
];
|
||||
|
||||
src = fetchurl {
|
||||
name = mkFilename artifact;
|
||||
urls = map (url: mkArtifactUrl url artifact) repositories;
|
||||
inherit (artifact) sha256;
|
||||
};
|
||||
|
||||
phases = "installPhase fixupPhase";
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/${artifactPath}
|
||||
ln -s ${src} $out/${artifactPath}/${artifactName}
|
||||
'';
|
||||
};
|
||||
|
||||
in
|
||||
buildEnv {
|
||||
inherit name;
|
||||
paths = map fetchArtifact dependencies;
|
||||
}
|
||||
Reference in New Issue
Block a user