Use Gradle Tooling API

This commit is contained in:
Tad Fisher
2019-05-28 17:19:32 -07:00
parent dc9d1bfda6
commit 13a84d7518
40 changed files with 921 additions and 633 deletions

12
README.org Normal file
View File

@@ -0,0 +1,12 @@
* gradle2nix
Application and Gradle plugin to generate Nix build scripts for Gradle-based
projects.
** Installation
** Usage
** License

View File

@@ -14,9 +14,17 @@ repositories {
} }
dependencies { dependencies {
implementation(project(":model"))
implementation(kotlin("stdlib-jdk8")) implementation(kotlin("stdlib-jdk8"))
implementation(kotlin("reflect"))
implementation("org.gradle:gradle-tooling-api:${gradle.gradleVersion}") implementation("org.gradle:gradle-tooling-api:${gradle.gradleVersion}")
implementation("com.github.ajalt:clikt:2.0.0") implementation("com.github.ajalt:clikt:2.0.0")
implementation("org.slf4j:slf4j-api:1.7.26")
runtimeOnly("org.slf4j:slf4j-simple:1.7.26")
implementation("com.squareup.moshi:moshi:1.8.0")
implementation("com.squareup.moshi:moshi-adapters:1.8.0")
implementation("com.squareup.moshi:moshi-kotlin:1.8.0")
implementation("com.squareup.okio:okio:2.2.2")
} }
application { application {

View File

@@ -0,0 +1,16 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.ajalt:clikt:2.0.0
com.squareup.moshi:moshi-adapters:1.8.0
com.squareup.moshi:moshi-kotlin:1.8.0
com.squareup.moshi:moshi:1.8.0
com.squareup.okio:okio:2.2.2
org.gradle:gradle-tooling-api:5.4.1
org.jetbrains.kotlin:kotlin-reflect:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.31
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21
org.jetbrains.kotlin:kotlin-stdlib:1.3.31
org.jetbrains:annotations:13.0
org.slf4j:slf4j-api:1.7.26

View File

@@ -0,0 +1,17 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.ajalt:clikt:2.0.0
com.squareup.moshi:moshi-adapters:1.8.0
com.squareup.moshi:moshi-kotlin:1.8.0
com.squareup.moshi:moshi:1.8.0
com.squareup.okio:okio:2.2.2
org.gradle:gradle-tooling-api:5.4.1
org.jetbrains.kotlin:kotlin-reflect:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.31
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21
org.jetbrains.kotlin:kotlin-stdlib:1.3.31
org.jetbrains:annotations:13.0
org.slf4j:slf4j-api:1.7.26
org.slf4j:slf4j-simple:1.7.26

View File

@@ -0,0 +1,16 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.ajalt:clikt:2.0.0
com.squareup.moshi:moshi-adapters:1.8.0
com.squareup.moshi:moshi-kotlin:1.8.0
com.squareup.moshi:moshi:1.8.0
com.squareup.okio:okio:2.2.2
org.gradle:gradle-tooling-api:5.4.1
org.jetbrains.kotlin:kotlin-reflect:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.31
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21
org.jetbrains.kotlin:kotlin-stdlib:1.3.31
org.jetbrains:annotations:13.0
org.slf4j:slf4j-api:1.7.26

View File

@@ -0,0 +1,17 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.ajalt:clikt:2.0.0
com.squareup.moshi:moshi-adapters:1.8.0
com.squareup.moshi:moshi-kotlin:1.8.0
com.squareup.moshi:moshi:1.8.0
com.squareup.okio:okio:2.2.2
org.gradle:gradle-tooling-api:5.4.1
org.jetbrains.kotlin:kotlin-reflect:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.31
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21
org.jetbrains.kotlin:kotlin-stdlib:1.3.31
org.jetbrains:annotations:13.0
org.slf4j:slf4j-api:1.7.26
org.slf4j:slf4j-simple:1.7.26

View File

@@ -1,45 +1,40 @@
package org.nixos.gradle2nix package org.nixos.gradle2nix
import org.gradle.tooling.GradleConnector import org.gradle.tooling.GradleConnector
import java.io.File import org.gradle.tooling.ProjectConnection
class GradleRunner( private val initScript: String = System.getProperty("org.nixos.gradle2nix.initScript")
private val projectDir: File,
private val useWrapper: Boolean,
private val gradleVersion: String?,
private val configurations: List<String>
) {
companion object {
val initScript: String = System.getProperty("org.nixos.gradle2nix.initScript")
}
fun runGradle() { fun connect(config: Config): ProjectConnection =
GradleConnector.newConnector() GradleConnector.newConnector()
.apply { .apply {
if (useWrapper) { if (config.wrapper) {
useBuildDistribution() useBuildDistribution()
} else if (gradleVersion != null) { } else if (config.gradleVersion != null) {
useGradleVersion(gradleVersion) useGradleVersion(config.gradleVersion)
} }
} }
.forProjectDirectory(projectDir) .forProjectDirectory(config.projectDir)
.connect() .connect()
.use { connection ->
connection.newBuild() fun ProjectConnection.getBuildModel(config: Config, path: String): DefaultBuild {
.withArguments("--init-script", initScript) val arguments = mutableListOf(
.apply { "--init-script=$initScript",
if (configurations.isNotEmpty()) { "-Dorg.nixos.gradle2nix.configurations='${config.configurations.joinToString(",")}'"
withArguments(
"-Dorg.nixos.gradle2nix.configurations=${configurations.joinToString(
","
)}"
) )
if (path.isNotEmpty()) {
arguments += "--project-dir=$path"
}
return model(Build::class.java)
.withArguments(arguments)
.apply {
if (config.verbose) {
setStandardOutput(System.out)
setStandardError(System.err)
} }
} }
.forTasks("nixGradleEnv") .get()
.setStandardOutput(System.out) .let { DefaultBuild(it) }
.setStandardError(System.err)
.run()
}
}
} }

View File

@@ -0,0 +1,19 @@
package org.nixos.gradle2nix
import java.io.PrintStream
class Logger(
val out: PrintStream = System.err,
val verbose: Boolean) {
val log: (String) -> Unit = { if (verbose) out.println(it) }
val warn: (String) -> Unit = { out.println("Warning: $it")}
val error: (String) -> Unit = {
out.println("Error: $it")
System.exit(1)
}
operator fun component1() = log
operator fun component2() = warn
operator fun component3() = error
}

View File

@@ -1,25 +1,103 @@
package org.nixos.gradle2nix package org.nixos.gradle2nix
import com.github.ajalt.clikt.completion.CompletionCandidates
import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.ProcessedArgument
import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.arguments.defaultLazy import com.github.ajalt.clikt.parameters.arguments.convert
import com.github.ajalt.clikt.parameters.options.flag import com.github.ajalt.clikt.parameters.arguments.default
import com.github.ajalt.clikt.parameters.options.multiple import com.github.ajalt.clikt.parameters.options.*
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.types.file import com.github.ajalt.clikt.parameters.types.file
import com.squareup.moshi.Moshi
import okio.buffer
import okio.sink
import java.io.File import java.io.File
data class Config(
val wrapper: Boolean,
val gradleVersion: String?,
val configurations: List<String>,
val projectDir: File,
val includes: List<File>,
val buildSrc: Boolean,
val verbose: Boolean
) {
val allProjects = listOf(projectDir) + includes
}
class Main : CliktCommand() { class Main : CliktCommand() {
val wrapper: Boolean by option(help = "Use the project's gradle wrapper for building").flag() val wrapper: Boolean by option("--gradle-wrapper", "-w",
val gradleVersion: String? by option(help = "Use a specific Gradle version") help = "Use the project's gradle wrapper for building")
.flag()
val gradleVersion: String? by option("--gradle-version", "-g",
help = "Use a specific Gradle version")
val configurations: List<String> by option(help = "Project configuration(s)").multiple() val configurations: List<String> by option(help = "Project configuration(s)").multiple()
val projectDir: File by argument(help = "Path to the project root") val projectDir: File by argument(help = "Path to the project root")
.projectDir()
.default(File("."))
val outputDir: File by option("--out", "-o",
help = "Path to write Nix environment files")
.file(fileOkay = false, folderOkay = true)
.default(File("."))
val includes: List<File> by option("--include", "-i",
help = "Path to included build(s)",
metavar = "DIR")
.file(exists = true, fileOkay = false, folderOkay = true, readable = true) .file(exists = true, fileOkay = false, folderOkay = true, readable = true)
.defaultLazy { File(".") } .multiple()
.validate { files ->
val failures = files.filterNot { it.isProjectRoot() }
if (failures.isNotEmpty()) {
val message = failures.joinToString("\n ")
fail("Included builds are not Gradle projects:\n$message\n" +
"Gradle projects must contain a settings.gradle or settings.gradle.kts script.")
}
}
val buildSrc: Boolean by option("--enableBuildSrc", help = "Include buildSrc project")
.flag("--disableBuildSrc", default = true)
val verbose: Boolean by option("--verbose", "-v", help = "Enable verbose logging")
.flag(default = false)
override fun run() { override fun run() {
GradleRunner(projectDir, wrapper, gradleVersion, configurations).runGradle() val config = Config(wrapper, gradleVersion, configurations, projectDir, includes, buildSrc, verbose)
val (log, warn, error) = Logger(verbose = config.verbose)
val json by lazy { Moshi.Builder().build().adapter(DefaultBuild::class.java).indent(" ") }
val out by lazy { outputDir.also { it.mkdirs() }}
val paths = resolveProjects(config).map { p ->
p.toRelativeString(config.projectDir)
}
connect(config).use { connection ->
for (project in paths) {
log("Resolving project model: ${project.takeIf { it.isNotEmpty() } ?: "root project"}")
val build = connection.getBuildModel(config, project)
val filename = build.rootProject.name + ".json"
val file = out.resolve(filename)
file.sink().buffer().use { sink -> json.toJson(sink, build) }
log(" --> $file")
}
}
}
}
fun ProcessedArgument<String, String>.projectDir(): ProcessedArgument<File, File> {
return convert(completionCandidates = CompletionCandidates.Path) {
File(it).also { file ->
if (!file.exists()) fail("Directory \"$file\" does not exist.")
if (file.isFile) fail("Directory \"$file\" is a file.")
if (!file.canRead()) fail("Directory \"$file\" is not readable.")
if (!file.isProjectRoot()) fail("Directory \"$file\" is not a Gradle project.")
}
} }
} }
fun main(args: Array<String>) = Main().main(args) fun main(args: Array<String>) = Main().main(args)

View File

@@ -0,0 +1,17 @@
package org.nixos.gradle2nix
import java.io.File
fun resolveProjects(config: Config) = config.allProjects.run {
if (config.buildSrc) {
flatMap { listOfNotNull(it, it.findBuildSrc()) }
} else {
this
}
}
fun File.findBuildSrc(): File? =
resolve("buildSrc").takeIf { it.isDirectory }
fun File.isProjectRoot(): Boolean =
isDirectory && (resolve("settings.gradle").isFile || resolve("settings.gradle.kts").isFile)

View File

@@ -5,7 +5,36 @@ plugins {
kotlin("jvm") version embeddedKotlinVersion apply false kotlin("jvm") version embeddedKotlinVersion apply false
kotlin("kapt") version embeddedKotlinVersion apply false kotlin("kapt") version embeddedKotlinVersion apply false
id("com.github.johnrengelman.shadow") version "5.0.0" apply false id("com.github.johnrengelman.shadow") version "5.0.0" apply false
id("org.ysb33r.gradletest") version "2.0-rc.4" apply false id("org.ajoberstar.stutter") version "0.5.0" apply false
}
allprojects {
plugins.withType<JavaBasePlugin> {
this@allprojects.withConvention(JavaPluginConvention::class) {
sourceSets.all {
configurations {
named(compileClasspathConfigurationName) {
resolutionStrategy.activateDependencyLocking()
}
named(runtimeClasspathConfigurationName) {
resolutionStrategy.activateDependencyLocking()
}
}
}
tasks.register("lock") {
doFirst {
assert(gradle.startParameter.isWriteDependencyLocks)
}
doLast {
sourceSets.all {
configurations[compileClasspathConfigurationName].resolve()
configurations[runtimeClasspathConfigurationName].resolve()
}
}
}
}
}
} }
tasks { tasks {

15
model/build.gradle.kts Normal file
View File

@@ -0,0 +1,15 @@
plugins {
`embedded-kotlin`
kotlin("kapt")
}
repositories {
mavenCentral()
maven { url = uri("https://repo.gradle.org/gradle/libs-releases") }
}
dependencies {
compileOnly("com.squareup.moshi:moshi:1.8.0")
compileOnly("com.squareup.okio:okio:2.2.2")
kapt("com.squareup.moshi:moshi-kotlin-codegen:1.8.0")
}

View File

@@ -0,0 +1,10 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.squareup.moshi:moshi:1.8.0
com.squareup.okio:okio:2.2.2
org.jetbrains.kotlin:kotlin-reflect:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21
org.jetbrains.kotlin:kotlin-stdlib:1.3.21
org.jetbrains:annotations:13.0

View File

@@ -0,0 +1,3 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.

View File

@@ -0,0 +1,8 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
org.jetbrains.kotlin:kotlin-reflect:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21
org.jetbrains.kotlin:kotlin-stdlib:1.3.21
org.jetbrains:annotations:13.0

View File

@@ -0,0 +1,8 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
org.jetbrains.kotlin:kotlin-reflect:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21
org.jetbrains.kotlin:kotlin-stdlib:1.3.21
org.jetbrains:annotations:13.0

View File

@@ -0,0 +1,117 @@
package org.nixos.gradle2nix
import com.squareup.moshi.JsonClass
import java.io.Serializable
@JsonClass(generateAdapter = true)
data class DefaultBuild(
override val gradle: DefaultGradle,
override val pluginDependencies: DefaultDependencies,
override val rootProject: DefaultProject,
override val includedBuilds: List<DefaultIncludedBuild>
) : Build, Serializable {
constructor(model: Build) : this(
DefaultGradle(model.gradle),
DefaultDependencies(model.pluginDependencies),
DefaultProject(model.rootProject),
model.includedBuilds.map { DefaultIncludedBuild(it) }
)
}
@JsonClass(generateAdapter = true)
data class DefaultIncludedBuild(
override val name: String,
override val projectDir: String
) : IncludedBuild, Serializable {
constructor(model: IncludedBuild) : this(
model.name,
model.projectDir
)
}
@JsonClass(generateAdapter = true)
data class DefaultGradle(
override val version: String,
override val type: String,
override val url: String,
override val sha256: String,
override val nativeVersion: String
) : Gradle, Serializable {
constructor(model: Gradle) : this(
model.version,
model.type,
model.url,
model.sha256,
model.nativeVersion
)
}
@JsonClass(generateAdapter = true)
data class DefaultProject(
override val name: String,
override val path: String,
override val projectDir: String,
override val buildscriptDependencies: DefaultDependencies,
override val projectDependencies: DefaultDependencies,
override val children: List<DefaultProject>
) : Project, Serializable {
constructor(model: Project) : this(
model.name,
model.path,
model.projectDir,
DefaultDependencies(model.buildscriptDependencies),
DefaultDependencies(model.projectDependencies),
model.children.map { DefaultProject(it) }
)
}
@JsonClass(generateAdapter = true)
data class DefaultDependencies(
override val repositories: DefaultRepositories,
override val artifacts: List<DefaultArtifact>
) : Dependencies, Serializable {
constructor(model: Dependencies) : this(
DefaultRepositories(model.repositories),
model.artifacts.map { DefaultArtifact(it) }
)
}
@JsonClass(generateAdapter = true)
data class DefaultRepositories(
override val maven: List<DefaultMaven>
) : Repositories, Serializable {
constructor(model: Repositories) : this(
model.maven.map { DefaultMaven(it) }
)
}
@JsonClass(generateAdapter = true)
data class DefaultMaven(
override val urls: List<String>
) : Maven, Serializable {
constructor(model: Maven) : this(
model.urls.toList()
)
}
@JsonClass(generateAdapter = true)
data class DefaultArtifact(
override val groupId: String,
override val artifactId: String,
override val version: String,
override val classifier: String,
override val extension: String,
override val sha256: String
) : Artifact, Comparable<DefaultArtifact>, Serializable {
constructor(model: Artifact) : this(
model.groupId,
model.artifactId,
model.version,
model.classifier,
model.extension,
model.sha256
)
override fun toString() = "$groupId:$artifactId:$version:$classifier:$extension"
override fun compareTo(other: DefaultArtifact): Int = toString().compareTo(other.toString())
}

View File

@@ -0,0 +1,52 @@
package org.nixos.gradle2nix
interface Build {
val gradle: Gradle
val pluginDependencies: Dependencies
val rootProject: Project
val includedBuilds: List<IncludedBuild>
}
interface IncludedBuild {
val name: String
val projectDir: String
}
interface Gradle {
val version: String
val type: String
val url: String
val sha256: String
val nativeVersion: String
}
interface Project {
val name: String
val path: String
val projectDir: String
val buildscriptDependencies: Dependencies
val projectDependencies: Dependencies
val children: List<Project>
}
interface Dependencies {
val repositories: Repositories
val artifacts: List<Artifact>
}
interface Repositories {
val maven: List<Maven>
}
interface Maven {
val urls: List<String>
}
interface Artifact {
val groupId: String
val artifactId: String
val version: String
val classifier: String
val extension: String
val sha256: String
}

View File

@@ -0,0 +1,12 @@
# DO NOT MODIFY: Generated by Stutter plugin.
4.5.1
4.6
4.7
4.8.1
4.9
4.10.3
5.0
5.1.1
5.2.1
5.3.1
5.4.1

View File

@@ -1,26 +1,43 @@
buildscript {
configurations.classpath {
resolutionStrategy.activateDependencyLocking()
}
}
plugins { plugins {
`java-gradle-plugin`
`kotlin-dsl` `kotlin-dsl`
`maven-publish`
kotlin("kapt")
id("com.github.johnrengelman.shadow") id("com.github.johnrengelman.shadow")
id("org.ysb33r.gradletest") id("org.ajoberstar.stutter")
}
apply {
plugin("kotlin")
} }
group = "org.nixos" group = "org.nixos"
version = "1.0.0-SNAPSHOT" version = "1.0.0-SNAPSHOT"
repositories { repositories {
jcenter() mavenCentral()
maven { url = uri("https://repo.gradle.org/gradle/libs-releases") }
}
dependencyLocking {
lockAllConfigurations()
} }
dependencies { dependencies {
implementation(kotlin("stdlib-jdk8")) implementation(project(":model"))
implementation("org.apache.maven:maven-model:3.6.1") compileOnly("org.gradle:gradle-tooling-api:${gradle.gradleVersion}")
implementation("org.apache.maven:maven-model-builder:3.6.1") implementation("org.apache.maven:maven-model:latest.release")
implementation("com.squareup.okio:okio:2.2.2") implementation("org.apache.maven:maven-model-builder:latest.release")
implementation("com.squareup.moshi:moshi:1.8.0") implementation("com.squareup.okio:okio:latest.release")
kapt("com.squareup.moshi:moshi-kotlin-codegen:1.8.0")
compatTestImplementation(embeddedKotlin("stdlib-jdk8"))
compatTestImplementation(embeddedKotlin("test-junit5"))
compatTestImplementation("org.junit.jupiter:junit-jupiter-api:5.4+")
compatTestRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.4+")
compatTestImplementation(gradleTestKit())
compatTestImplementation(project(":model"))
} }
gradlePlugin { gradlePlugin {
@@ -28,37 +45,46 @@ gradlePlugin {
register("gradle2nix") { register("gradle2nix") {
id = "org.nixos.gradle2nix" id = "org.nixos.gradle2nix"
displayName = "gradle2nix" displayName = "gradle2nix"
description = "Create Nix configurations suitable for reproducible packaging" description = "Expose Gradle tooling model for the gradle2nix tool"
implementationClass = "org.nixos.gradle2nix.Gradle2NixPlugin" implementationClass = "org.nixos.gradle2nix.Gradle2NixPlugin"
} }
} }
} }
kotlinDslPluginOptions {
experimentalWarning.set(false)
}
stutter {
java(8) {
compatibleRange("4.5")
}
java(11) {
compatibleRange("5.0")
}
}
tasks { tasks {
gradleTest { withType<Test> {
versions("5.0", "5.1.1", "5.2.1", "5.3.1", "5.4.1") useJUnitPlatform()
kotlinDsl = true
} }
gradleTestGenerator { // gradleTestGenerator {
dependsOn(shadowJar) // dependsOn(shadowJar)
doLast { // doLast {
file(gradleTest.get().initScript).bufferedWriter().use { out -> // file(gradleTest.get().initScript).bufferedWriter().use { out ->
out.appendln(""" // out.append("""
initscript { // initscript {
dependencies { // dependencies {
classpath fileTree('file:${buildDir.absolutePath}/libs'.toURI()) { // classpath fileTree('file:${buildDir.absolutePath}/libs'.toURI()) {
include '*.jar' // include '*.jar'
} // }
""".trimIndent()) // }
// }
out.appendln(""" //
} // apply plugin: org.nixos.gradle2nix.Gradle2NixPlugin
} // """.trimIndent())
// }
apply plugin: org.nixos.gradle2nix.Gradle2NixPlugin // }
""".trimIndent()) // }
}
}
}
} }

View File

@@ -0,0 +1,25 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:1.2.6
org.gradle.kotlin:plugins:1.2.6
org.jetbrains.intellij.deps:trove4j:1.0.20181211
org.jetbrains.kotlin:kotlin-android-extensions:1.3.21
org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.3.21
org.jetbrains.kotlin:kotlin-build-common:1.3.21
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.3.21
org.jetbrains.kotlin:kotlin-compiler-runner:1.3.21
org.jetbrains.kotlin:kotlin-daemon-client:1.3.21
org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.3.21
org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.3.21
org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.21
org.jetbrains.kotlin:kotlin-native-utils:1.3.21
org.jetbrains.kotlin:kotlin-reflect:1.3.21
org.jetbrains.kotlin:kotlin-sam-with-receiver:1.3.21
org.jetbrains.kotlin:kotlin-script-runtime:1.3.21
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21
org.jetbrains.kotlin:kotlin-stdlib:1.3.21
org.jetbrains:annotations:13.0

View File

@@ -0,0 +1,16 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
org.apiguardian:apiguardian-api:1.0.0
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21
org.jetbrains.kotlin:kotlin-stdlib:1.3.21
org.jetbrains.kotlin:kotlin-test-annotations-common:1.3.21
org.jetbrains.kotlin:kotlin-test-common:1.3.21
org.jetbrains.kotlin:kotlin-test-junit5:1.3.21
org.jetbrains.kotlin:kotlin-test:1.3.21
org.jetbrains:annotations:13.0
org.junit.jupiter:junit-jupiter-api:5.4.2
org.junit.platform:junit-platform-commons:1.4.2
org.opentest4j:opentest4j:1.1.1

View File

@@ -0,0 +1,18 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
org.apiguardian:apiguardian-api:1.0.0
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21
org.jetbrains.kotlin:kotlin-stdlib:1.3.21
org.jetbrains.kotlin:kotlin-test-annotations-common:1.3.21
org.jetbrains.kotlin:kotlin-test-common:1.3.21
org.jetbrains.kotlin:kotlin-test-junit5:1.3.21
org.jetbrains.kotlin:kotlin-test:1.3.21
org.jetbrains:annotations:13.0
org.junit.jupiter:junit-jupiter-api:5.4.2
org.junit.jupiter:junit-jupiter-engine:5.4.2
org.junit.platform:junit-platform-commons:1.4.2
org.junit.platform:junit-platform-engine:1.4.2
org.opentest4j:opentest4j:1.1.1

View File

@@ -0,0 +1,18 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.squareup.okio:okio:2.2.2
org.apache.commons:commons-lang3:3.8.1
org.apache.maven:maven-artifact:3.6.1
org.apache.maven:maven-builder-support:3.6.1
org.apache.maven:maven-model-builder:3.6.1
org.apache.maven:maven-model:3.6.1
org.codehaus.plexus:plexus-component-annotations:1.7.1
org.codehaus.plexus:plexus-interpolation:1.25
org.codehaus.plexus:plexus-utils:3.2.0
org.gradle:gradle-tooling-api:5.4.1
org.jetbrains.kotlin:kotlin-reflect:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21
org.jetbrains.kotlin:kotlin-stdlib:1.3.21
org.jetbrains:annotations:13.0

View File

@@ -0,0 +1,15 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.squareup.okio:okio:2.2.2
org.apache.commons:commons-lang3:3.8.1
org.apache.maven:maven-artifact:3.6.1
org.apache.maven:maven-builder-support:3.6.1
org.apache.maven:maven-model-builder:3.6.1
org.apache.maven:maven-model:3.6.1
org.codehaus.plexus:plexus-component-annotations:1.7.1
org.codehaus.plexus:plexus-interpolation:1.25
org.codehaus.plexus:plexus-utils:3.2.0
org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60
org.jetbrains.kotlin:kotlin-stdlib:1.2.60
org.jetbrains:annotations:13.0

View File

@@ -0,0 +1,17 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.squareup.okio:okio:2.2.2
org.apache.commons:commons-lang3:3.8.1
org.apache.maven:maven-artifact:3.6.1
org.apache.maven:maven-builder-support:3.6.1
org.apache.maven:maven-model-builder:3.6.1
org.apache.maven:maven-model:3.6.1
org.codehaus.plexus:plexus-component-annotations:1.7.1
org.codehaus.plexus:plexus-interpolation:1.25
org.codehaus.plexus:plexus-utils:3.2.0
org.jetbrains.kotlin:kotlin-reflect:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21
org.jetbrains.kotlin:kotlin-stdlib:1.3.21
org.jetbrains:annotations:13.0

View File

@@ -0,0 +1,17 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.squareup.okio:okio:2.2.2
org.apache.commons:commons-lang3:3.8.1
org.apache.maven:maven-artifact:3.6.1
org.apache.maven:maven-builder-support:3.6.1
org.apache.maven:maven-model-builder:3.6.1
org.apache.maven:maven-model:3.6.1
org.codehaus.plexus:plexus-component-annotations:1.7.1
org.codehaus.plexus:plexus-interpolation:1.25
org.codehaus.plexus:plexus-utils:3.2.0
org.jetbrains.kotlin:kotlin-reflect:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21
org.jetbrains.kotlin:kotlin-stdlib:1.3.21
org.jetbrains:annotations:13.0

View File

@@ -0,0 +1,78 @@
package org.nixos.gradle2nix
import org.gradle.internal.classpath.DefaultClassPath
import org.gradle.testkit.runner.internal.PluginUnderTestMetadataReading
import org.gradle.tooling.GradleConnector
import org.gradle.tooling.internal.consumer.DefaultModelBuilder
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import java.io.File
import kotlin.test.assertEquals
class BasicTest {
companion object {
@JvmStatic @TempDir lateinit var projectDir: File
val initScript: File by lazy {
projectDir.resolve("init.gradle").also {
it.writer().use { out ->
// val classpath = DefaultClassPath.of(PluginUnderTestMetadataReading.readImplementationClasspath())
// .asFiles.joinToString(prefix = "'", postfix = "'")
// out.appendln("""
// initscript {
// dependencies {
// classpath files($classpath)
// }
// }
//
// apply plugin: org.nixos.gradle2nix.Gradle2NixPlugin
// """.trimIndent())
out.appendln("apply plugin: org.nixos.gradle2nix.Gradle2NixPlugin")
}
}
}
}
@Test
fun `builds basic project with kotlin dsl`() {
projectDir.resolve("build.gradle.kts").writeText("""
plugins {
java
}
repositories {
jcenter()
}
dependencies {
implementation("com.squareup.okio:okio:2.2.2")
implementation("com.squareup.moshi:moshi:1.8.0")
}
""".trimIndent())
val connection = GradleConnector.newConnector()
.useGradleVersion(System.getProperty("compat.gradle.version"))
.forProjectDirectory(projectDir)
.connect()
val model = (connection.model(Build::class.java) as DefaultModelBuilder<Build>)
.withArguments(
"--init-script=$initScript",
"--stacktrace"
)
.withInjectedClassPath(DefaultClassPath.of(PluginUnderTestMetadataReading.readImplementationClasspath()))
.setStandardOutput(System.out)
.setStandardError(System.out)
.get()
assertEquals(model.gradle.version, System.getProperty("compat.gradle.version"))
with(model.rootProject.projectDependencies) {
with(repositories) {
assertEquals(1, maven.size)
assertEquals(maven[0].urls[0], "https://jcenter.bintray.com/")
}
}
}
}

View File

@@ -15,27 +15,28 @@ import org.gradle.api.artifacts.component.ModuleComponentIdentifier
import org.gradle.api.artifacts.dsl.DependencyHandler import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.api.artifacts.result.ResolvedArtifactResult import org.gradle.api.artifacts.result.ResolvedArtifactResult
import org.gradle.api.logging.Logger import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.gradle.maven.MavenModule import org.gradle.maven.MavenModule
import org.gradle.maven.MavenPomArtifact import org.gradle.maven.MavenPomArtifact
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
import java.net.URI import java.net.URI
internal class Resolver( internal class DependencyResolver(
private val configurations: ConfigurationContainer, private val configurations: ConfigurationContainer,
private val dependencies: DependencyHandler, private val dependencies: DependencyHandler,
private val logger: Logger private val logger: Logger = Logging.getLogger(DependencyResolver::class.simpleName)
) { ) {
private val mavenPomResolver = MavenPomResolver(configurations, dependencies) private val mavenPomResolver = MavenPomResolver(configurations, dependencies)
fun resolveDependencies(configuration: Configuration): Set<Artifact> { fun resolveDependencies(configuration: Configuration): Set<DefaultArtifact> {
if (!configuration.isCanBeResolved) { if (!configuration.isCanBeResolved) {
logger.warn("Cannot resolve configuration ${configuration.name}; ignoring.") logger.warn("Cannot resolve configuration ${configuration.name}; ignoring.")
return emptySet() return emptySet()
} }
return configuration.resolvedConfiguration.resolvedArtifacts.mapTo(sortedSetOf()) { return configuration.resolvedConfiguration.resolvedArtifacts.mapTo(sortedSetOf()) {
with (it) { with (it) {
Artifact( DefaultArtifact(
groupId = moduleVersion.id.group, groupId = moduleVersion.id.group,
artifactId = moduleVersion.id.name, artifactId = moduleVersion.id.name,
version = moduleVersion.id.version, version = moduleVersion.id.version,
@@ -50,13 +51,13 @@ internal class Resolver(
fun resolveDependencies( fun resolveDependencies(
dependencies: Collection<Dependency>, dependencies: Collection<Dependency>,
includeTransitive: Boolean = false includeTransitive: Boolean = false
): Set<Artifact> { ): Set<DefaultArtifact> {
val configuration = configurations.detachedConfiguration(*(dependencies.toTypedArray())) val configuration = configurations.detachedConfiguration(*(dependencies.toTypedArray()))
configuration.isTransitive = includeTransitive configuration.isTransitive = includeTransitive
return resolveDependencies(configuration) return resolveDependencies(configuration)
} }
fun resolvePoms(configuration: Configuration): Set<Artifact> { fun resolvePoms(configuration: Configuration): Set<DefaultArtifact> {
return dependencies.createArtifactResolutionQuery() return dependencies.createArtifactResolutionQuery()
.forComponents(configuration.incoming.resolutionResult.allComponents.map { it.id }) .forComponents(configuration.incoming.resolutionResult.allComponents.map { it.id })
.withArtifacts(MavenModule::class.java, MavenPomArtifact::class.java) .withArtifacts(MavenModule::class.java, MavenPomArtifact::class.java)
@@ -73,7 +74,7 @@ internal class Resolver(
} }
} }
.flatMapTo(sortedSetOf()) { (id, artifact) -> .flatMapTo(sortedSetOf()) { (id, artifact) ->
sequenceOf(Artifact( sequenceOf(DefaultArtifact(
groupId = id.group, groupId = id.group,
artifactId = id.module, artifactId = id.module,
version = id.version, version = id.version,
@@ -87,7 +88,7 @@ internal class Resolver(
fun resolvePoms( fun resolvePoms(
dependencies: Collection<Dependency>, dependencies: Collection<Dependency>,
includeTransitive: Boolean = false includeTransitive: Boolean = false
): Set<Artifact> { ): Set<DefaultArtifact> {
val configuration = configurations.detachedConfiguration(*(dependencies.toTypedArray())) val configuration = configurations.detachedConfiguration(*(dependencies.toTypedArray()))
configuration.isTransitive = includeTransitive configuration.isTransitive = includeTransitive
return resolvePoms(configuration) return resolvePoms(configuration)
@@ -99,10 +100,10 @@ private class MavenPomResolver(
private val dependencies: DependencyHandler private val dependencies: DependencyHandler
) : ModelResolver { ) : ModelResolver {
private val modelBuilder = DefaultModelBuilderFactory().newInstance() private val modelBuilder = DefaultModelBuilderFactory().newInstance()
private val resolvedDependencies = mutableSetOf<Artifact>() private val resolvedDependencies = mutableSetOf<DefaultArtifact>()
@Synchronized @Synchronized
fun resolve(pom: File): Set<Artifact> { fun resolve(pom: File): Set<DefaultArtifact> {
resolvedDependencies.clear() resolvedDependencies.clear()
modelBuilder.build( modelBuilder.build(
DefaultModelBuildingRequest() DefaultModelBuildingRequest()
@@ -124,7 +125,7 @@ private class MavenPomResolver(
val file = configurations val file = configurations
.detachedConfiguration(dependencies.create("$groupId:$artifactId:$version@pom")) .detachedConfiguration(dependencies.create("$groupId:$artifactId:$version@pom"))
.singleFile .singleFile
resolvedDependencies.add(Artifact( resolvedDependencies.add(DefaultArtifact(
groupId = groupId, groupId = groupId,
artifactId = artifactId, artifactId = artifactId,
version = version, version = version,

View File

@@ -1,21 +1,25 @@
package org.nixos.gradle2nix package org.nixos.gradle2nix
import com.squareup.moshi.Moshi
import okio.buffer import okio.buffer
import okio.source import okio.source
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.dsl.RepositoryHandler
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
import org.gradle.api.invocation.Gradle import org.gradle.api.invocation.Gradle
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.wrapper.Wrapper import org.gradle.api.tasks.wrapper.Wrapper
import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.named import org.gradle.kotlin.dsl.named
import org.gradle.kotlin.dsl.register import org.gradle.kotlin.dsl.newInstance
import org.gradle.kotlin.dsl.support.serviceOf
import org.gradle.plugin.management.PluginRequest import org.gradle.plugin.management.PluginRequest
import java.io.File import org.gradle.tooling.provider.model.ToolingModelBuilder
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import java.net.URL import java.net.URL
import java.util.* import java.util.*
import org.nixos.gradle2nix.Gradle as NixGradle
import org.nixos.gradle2nix.Project as NixProject
@Suppress("unused")
open class Gradle2NixPlugin : Plugin<Gradle> { open class Gradle2NixPlugin : Plugin<Gradle> {
override fun apply(gradle: Gradle) { override fun apply(gradle: Gradle) {
val configurationNames: List<String> = val configurationNames: List<String> =
@@ -24,52 +28,8 @@ open class Gradle2NixPlugin : Plugin<Gradle> {
val pluginRequests = collectPlugins(gradle) val pluginRequests = collectPlugins(gradle)
gradle.projectsLoaded { gradle.projectsLoaded {
val extension = rootProject.extensions.create<Gradle2NixExtension>( rootProject.serviceOf<ToolingModelBuilderRegistry>()
"gradle2nix", .register(NixToolingModelBuilder(configurationNames, pluginRequests))
rootProject,
configurationNames
)
val gradleEnv = rootProject.tasks.register("nixGradleEnv", NixGradleEnv::class) {
outputDir.set(extension.outputDir)
}
val buildSrcDir = rootProject.projectDir.resolve("buildSrc")
if (buildSrcDir.exists() && buildSrcDir.isDirectory) {
val buildSrcEnv =
rootProject.tasks.register("nixBuildSrcEnv", NixBuildSrcEnv::class) {
dir = buildSrcDir
val buildFile = buildSrcDir.listFiles().let { files ->
files.find { it.name == "build.gradle.kts" } ?:
files.find { it.name == "build.gradle" }
}
if (buildFile != null) this.buildFile = buildFile
}
gradleEnv.configure {
dependsOn(buildSrcEnv)
}
}
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)
} }
} }
@@ -84,53 +44,97 @@ open class Gradle2NixPlugin : Plugin<Gradle> {
} }
return pluginRequests return pluginRequests
} }
}
private fun resolveGradleDist( private class NixToolingModelBuilder(
gradle: Gradle, private val explicitConfigurations: List<String>,
extension: Gradle2NixExtension, private val pluginRequests: List<PluginRequest>
gradleEnv: TaskProvider<NixGradleEnv> ) : ToolingModelBuilder {
) { override fun canBuild(modelName: String): Boolean {
gradle.projectsEvaluated { return modelName == "org.nixos.gradle2nix.Build"
val gradleDist = rootProject.tasks.named("wrapper", Wrapper::class).map { }
GradleDist(
version = it.gradleVersion, override fun buildAll(modelName: String, project: Project): Build = project.run {
type = it.distributionType.name.toLowerCase(Locale.US), val plugins = buildPlugins(pluginRequests)
url = it.distributionUrl, DefaultBuild(
sha256 = it.distributionSha256Sum ?: fetchDistSha256(it.distributionUrl), gradle = buildGradle(),
pluginDependencies = plugins,
rootProject = buildProject(explicitConfigurations, plugins),
includedBuilds = includedBuilds()
)
}
}
private fun Project.buildGradle(): DefaultGradle =
with(tasks.named<Wrapper>("wrapper").get()) {
DefaultGradle(
version = gradleVersion,
type = distributionType.name.toLowerCase(Locale.US),
url = distributionUrl,
sha256 = distributionSha256Sum ?: fetchDistSha256(distributionUrl),
nativeVersion = gradle.gradleHomeDir?.resolve("lib")?.listFiles() nativeVersion = gradle.gradleHomeDir?.resolve("lib")?.listFiles()
?.firstOrNull { f -> f.name.matches(nativePlatformJarRegex) }?.let { nf -> ?.firstOrNull { f -> nativePlatformJarRegex matches f.name }?.let { nf ->
nativePlatformJarRegex.find(nf.name)?.groupValues?.get(1) nativePlatformJarRegex.find(nf.name)?.groupValues?.get(1)
} }
?: throw IllegalStateException(""" ?: throw IllegalStateException(
"""
Failed to find native-platform jar in ${gradle.gradleHomeDir}. Failed to find native-platform jar in ${gradle.gradleHomeDir}.
Ask Tad to fix this. Ask Tad to fix this.
""".trimIndent()) """.trimIndent()
)
) )
} }
val gradleDistTask =
gradle.rootProject.tasks.register("nixGradleDist", NixGradleDist::class) { private fun Project.buildPlugins(pluginRequests: List<PluginRequest>): DefaultDependencies =
this.gradleDist.set(gradleDist) with(objects.newInstance<PluginResolver>(pluginRequests)) {
outputDir.set(extension.outputDir) DefaultDependencies(repositories.repositories(), artifacts())
}
gradleEnv.configure {
dependsOn(gradleDistTask)
}
}
}
} }
internal val pluginJar by lazy { private fun Project.includedBuilds(): List<DefaultIncludedBuild> =
File(Gradle2NixPlugin::class.java.protectionDomain.codeSource.location.toURI()).absoluteFile gradle.includedBuilds.map {
DefaultIncludedBuild(it.name, it.projectDir.toRelativeString(project.projectDir))
} }
internal val moshi by lazy { Moshi.Builder().build() } private fun Project.buildProject(
explicitConfigurations: List<String>,
plugins: DefaultDependencies
): DefaultProject =
DefaultProject(
name = name,
path = path,
projectDir = projectDir.toRelativeString(rootProject.projectDir),
buildscriptDependencies = buildscriptDependencies(plugins),
projectDependencies = projectDependencies(explicitConfigurations),
children = childProjects.values.map { it.buildProject(explicitConfigurations, plugins) }
)
open class Gradle2NixExtension(project: Project, defaultConfigurations: List<String>) { private fun Project.buildscriptDependencies(plugins: DefaultDependencies): DefaultDependencies =
var outputDir: File = project.projectDir.resolve("gradle/nix") with(DependencyResolver(buildscript.configurations, buildscript.dependencies)) {
var configurations: MutableList<String> = mutableListOf<String>().apply { DefaultDependencies(
addAll(defaultConfigurations) repositories = buildscript.repositories.repositories(),
artifacts = buildscript.configurations
.filter { it.isCanBeResolved }
.flatMap { resolveDependencies(it) + resolvePoms(it) }
.minus(plugins.artifacts)
.distinct()
)
} }
private fun Project.projectDependencies(explicitConfigurations: List<String>): DefaultDependencies =
with(DependencyResolver(configurations, dependencies)) {
val toResolve = if (explicitConfigurations.isEmpty()) {
configurations.filter { it.isCanBeResolved }
} else {
configurations.filter { it.name in explicitConfigurations }
}
DefaultDependencies(
repositories = repositories.repositories(),
artifacts = toResolve.flatMap { resolveDependencies(it) + resolvePoms(it) }
.sorted()
.distinct()
)
} }
private fun fetchDistSha256(url: String): String { private fun fetchDistSha256(url: String): String {
@@ -142,5 +146,12 @@ private fun fetchDistSha256(url: String): String {
} }
} }
private val nativePlatformJarRegex = Regex("""native-platform-(\d\.\d+)\.jar""") private val nativePlatformJarRegex = Regex("""native-platform-([\d.]+)\.jar""")
internal fun RepositoryHandler.repositories() = DefaultRepositories(
maven = filterIsInstance<MavenArtifactRepository>()
.filterNot { it.name == "Embedded Kotlin Repository" }
.map { repo ->
DefaultMaven(listOf(repo.url.toString()) + repo.artifactUrls.map { it.toString() })
}
)

View File

@@ -1,109 +0,0 @@
package org.nixos.gradle2nix
import org.gradle.api.Project
import org.gradle.api.file.Directory
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFile
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.util.GradleVersion
private fun versionAtLeast(version: String): Boolean =
GradleVersion.current() >= GradleVersion.version(version)
internal fun <T> Property<T>.conventionCompat(value: T): Property<T> {
return if (versionAtLeast("5.1")) {
convention(value)
} else {
apply { set(value) }
}
}
internal fun <T> Property<T>.conventionCompat(valueProvider: Provider<out T>): Property<T> {
return if (versionAtLeast("5.1")) {
convention(valueProvider)
} else {
apply { set(valueProvider) }
}
}
internal fun DirectoryProperty.conventionCompat(
value: Directory
): DirectoryProperty {
return if (versionAtLeast("5.1")) {
convention(value)
} else {
apply { set(value) }
}
}
internal fun DirectoryProperty.conventionCompat(
valueProvider: Provider<out Directory>
): DirectoryProperty {
return if (versionAtLeast("5.1")) {
convention(valueProvider)
} else {
apply { set(valueProvider) }
}
}
internal fun <T> ListProperty<T>.conventionCompat(
elements: Iterable<T>
): ListProperty<T> {
return if (versionAtLeast("5.1")) {
convention(elements)
} else {
apply { set(elements) }
}
}
internal fun <T> ListProperty<T>.conventionCompat(
provider: Provider<out Iterable<T>>
): ListProperty<T> {
return if (versionAtLeast("5.1")) {
convention(provider)
} else {
apply { set(provider) }
}
}
internal fun RegularFileProperty.conventionCompat(
value: RegularFile
): RegularFileProperty {
return if (versionAtLeast("5.1")) {
convention(value)
} else {
apply { set(value) }
}
}
internal fun RegularFileProperty.conventionCompat(
valueProvider: Provider<out RegularFile>
): RegularFileProperty {
return if (versionAtLeast("5.1")) {
convention(valueProvider)
} else {
apply { set(valueProvider) }
}
}
@Suppress("DEPRECATION")
internal fun Project.directoryPropertyCompat(): DirectoryProperty {
return if (versionAtLeast("5.0")) {
objects.directoryProperty()
} else {
layout.directoryProperty()
}
}
@Suppress("DEPRECATION")
internal fun Project.filePropertyCompat(): RegularFileProperty {
return if (versionAtLeast("5.0")) {
objects.fileProperty()
} else {
layout.fileProperty()
}
}

View File

@@ -1,28 +0,0 @@
package org.nixos.gradle2nix
import org.gradle.api.tasks.GradleBuild
import org.gradle.kotlin.dsl.configure
import java.io.File
open class NixBuildSrcEnv : GradleBuild() {
init {
configure {
tasks = listOf("nixGradleEnv")
startParameter.addInitScript(writeInitScriptTo(dir.resolve("build/nix/init.gradle")))
}
}
}
private fun writeInitScriptTo(dest: File): File {
dest.parentFile.mkdirs()
dest.writeText("""
initscript {
dependencies {
classpath files("$pluginJar")
}
}
apply plugin: org.nixos.gradle2nix.Gradle2NixPlugin
""".trimIndent())
return dest
}

View File

@@ -1,42 +0,0 @@
package org.nixos.gradle2nix
import okio.buffer
import okio.source
import org.gradle.api.tasks.InputFile
import java.net.URI
open class NixBuildscriptEnv : NixEnv() {
@InputFile
val pluginEnvFile = project.filePropertyCompat()
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"
}

View File

@@ -1,73 +0,0 @@
@file:Suppress("UnstableApiUsage")
package org.nixos.gradle2nix
import com.squareup.moshi.JsonClass
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.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.directoryPropertyCompat()
.conventionCompat(project.layout.buildDirectory.dir("nix"))
@OutputFile
val outputFile = project.filePropertyCompat()
.conventionCompat(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" }
}

View File

@@ -1,44 +0,0 @@
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.directoryPropertyCompat()
@OutputFile
val outputFile = project.filePropertyCompat()
.conventionCompat(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

View File

@@ -1,58 +0,0 @@
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.model.ObjectFactory
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import javax.inject.Inject
open class NixGradleEnv @Inject constructor(
objects: ObjectFactory
) : DefaultTask() {
@InputFiles
val inputEnvs = project.files()
@Internal
val outputDir = objects.directoryProperty()
@OutputFile
val outputFile = objects.fileProperty()
.conventionCompat(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()
}
}
}

View File

@@ -1,41 +0,0 @@
package org.nixos.gradle2nix
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional
import org.gradle.kotlin.dsl.listProperty
import java.net.URI
open class NixProjectEnv : NixEnv() {
@Input @Optional
val configurations = project.objects.listProperty<String>().conventionCompat(emptyList())
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"
}

View File

@@ -11,15 +11,14 @@ import org.gradle.plugin.use.resolve.internal.ArtifactRepositoriesPluginResolver
import org.gradle.plugin.use.resolve.internal.PluginResolution import org.gradle.plugin.use.resolve.internal.PluginResolution
import org.gradle.plugin.use.resolve.internal.PluginResolutionResult import org.gradle.plugin.use.resolve.internal.PluginResolutionResult
import org.gradle.plugin.use.resolve.internal.PluginResolveContext import org.gradle.plugin.use.resolve.internal.PluginResolveContext
import java.net.URI
import javax.inject.Inject import javax.inject.Inject
open class NixPluginEnv @Inject constructor( open class PluginResolver @Inject constructor(
private val pluginDependencyResolutionServices: PluginDependencyResolutionServices, private val pluginDependencyResolutionServices: PluginDependencyResolutionServices,
versionSelectorScheme: VersionSelectorScheme, versionSelectorScheme: VersionSelectorScheme,
private val pluginRequests: Collection<PluginRequest> private val pluginRequests: Collection<PluginRequest>
) : NixEnv() { ) {
private val repositories by lazy { val repositories by lazy {
pluginDependencyResolutionServices.resolveRepositoryHandler pluginDependencyResolutionServices.resolveRepositoryHandler
} }
@@ -29,10 +28,9 @@ open class NixPluginEnv @Inject constructor(
) )
private val resolver by lazy { private val resolver by lazy {
Resolver( DependencyResolver(
pluginDependencyResolutionServices.configurationContainer, pluginDependencyResolutionServices.configurationContainer,
pluginDependencyResolutionServices.dependencyHandler, pluginDependencyResolutionServices.dependencyHandler
logger
) )
} }
@@ -50,23 +48,13 @@ open class NixPluginEnv @Inject constructor(
} }
} }
override fun environment(): String = "plugins" fun artifacts(): List<DefaultArtifact> {
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) + return (resolver.resolveDependencies(pluginContext.dependencies, true) +
resolver.resolvePoms(pluginContext.dependencies, true)) resolver.resolvePoms(pluginContext.dependencies, true))
.sorted() .sorted()
.distinct() .distinct()
} }
override fun filename(): String = "plugins.json"
}
private class PluginResult : PluginResolutionResult { private class PluginResult : PluginResolutionResult {
val found = mutableSetOf<PluginResolution>() val found = mutableSetOf<PluginResolution>()
@@ -108,3 +96,5 @@ private class PluginContext : PluginResolveContext {
} }
} }
} }
}

View File

@@ -14,6 +14,9 @@ let
mkFilename = artifact: with artifact; mkFilename = artifact: with artifact;
"${artifactId}-${version}${lib.optionalString (classifier != "") "-${classifier}"}.${extension}"; "${artifactId}-${version}${lib.optionalString (classifier != "") "-${classifier}"}.${extension}";
mkMavenUrls = repo: artifact:
mkArtifactUrl = base: artifact: mkArtifactUrl = base: artifact:
"${lib.removeSuffix "/" base}/${mkPath artifact}/${mkFilename artifact}"; "${lib.removeSuffix "/" base}/${mkPath artifact}/${mkFilename artifact}";

View File

@@ -1,2 +1 @@
include(":app") include(":app", ":model", ":plugin")
include(":plugin")