Implement Gradle Kotlin DSL build

This commit is contained in:
Ilya Chernikov
2017-08-02 12:26:50 +02:00
parent f5d6e41993
commit 61dfb75e0e
93 changed files with 3737 additions and 0 deletions
+48
View File
@@ -0,0 +1,48 @@
buildscript {
extra["kotlin_version"] = file("../kotlin-version-for-gradle.txt").readText().trim()
extra["kotlin_gradle_plugin_version"] = extra["kotlin_version"]
extra["repos"] = listOf(
"https://dl.bintray.com/kotlin/kotlin-dev",
"https://repo.gradle.org/gradle/repo",
"https://plugins.gradle.org/m2",
"http://repository.jetbrains.com/utils/")
repositories {
for (repo in (rootProject.extra["repos"] as List<String>)) {
maven { setUrl(repo) }
}
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${rootProject.extra["kotlin_version"]}")
classpath("org.jetbrains.kotlin:kotlin-sam-with-receiver:${rootProject.extra["kotlin_version"]}")
}
}
apply {
plugin("kotlin")
plugin("kotlin-sam-with-receiver")
}
plugins {
`kotlin-dsl`
}
repositories {
for (repo in (rootProject.extra["repos"] as List<String>)) {
maven { setUrl(repo) }
}
}
dependencies {
// TODO: adding the dep to the plugin breaks the build unexpectedly, resolve and uncomment
// compile("org.jetbrains.kotlin:kotlin-gradle-plugin:${rootProject.extra["kotlin_version"]}")
}
samWithReceiver {
annotation("org.gradle.api.HasImplicitReceiver")
}
fun Project.`samWithReceiver`(configure: org.jetbrains.kotlin.samWithReceiver.gradle.SamWithReceiverExtension.() -> Unit): Unit =
extensions.configure("samWithReceiver", configure)
+1
View File
@@ -0,0 +1 @@
rootProject.buildFileName = 'build.gradle.kts'
+71
View File
@@ -0,0 +1,71 @@
@file:Suppress("unused") // usages in build scripts are not tracked properly
import org.gradle.api.*
import org.gradle.api.tasks.*
import org.gradle.kotlin.dsl.*
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.internal.AbstractTask
import org.gradle.jvm.tasks.Jar
import java.io.File
inline fun <reified T : Task> Project.task(noinline configuration: T.() -> Unit) = tasks.creating(T::class, configuration)
fun AbstractTask.dependsOnTaskIfExists(task: String) {
project.tasks.firstOrNull { it.name == task }?.let { dependsOn(it) }
}
fun AbstractTask.dependsOnTaskIfExistsRec(task: String, project: Project? = null) {
dependsOnTaskIfExists(task)
(project ?: this.project).subprojects.forEach {
dependsOnTaskIfExistsRec(task, it)
}
}
fun Jar.setupRuntimeJar(implementationTitle: String): Unit {
dependsOn(":prepare:build.version:prepare")
manifest.attributes.apply {
put("Built-By", project.rootProject.extra["manifest.impl.vendor"])
put("Implementation-Vendor", project.rootProject.extra["manifest.impl.vendor"])
put("Implementation-Title", implementationTitle)
put("Implementation-Version", project.rootProject.extra["build.number"])
}
// from(project.configurations.getByName("build-version").files, action = { into("META-INF/") })
}
fun Jar.setupSourceJar(implementationTitle: String): Unit {
dependsOn("classes")
setupRuntimeJar(implementationTitle + " Sources")
project.pluginManager.withPlugin("java-base") {
from(project.the<JavaPluginConvention>().sourceSets["main"].allSource)
}
classifier = "sources"
project.artifacts.add("archives", this)
}
inline fun<T: Any> Project.withJavaPlugin(crossinline body: () -> T?): T? {
var res: T? = null
pluginManager.withPlugin("java") {
res = body()
}
return res
}
fun Project.getCompiledClasses(): SourceSetOutput? = withJavaPlugin {
the<JavaPluginConvention>().sourceSets.getByName("main").output
}
fun Project.getSources(): SourceDirectorySet? = withJavaPlugin {
the<JavaPluginConvention>().sourceSets.getByName("main").allSource
}
fun Project.getResourceFiles(): SourceDirectorySet? = withJavaPlugin {
the<JavaPluginConvention>().sourceSets.getByName("main").resources
}
fun File(root: File, vararg children: String): File = children.fold(root, { f, c -> File(f, c) })
fun File(root: String, vararg children: String): File = children.fold(File(root), { f, c -> File(f, c) })
+135
View File
@@ -0,0 +1,135 @@
@file:Suppress("unused") // usages in build scripts are not tracked properly
import org.gradle.api.*
import org.gradle.api.artifacts.ConfigurablePublishArtifact
import org.gradle.api.tasks.*
import org.gradle.kotlin.dsl.*
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.jvm.tasks.Jar
fun Project.testsJar(body: Jar.() -> Unit): Jar {
val testsJarCfg = configurations.getOrCreate("tests-jar").extendsFrom(configurations["testCompile"])
return task<Jar>("testJar") {
dependsOn("testClasses")
pluginManager.withPlugin("java") {
from(project.the<JavaPluginConvention>().sourceSets.getByName("test").output)
}
classifier = "tests"
body()
project.addArtifact(testsJarCfg, this, this)
}
}
fun<T> Project.runtimeJarArtifactBy(task: Task, artifactRef: T, body: ConfigurablePublishArtifact.() -> Unit = {}) {
addArtifact("archives", task, artifactRef, body)
addArtifact("runtimeJar", task, artifactRef, body)
}
fun<T: Jar> Project.runtimeJar(task: T, body: T.() -> Unit = {}): T =
task.apply {
setupPublicJar()
body()
project.runtimeJarArtifactBy(this, this)
}
fun Project.runtimeJar(taskName: String = "jar", body: Jar.() -> Unit = {}): Jar = runtimeJar(getOrCreateTask(taskName, body))
fun Project.sourcesJar(body: Jar.() -> Unit = {}): Jar =
getOrCreateTask("sourcesJar") {
setupPublicJar("Sources")
try {
project.pluginManager.withPlugin("java-base") {
from(project.the<JavaPluginConvention>().sourceSets["main"].allSource)
}
} catch (e: UnknownDomainObjectException) {
// skip default sources location
}
tasks.findByName("classes")?.let { dependsOn(it) }
body()
project.addArtifact("archives", this, this)
}
fun Project.javadocJar(body: Jar.() -> Unit = {}): Jar =
getOrCreateTask("javadocJar") {
setupPublicJar("JavaDoc")
tasks.findByName("javadoc")?.let{ it as Javadoc }?.takeIf { it.enabled }?.let {
dependsOn(it)
from(it.destinationDir)
}
body()
project.addArtifact("archives", this, this)
}
fun Project.standardPublicJars(): Unit {
runtimeJar()
sourcesJar()
javadocJar()
}
fun Project.publish(body: Upload.() -> Unit = {}): Upload {
apply<plugins.PublishedKotlinModule>()
return (tasks.getByName("uploadArchives") as Upload).apply {
body()
}
}
fun Project.ideaPlugin(subdir: String = "lib", body: Copy.() -> Unit) {
task<Copy>("idea-plugin") {
body()
into(File(rootProject.extra["ideaPluginDir"].toString(), subdir).path)
rename("-${java.util.regex.Pattern.quote(rootProject.extra["build.number"].toString())}", "")
}
}
fun Project.ideaPlugin() = ideaPlugin {
tasks.findByName("jar")?.let {
from(it)
}
}
fun Project.dist(body: Copy.() -> Unit = {}) {
task<Copy>("dist") {
tasks.findByName("assemble")?.let {
dependsOn(it)
}
body()
rename("-${java.util.regex.Pattern.quote(rootProject.extra["build.number"].toString())}", "")
into(rootProject.extra["distLibDir"].toString())
}
}
fun ConfigurationContainer.getOrCreate(name: String): Configuration = findByName(name) ?: create(name)
fun Jar.setupPublicJar(classifier: String = "", classifierDescr: String? = null) {
this.classifier = classifier.toLowerCase()
dependsOn(":prepare:build.version:prepare")
manifest.attributes.apply {
put("Built-By", project.rootProject.extra["manifest.impl.vendor"])
put("Implementation-Vendor", project.rootProject.extra["manifest.impl.vendor"])
put("Implementation-Title", "${project.description} ${classifierDescr ?: classifier}".trim())
put("Implementation-Version", project.rootProject.extra["build.number"])
}
// from(project.configurations.getByName("build-version").files, action = { into("META-INF/") })
}
fun<T> Project.addArtifact(configuration: Configuration, task: Task, artifactRef: T, body: ConfigurablePublishArtifact.() -> Unit = {}) {
artifacts.add(configuration.name, artifactRef) {
builtBy(task)
body()
}
}
fun<T> Project.addArtifact(configurationName: String, task: Task, artifactRef: T, body: ConfigurablePublishArtifact.() -> Unit = {}) =
addArtifact(configurations.getOrCreate(configurationName), task, artifactRef, body)
inline fun<reified T: Task> Project.getOrCreateTask(taskName: String, body: T.() -> Unit): T =
(tasks.findByName(taskName)?.let { it as T } ?: task<T>(taskName)).apply{ body() }
+93
View File
@@ -0,0 +1,93 @@
@file:Suppress("unused") // usages in build scripts are not tracked properly
import org.gradle.api.*
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.kotlin.dsl.*
import org.gradle.plugin.use.PluginDependenciesSpec
import org.gradle.plugin.use.PluginDependencySpec
import java.io.File
import java.util.*
val bootstrapKotlinVersion: String = System.getProperty("bootstrap.kotlin.version") ?: embeddedKotlinVersion
fun PluginDependenciesSpec.kotlin(module: String, version: String? = null): PluginDependencySpec =
id("org.jetbrains.kotlin.$module") version (version ?: bootstrapKotlinVersion)
fun Project.buildVersion(): Dependency {
val cfg = configurations.create("build-version")
return dependencies.add(cfg.name, dependencies.project(":prepare:build.version", configuration = "default"))
}
fun Project.commonDep(coord: String): String {
val parts = coord.split(':')
return when (parts.size) {
1 -> "$coord:$coord:${rootProject.extra["versions.$coord"]}"
2 -> "${parts[0]}:${parts[1]}:${rootProject.extra["versions.${parts[1]}"]}"
3 -> coord
else -> throw IllegalArgumentException("Illegal maven coordinates: $coord")
}
}
fun Project.commonDep(group: String, artifact: String): String = "$group:$artifact:${rootProject.extra["versions.$artifact"]}"
fun Project.preloadedDeps(vararg artifactBaseNames: String, baseDir: File = File(rootDir, "dependencies"), subdir: String? = null): ConfigurableFileCollection {
val dir = if (subdir != null) File(baseDir, subdir) else baseDir
if (!dir.exists() || !dir.isDirectory) throw GradleException("Invalid base directory $dir")
val matchingFiles = dir.listFiles { file -> artifactBaseNames.any { file.matchMaybeVersionedArtifact(it) } }
if (matchingFiles == null || matchingFiles.size < artifactBaseNames.size)
throw GradleException("Not all matching artifacts '${artifactBaseNames.joinToString()}' found in the '$dir' (found: ${matchingFiles?.joinToString { it.name }})")
return files(*matchingFiles.map { it.canonicalPath }.toTypedArray())
}
fun Project.ideaSdkDeps(vararg artifactBaseNames: String, subdir: String = "lib"): ConfigurableFileCollection =
preloadedDeps(*artifactBaseNames, baseDir = File(rootDir, "ideaSDK"), subdir = subdir)
fun Project.ideaSdkCoreDeps(vararg artifactBaseNames: String): ConfigurableFileCollection = ideaSdkDeps(*artifactBaseNames, subdir = "core")
fun Project.ideaPluginDeps(vararg artifactBaseNames: String, plugin: String, subdir: String = "lib"): ConfigurableFileCollection =
preloadedDeps(*artifactBaseNames, baseDir = File(rootDir, "ideaSDK"), subdir = "plugins/$plugin/$subdir")
fun Project.kotlinDep(artifactBaseName: String, version: String? = null): String = "org.jetbrains.kotlin:kotlin-$artifactBaseName:${version ?: bootstrapKotlinVersion}"
fun DependencyHandler.projectDep(name: String): Dependency = project(name, configuration = "default")
fun DependencyHandler.projectDepIntransitive(name: String): Dependency =
project(name, configuration = "default").apply { isTransitive = false }
fun DependencyHandler.projectTests(name: String): Dependency = project(name, configuration = "tests-jar").apply { isTransitive = false }
val protobufLiteProject = ":custom-dependencies:protobuf-lite"
fun DependencyHandler.protobufLite(): ProjectDependency =
project(protobufLiteProject, configuration = "default").apply { isTransitive = false }
val protobufLiteTask = "$protobufLiteProject:prepare"
fun DependencyHandler.protobufFull(): ProjectDependency =
project(protobufLiteProject, configuration = "relocated").apply { isTransitive = false }
val protobufFullTask = "$protobufLiteProject:prepare-relocated-protobuf"
private fun File.matchMaybeVersionedArtifact(baseName: String) = name.matches(baseName.toMaybeVersionedJarRegex())
private val wildcardsRe = """[^*?]+|(\*)|(\?)""".toRegex()
private fun String.wildcardsToEscapedRegexString(): String = buildString {
wildcardsRe.findAll(this@wildcardsToEscapedRegexString).forEach {
when {
it.groups[1] != null -> append(".*")
it.groups[2] != null -> append(".")
else -> append("\\Q${it.groups[0]!!.value}\\E")
}
}
}
private fun String.toMaybeVersionedJarRegex(): Regex {
val hasJarExtension = endsWith(".jar")
val escaped = this.wildcardsToEscapedRegexString()
return Regex(if (hasJarExtension) escaped else "$escaped(-\\d.*)?\\.jar") // TODO: consider more precise version part of the regex
}
val propertiesX =
java.util.Properties().apply { load(java.io.FileInputStream("gradle.properties")) }
+125
View File
@@ -0,0 +1,125 @@
@file:Suppress("unused") // usages in build scripts are not tracked properly
import org.gradle.api.*
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.internal.HasConvention
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.*
import org.gradle.kotlin.dsl.*
import java.io.File
//import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
private fun Project.configureKotlinProjectSourceSet(srcs: Iterable<File>,
sourceSetName: String,
getSources: SourceSet.() -> SourceDirectorySet,
configureSourceDirs: SourceDirectorySet.() -> Unit) =
configure<JavaPluginConvention> {
// if (srcs.none()) {
// sourceSets.removeIf { it.name == sourceSetName }
// }
// else {
sourceSets.matching { it.name == sourceSetName }.forEach { it.getSources().setSrcDirs(srcs).configureSourceDirs() }
// }
}
private fun Project.configureKotlinProjectSourceSet(vararg srcs: String, sourceSetName: String,
getSources: SourceSet.() -> SourceDirectorySet,
sourcesBaseDir: File? = null,
configureSourceDirs: SourceDirectorySet.() -> Unit = {}) =
configureKotlinProjectSourceSet(srcs.map { File(sourcesBaseDir ?: projectDir, it) }, sourceSetName, getSources, configureSourceDirs)
fun Project.configureKotlinProjectSources(vararg srcs: String,
sourcesBaseDir: File? = null,
configureSourceDirs: SourceDirectorySet.() -> Unit = {}) =
configureKotlinProjectSourceSet(*srcs, sourceSetName = "main", getSources = { this.java },
sourcesBaseDir = sourcesBaseDir, configureSourceDirs = configureSourceDirs)
fun Project.configureKotlinProjectSources(srcs: Iterable<File>,
configureSourceDirs: SourceDirectorySet.() -> Unit = {}) =
configureKotlinProjectSourceSet(srcs, sourceSetName = "main", getSources = { this.java }, configureSourceDirs = configureSourceDirs)
fun Project.configureKotlinProjectSourcesDefault(sourcesBaseDir: File? = null,
configureSourceDirs: SourceDirectorySet.() -> Unit = {}) =
configureKotlinProjectSources("src", sourcesBaseDir = sourcesBaseDir, configureSourceDirs = configureSourceDirs)
fun Project.configureKotlinProjectResources(vararg srcs: String,
sourcesBaseDir: File? = null,
configureSourceDirs: SourceDirectorySet.() -> Unit = {}) =
configureKotlinProjectSourceSet(*srcs, sourceSetName = "main", getSources = { this.resources },
sourcesBaseDir = sourcesBaseDir, configureSourceDirs = configureSourceDirs)
fun Project.configureKotlinProjectResources(srcs: Iterable<File>,
configureSourceDirs: SourceDirectorySet.() -> Unit = {}) =
configureKotlinProjectSourceSet(srcs, sourceSetName = "main", getSources = { this.resources }, configureSourceDirs = configureSourceDirs)
fun Project.configureKotlinProjectResourcesDefault(sourcesBaseDir: File? = null) {
configureKotlinProjectResources("resources", sourcesBaseDir = sourcesBaseDir)
configureKotlinProjectResources("src", sourcesBaseDir = sourcesBaseDir) { include("META-INF/**", "**/*.properties") }
}
fun Project.configureKotlinProjectNoTests() {
configureKotlinProjectSourceSet(sourceSetName = "test", getSources = { this.java })
configureKotlinProjectSourceSet(sourceSetName = "test", getSources = { this.resources })
}
fun Project.configureKotlinProjectTests(vararg srcs: String, sourcesBaseDir: File? = null,
configureSourceDirs: SourceDirectorySet.() -> Unit = {}) =
configureKotlinProjectSourceSet(*srcs, sourceSetName = "test", getSources = { this.java },
sourcesBaseDir = sourcesBaseDir, configureSourceDirs = configureSourceDirs)
fun Project.configureKotlinProjectTestsDefault(sourcesBaseDir: File? = null,
configureSourceDirs: SourceDirectorySet.() -> Unit = {}) =
configureKotlinProjectTests("test", "tests", sourcesBaseDir = sourcesBaseDir, configureSourceDirs = configureSourceDirs)
fun Project.configureKotlinProjectTestResources(vararg srcs: String,
sourcesBaseDir: File? = null,
configureSourceDirs: SourceDirectorySet.() -> Unit = {}) =
configureKotlinProjectSourceSet(*srcs, sourceSetName = "test", getSources = { this.resources },
sourcesBaseDir = sourcesBaseDir, configureSourceDirs = configureSourceDirs)
inline fun Project.sourceSets(crossinline body: SourceSetsBuilder.() -> Unit) =
SourceSetsBuilder(this).body()
class SourceSetsBuilder(val project: Project) {
inline operator fun String.invoke(crossinline body: SourceSet.() -> Unit) {
val sourceSetName = this
project.configure<JavaPluginConvention>
{
sourceSets.matching { it.name == sourceSetName }.forEach {
it.body()
}
}
}
}
fun SourceSet.none() {
java.srcDirs()
resources.srcDirs()
}
fun SourceSet.default() {
when (name) {
"main" -> {
java.srcDirs("src")
resources.srcDir("resources")
resources.srcDir("src").apply { include("META-INF/**", "**/*.properties") }
}
"test" -> {
java.srcDirs("test", "tests")
}
}
}
// TODO: adding dep to the plugin breaks the build unexpectedly, resolve and uncomment
//val SourceSet.kotlin: SourceDirectorySet
// get() =
// (this as HasConvention)
// .convention
// .getPlugin(KotlinSourceSet::class.java)
// .kotlin
//
//
//fun SourceSet.kotlin(action: SourceDirectorySet.() -> Unit) =
// kotlin.action()