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 {
implementation(project(":model"))
implementation(kotlin("stdlib-jdk8"))
implementation(kotlin("reflect"))
implementation("org.gradle:gradle-tooling-api:${gradle.gradleVersion}")
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 {

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

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
import com.github.ajalt.clikt.completion.CompletionCandidates
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.defaultLazy
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.multiple
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.arguments.convert
import com.github.ajalt.clikt.parameters.arguments.default
import com.github.ajalt.clikt.parameters.options.*
import com.github.ajalt.clikt.parameters.types.file
import com.squareup.moshi.Moshi
import okio.buffer
import okio.sink
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() {
val wrapper: Boolean by option(help = "Use the project's gradle wrapper for building").flag()
val gradleVersion: String? by option(help = "Use a specific Gradle version")
val wrapper: Boolean by option("--gradle-wrapper", "-w",
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 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)
.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() {
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 main(args: Array<String>) = Main().main(args)
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)

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("kapt") version embeddedKotlinVersion 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 {

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 {
`java-gradle-plugin`
`kotlin-dsl`
`maven-publish`
kotlin("kapt")
id("com.github.johnrengelman.shadow")
id("org.ysb33r.gradletest")
id("org.ajoberstar.stutter")
}
apply {
plugin("kotlin")
}
group = "org.nixos"
version = "1.0.0-SNAPSHOT"
repositories {
jcenter()
mavenCentral()
maven { url = uri("https://repo.gradle.org/gradle/libs-releases") }
}
dependencyLocking {
lockAllConfigurations()
}
dependencies {
implementation(kotlin("stdlib-jdk8"))
implementation("org.apache.maven:maven-model:3.6.1")
implementation("org.apache.maven:maven-model-builder:3.6.1")
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")
implementation(project(":model"))
compileOnly("org.gradle:gradle-tooling-api:${gradle.gradleVersion}")
implementation("org.apache.maven:maven-model:latest.release")
implementation("org.apache.maven:maven-model-builder:latest.release")
implementation("com.squareup.okio:okio:latest.release")
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 {
@@ -28,37 +45,46 @@ gradlePlugin {
register("gradle2nix") {
id = "org.nixos.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"
}
}
}
tasks {
gradleTest {
versions("5.0", "5.1.1", "5.2.1", "5.3.1", "5.4.1")
kotlinDsl = true
kotlinDslPluginOptions {
experimentalWarning.set(false)
}
stutter {
java(8) {
compatibleRange("4.5")
}
gradleTestGenerator {
dependsOn(shadowJar)
doLast {
file(gradleTest.get().initScript).bufferedWriter().use { out ->
out.appendln("""
initscript {
dependencies {
classpath fileTree('file:${buildDir.absolutePath}/libs'.toURI()) {
include '*.jar'
}
""".trimIndent())
out.appendln("""
}
}
apply plugin: org.nixos.gradle2nix.Gradle2NixPlugin
""".trimIndent())
}
}
java(11) {
compatibleRange("5.0")
}
}
tasks {
withType<Test> {
useJUnitPlatform()
}
// gradleTestGenerator {
// dependsOn(shadowJar)
// doLast {
// file(gradleTest.get().initScript).bufferedWriter().use { out ->
// out.append("""
// initscript {
// dependencies {
// classpath fileTree('file:${buildDir.absolutePath}/libs'.toURI()) {
// include '*.jar'
// }
// }
// }
//
// 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.result.ResolvedArtifactResult
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
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(
internal class DependencyResolver(
private val configurations: ConfigurationContainer,
private val dependencies: DependencyHandler,
private val logger: Logger
private val logger: Logger = Logging.getLogger(DependencyResolver::class.simpleName)
) {
private val mavenPomResolver = MavenPomResolver(configurations, dependencies)
fun resolveDependencies(configuration: Configuration): Set<Artifact> {
fun resolveDependencies(configuration: Configuration): Set<DefaultArtifact> {
if (!configuration.isCanBeResolved) {
logger.warn("Cannot resolve configuration ${configuration.name}; ignoring.")
return emptySet()
}
return configuration.resolvedConfiguration.resolvedArtifacts.mapTo(sortedSetOf()) {
with (it) {
Artifact(
DefaultArtifact(
groupId = moduleVersion.id.group,
artifactId = moduleVersion.id.name,
version = moduleVersion.id.version,
@@ -50,13 +51,13 @@ internal class Resolver(
fun resolveDependencies(
dependencies: Collection<Dependency>,
includeTransitive: Boolean = false
): Set<Artifact> {
): Set<DefaultArtifact> {
val configuration = configurations.detachedConfiguration(*(dependencies.toTypedArray()))
configuration.isTransitive = includeTransitive
return resolveDependencies(configuration)
}
fun resolvePoms(configuration: Configuration): Set<Artifact> {
fun resolvePoms(configuration: Configuration): Set<DefaultArtifact> {
return dependencies.createArtifactResolutionQuery()
.forComponents(configuration.incoming.resolutionResult.allComponents.map { it.id })
.withArtifacts(MavenModule::class.java, MavenPomArtifact::class.java)
@@ -73,7 +74,7 @@ internal class Resolver(
}
}
.flatMapTo(sortedSetOf()) { (id, artifact) ->
sequenceOf(Artifact(
sequenceOf(DefaultArtifact(
groupId = id.group,
artifactId = id.module,
version = id.version,
@@ -87,7 +88,7 @@ internal class Resolver(
fun resolvePoms(
dependencies: Collection<Dependency>,
includeTransitive: Boolean = false
): Set<Artifact> {
): Set<DefaultArtifact> {
val configuration = configurations.detachedConfiguration(*(dependencies.toTypedArray()))
configuration.isTransitive = includeTransitive
return resolvePoms(configuration)
@@ -99,10 +100,10 @@ private class MavenPomResolver(
private val dependencies: DependencyHandler
) : ModelResolver {
private val modelBuilder = DefaultModelBuilderFactory().newInstance()
private val resolvedDependencies = mutableSetOf<Artifact>()
private val resolvedDependencies = mutableSetOf<DefaultArtifact>()
@Synchronized
fun resolve(pom: File): Set<Artifact> {
fun resolve(pom: File): Set<DefaultArtifact> {
resolvedDependencies.clear()
modelBuilder.build(
DefaultModelBuildingRequest()
@@ -124,7 +125,7 @@ private class MavenPomResolver(
val file = configurations
.detachedConfiguration(dependencies.create("$groupId:$artifactId:$version@pom"))
.singleFile
resolvedDependencies.add(Artifact(
resolvedDependencies.add(DefaultArtifact(
groupId = groupId,
artifactId = artifactId,
version = version,

View File

@@ -1,21 +1,25 @@
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.artifacts.dsl.RepositoryHandler
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
import org.gradle.api.invocation.Gradle
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.register
import org.gradle.kotlin.dsl.newInstance
import org.gradle.kotlin.dsl.support.serviceOf
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.util.*
import org.nixos.gradle2nix.Gradle as NixGradle
import org.nixos.gradle2nix.Project as NixProject
@Suppress("unused")
open class Gradle2NixPlugin : Plugin<Gradle> {
override fun apply(gradle: Gradle) {
val configurationNames: List<String> =
@@ -24,52 +28,8 @@ open class Gradle2NixPlugin : Plugin<Gradle> {
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 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)
rootProject.serviceOf<ToolingModelBuilderRegistry>()
.register(NixToolingModelBuilder(configurationNames, pluginRequests))
}
}
@@ -84,54 +44,98 @@ open class Gradle2NixPlugin : Plugin<Gradle> {
}
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("""
private class NixToolingModelBuilder(
private val explicitConfigurations: List<String>,
private val pluginRequests: List<PluginRequest>
) : ToolingModelBuilder {
override fun canBuild(modelName: String): Boolean {
return modelName == "org.nixos.gradle2nix.Build"
}
override fun buildAll(modelName: String, project: Project): Build = project.run {
val plugins = buildPlugins(pluginRequests)
DefaultBuild(
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()
?.firstOrNull { f -> nativePlatformJarRegex matches f.name }?.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())
""".trimIndent()
)
}
val gradleDistTask =
gradle.rootProject.tasks.register("nixGradleDist", NixGradleDist::class) {
this.gradleDist.set(gradleDist)
outputDir.set(extension.outputDir)
}
gradleEnv.configure {
dependsOn(gradleDistTask)
}
)
}
private fun Project.buildPlugins(pluginRequests: List<PluginRequest>): DefaultDependencies =
with(objects.newInstance<PluginResolver>(pluginRequests)) {
DefaultDependencies(repositories.repositories(), artifacts())
}
private fun Project.includedBuilds(): List<DefaultIncludedBuild> =
gradle.includedBuilds.map {
DefaultIncludedBuild(it.name, it.projectDir.toRelativeString(project.projectDir))
}
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) }
)
private fun Project.buildscriptDependencies(plugins: DefaultDependencies): DefaultDependencies =
with(DependencyResolver(buildscript.configurations, buildscript.dependencies)) {
DefaultDependencies(
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()
)
}
}
internal val pluginJar by lazy {
File(Gradle2NixPlugin::class.java.protectionDomain.codeSource.location.toURI()).absoluteFile
}
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 {
@@ -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.PluginResolutionResult
import org.gradle.plugin.use.resolve.internal.PluginResolveContext
import java.net.URI
import javax.inject.Inject
open class NixPluginEnv @Inject constructor(
open class PluginResolver @Inject constructor(
private val pluginDependencyResolutionServices: PluginDependencyResolutionServices,
versionSelectorScheme: VersionSelectorScheme,
private val pluginRequests: Collection<PluginRequest>
) : NixEnv() {
private val repositories by lazy {
) {
val repositories by lazy {
pluginDependencyResolutionServices.resolveRepositoryHandler
}
@@ -29,10 +28,9 @@ open class NixPluginEnv @Inject constructor(
)
private val resolver by lazy {
Resolver(
DependencyResolver(
pluginDependencyResolutionServices.configurationContainer,
pluginDependencyResolutionServices.dependencyHandler,
logger
pluginDependencyResolutionServices.dependencyHandler
)
}
@@ -50,61 +48,53 @@ open class NixPluginEnv @Inject constructor(
}
}
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> {
fun artifacts(): List<DefaultArtifact> {
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>()
private class PluginResult : PluginResolutionResult {
val found = mutableSetOf<PluginResolution>()
override fun notFound(sourceDescription: String?, notFoundMessage: String?) {}
override fun notFound(sourceDescription: String?, notFoundMessage: String?) {}
override fun notFound(
sourceDescription: String?,
notFoundMessage: String?,
notFoundDetail: 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)
}
}
override fun isFound(): Boolean = true
private class PluginContext : PluginResolveContext {
val dependencies = mutableSetOf<ExternalModuleDependency>()
val repositories = mutableSetOf<String>()
override fun found(sourceDescription: String, pluginResolution: PluginResolution) {
found.add(pluginResolution)
}
}
override fun add(plugin: PluginImplementation<*>) {
println("add: $plugin")
}
private class PluginContext : PluginResolveContext {
val dependencies = mutableSetOf<ExternalModuleDependency>()
val repositories = mutableSetOf<String>()
override fun addFromDifferentLoader(plugin: PluginImplementation<*>) {
println("addFromDifferentLoader: $plugin")
}
override fun add(plugin: PluginImplementation<*>) {
println("add: $plugin")
}
override fun addLegacy(pluginId: PluginId, m2RepoUrl: String, dependencyNotation: Any) {
repositories.add(m2RepoUrl)
}
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)
override fun addLegacy(pluginId: PluginId, dependencyNotation: Any) {
if (dependencyNotation is ExternalModuleDependency) {
dependencies.add(dependencyNotation)
}
}
}
}

View File

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

View File

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