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

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)