How to access Kotlin DSL extensions from non-build-gradle file? - maven

There are few Kotlin Extensions that come along with org.gradle.kotlin.dsl like
android or publishing etc. Say, for publishing, the extension is defined as below
fun org.gradle.api.Project.`publishing`(configure: org.gradle.api.publish.PublishingExtension.() -> Unit): Unit =
(this as org.gradle.api.plugins.ExtensionAware).extensions.configure("publishing", configure)
That means within mybuild.gradle.kts I can call
publishing {
// blablabla
}
In my case, I have a separate file that handles publishing defined as random.gradle.kts and is located at totally random directory.
Here, I want to access this publishing extension but not able to do it.
Any suggestions are welcomed.
To give another example android is also not accessible.
In my build.gradle.kts I can access android but in my random.gradle.kts I am not able to do it.
android extension is defined as below
val org.gradle.api.Project.`android`: com.android.build.gradle.LibraryExtension get() =
(this as org.gradle.api.plugins.ExtensionAware).extensions.getByName("android") as com.android.build.gradle.LibraryExtension
In fact, LibraryExtension is also not accessible.
So, how to pass or import those extensions to my random.gradle.kts?

//build.gradle.kts tested with gradle 6.7
subprojects {
afterEvaluate {
(extensions.findByType(com.android.build.gradle.LibraryExtension::class)
?: extensions.findByType(com.android.build.gradle.AppExtension::class))?.apply {
println("Found android subproject $name")
lintOptions.isAbortOnError = false
compileSdkVersion(30)
if (this is com.android.build.gradle.AppExtension)
println("$name is an application")
if (this is com.android.build.gradle.LibraryExtension) {
val publishing =
extensions.findByType(PublishingExtension::class.java) ?: return#afterEvaluate
val sourcesJar by tasks.registering(Jar::class) {
archiveClassifier.set("sources")
from(sourceSets.getByName("main").java.srcDirs)
}
afterEvaluate {
publishing.apply {
val projectName = name
publications {
val release by registering(MavenPublication::class) {
components.forEach {
println("Publication component: ${it.name}")
}
from(components["release"])
artifact(sourcesJar.get())
artifactId = projectName
groupId = ProjectVersions.GROUP_ID
version = ProjectVersions.VERSION_NAME
}
}
}
}
}
}
}
}

Related

no build output compiled for common module of Kotlin Multiplatform project

I'm trying to figure out why dependent projects for my Kotlin MPP library don't see any provided modules in their common modules even though the targets (jvm, android) can see them.
Published via maven-publish.
The /build directory for the library contains nothing I can identify as an intermediate representation of my common modules, leading me to think that I need to explicitly tell Gradle to produce the files to be included as common in the published package.
As it is, the .aar and .jar files produced in the android and desktop (jvm) modules each look normal, but the published common module is empty.
I need that common module to be populated before I can code against it inside the common module of dependent projects.
Here is the relevant section of my build.gradle.kts. I omit the repository config as it appears to work.
I basically followed the instructions from kotlinlang.org.
I've looked at the maven-publish plugin configuration, the settings for the kotlin-multiplatformm plugin, and the configured project structure.
kotlin version is 1.6.10, unable to update due to Jetbrains Compose dependency.
plugins {
kotlin("multiplatform")
id("com.android.library")
id("maven-publish")
}
kotlin {
android {
publishLibraryVariants = listOf("release", "debug")
}
jvm("desktop") {
compilations.all {
kotlinOptions {
jvmTarget = "11"
}
}
}
val publicationsFromMainHost = listOf(jvm("desktop").name, "kotlinMultiplatform")
publishing {
publications {
matching { it.name in publicationsFromMainHost }.all {
val targetPublication = this#all
tasks.withType<AbstractPublishToMaven>()
.matching { it.publication == targetPublication }
}
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.3.2")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val androidMain by getting {
dependencies {
implementation("androidx.startup:startup-runtime:1.1.1")
}
}
val androidTest by getting {
dependencies {
implementation("junit:junit:4.13.2")
implementation("androidx.test:core:1.4.0")
implementation("androidx.test:runner:1.4.0")
implementation("androidx.test:rules:1.4.0")
implementation("org.robolectric:robolectric:4.6.1")
}
}
val desktopMain by getting
val desktopTest by getting {
dependencies {
implementation("junit:junit:4.13.2")
}
}
}
}
The answer is to manually supply the Kotlin stdlib dependency, rather than relying on the gradle plugin to add it.
When there are only jvm-based builds present, commonMain will be built with platform type of jdk8 rather than common. By making an explicit dependency on stdlib-common, it will be coerced back to the common platform, and then the correct metadata will be created and published.
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
}
}
}
}

How to access PlaginManager repositories from custom gradle plugin

My problem is that I develop a custom Gradle plugin and I need to get access to repositories which belongs to Settings object, particular PluginManagement object.
settings.gradle file of project which I connect my custom plugin looks like this:
pluginManagement {
resolutionStrategy {
}
repositories {
maven {
credentials {
username = System.getenv("USERNAME")
password = System.getenv("PASSWORD")
}
url = uri("${System.getenv("nexusUrl")}/repository/***")
}
}
}
There is my code I want to just list url of all repositories:
project.afterEvaluate {
val gradle = project.rootProject.gradle as org.gradle.invocation.DefaultGradle
val settings = gradle.settings as org.gradle.initialization.DefaultSettings
settings.pluginManagement.repositories.forEach { repo ->
run {
repo as MavenArtifactRepository
println(repo.url)
}
}
}
I expect the following output result:
https://nexus_url/repository/***
But instead I get 'null' in output.
But when I run this code from custom task I get expected result:
tasks.create("abc") {
project.afterEvaluate {
val gradle = project.rootProject.gradle as org.gradle.invocation.DefaultGradle
val settings = gradle.settings as org.gradle.initialization.DefaultSettings
settings.pluginManagement.repositories.forEach { repo ->
run {
repo as MavenArtifactRepository
println(repo.url)
}
}
}
}
Why are in execution time this repositories not empty and what is the best way to access them from Project object?

Why is this a receiver type mismatch when it seems to match what I see in the Gradle docs?

I'm converting a build from Buildr to Gradle.
So far I have the following build script:
repositories {
jcenter()
}
allprojects {
version = "0.8.1-SNAPSHOT"
group = "org.trypticon.hex"
}
subprojects {
apply(plugin = "java-library")
apply(plugin = "maven-publish")
apply(plugin = "signing")
configure<JavaPluginExtension> {
sourceCompatibility = JavaVersion.VERSION_11
withJavadocJar()
withSourcesJar()
}
dependencies {
"testImplementation"("junit:junit:4.13")
}
configure<PublishingExtension> {
repositories {
maven {
url = uri(if (version.toString().contains("SNAPSHOT")) {
"https://oss.sonatype.org/content/repositories/snapshots"
} else {
"https://oss.sonatype.org/service/local/staging/deploy/maven2"
})
credentials {
username = System.getenv("DEPLOY_USER")
password = System.getenv("DEPLOY_PASS")
}
}
}
publications {
register<MavenPublication>("mavenJava") {
pom {
// omitting a bunch of metadata unrelatted to the issue
}
}
}
}
configure<SigningExtension> {
sign(publishing.publications["mavenJava"])
}
}
When I try to build, I get this:
build.gradle.kts:124:14: Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public val PluginDependenciesSpec.publishing: PluginDependencySpec defined in org.gradle.kotlin.dsl
IntelliJ says that the types match and shows no error.
I started with code from the Gradle docs which said to use this:
signing {
sign(publishing.publications["mavenJava"])
}
But that fails when inside a subprojects block.
Using:
Gradle 6.8
Java 11.0.3
Further investigation:
I tried splitting the lines out to see exactly which bit the issue was on.
configure<SigningExtension> {
val e: PublishingExtension = publishing
val p: Publication = e.publications["mavenJava"]
sign(p)
}
The error points directly at publishing on the first line. Hovering over it in IDEA, it helpfully tells me that Project.publishing is a PublishingExtension, so the call shows no error. Now the question is, why is it fine when inspecting the file, but fails at runtime? I thought this was exactly the kind of thing switching to Kotlin was going to stop. :(
I had a similar problem, which I fixed by writing the following code:
configure<SigningExtension> {
val pubExt = checkNotNull(extensions.findByType(PublishingExtension::class.java))
val publication = pubExt.publications["your_publication"]
sign(publication)
}
In your case, your_publication should be replaced with mavenJava.

How to consolidate imported plugins to custom plugin in Gradle using Kotlin

I have microservices that will share some of the same configuration between all of them, mainly Jib, publish, and release. Not sure if it's possible to do the same for dependencies but it would be beneficial to include actuator and log4j2 in each. Here is the build.gradle.kts for one of my projects.
import net.researchgate.release.BaseScmAdapter
import net.researchgate.release.GitAdapter
import net.researchgate.release.ReleaseExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("com.gorylenko.gradle-git-properties") version "1.5.1"
id("com.google.cloud.tools.jib") version "1.6.1"
id("io.spring.dependency-management") version "1.0.7.RELEASE"
id("net.researchgate.release") version "2.8.1"
id("org.sonarqube") version "2.7.1"
id("org.springframework.boot") version "2.1.6.RELEASE"
kotlin("jvm") version "1.2.71"
kotlin("plugin.spring") version "1.2.71"
jacoco
`maven-publish`
}
java.sourceCompatibility = JavaVersion.VERSION_1_8
springBoot {
buildInfo {
group = project.properties["group"].toString()
version = project.properties["version"].toString()
description = project.properties["description"].toString()
}
}
repositories {
maven(url = uri(project.properties["nexus.url.gateway"].toString()))
mavenCentral()
}
dependencies {
// Kotlin
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// Spring
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-log4j2")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.cloud:spring-cloud-config-server")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
dependencyManagement {
imports {
mavenBom("org.springframework.cloud:spring-cloud-dependencies:Greenwich.SR3")
}
}
configurations.all {
exclude(group = "ch.qos.logback", module = "logback-classic")
exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging")
}
tasks {
withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "1.8"
}
}
build { dependsOn(clean) }
afterReleaseBuild { dependsOn(publish) }
publish { dependsOn(build) }
jibDockerBuild { dependsOn(build) }
jacocoTestReport {
reports {
html.isEnabled = false
xml.isEnabled = true
}
}
}
publishing {
publications {
create<MavenPublication>(project.name) {
from(components["java"])
pom {
scm {
connection.set("scm:git:git#github.com:company/${project.name}.git")
developerConnection.set("scm:git:git#github.com:company/${project.name}.git")
url.set("https://github.com/company/${project.name}/")
}
}
versionMapping {
usage("java-api") {
fromResolutionOf("runtimeClasspath")
}
usage("java-runtime") {
fromResolutionResult()
}
}
}
}
repositories {
maven {
val releasesRepoUrl = "${project.properties["nexus.url.publish"].toString()}/releases"
val snapshotsRepoUrl = "${project.properties["nexus.url.publish"].toString()}/snapshots"
url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl)
credentials {
username = project.properties["nexus.user"].toString()
password = project.properties["nexus.password"].toString()
}
}
}
}
fun ReleaseExtension.git(configureFn : GitAdapter.GitConfig.() -> Unit) {
(propertyMissing("git") as GitAdapter.GitConfig).configureFn()
}
release {
scmAdapters = mutableListOf<Class<out BaseScmAdapter>> ( GitAdapter::class.java )
git {
requireBranch = "develop"
pushToRemote = project.properties["release.git.remote"].toString()
pushReleaseVersionBranch = "master"
tagTemplate = "${project.name}.${project.version}"
}
}
jib {
from {
image = "openjdk:8-jdk-alpine"
}
to {
image = "host:port/${project.name}:${project.version}"
auth {
username = project.properties["nexus.user"].toString()
password = project.properties["nexus.password"].toString()
}
}
container {
workingDirectory = "/"
ports = listOf("8080")
environment = mapOf(
"SPRING_OUTPUT_ANSI_ENABLED" to "ALWAYS",
"SPRING_CLOUD_BOOTSTRAP_LOCATION" to "/path/to/bootstrap.yml"
)
useCurrentTimestamp = true
}
setAllowInsecureRegistries(true)
}
I was able to get a custom plugin created and added to this project using git#github.com:klg71/kotlintestplugin.git and git#github.com:klg71/kotlintestpluginproject.git but I have no idea how to implement these existing plugins and their configurations. In the main Plugin class in the apply function I am able to call the project.pluginManager.apply(PublishingPlugin::class.java) which causes the task to show in the project referencing the custom plugin but I can't figure out how to configure it and it does not successfully publish to the nexus server. I can publish the plugin itself to the nexus server and reference it in the microservice but it skips running the task, which I assume is caused by the configuration not being included. Also, when trying to apply/configure the Jib plugin, all of the classes are not visible when attempting to import.
So the above answer isn't super long and to preserve the issues I ran into I am posting a new answer.
PLUGIN
This portion of the answer is going to discuss the actual custom plugin project.
Because the plugins wrapper in the build.gradle.kts is runtime, the CustomPlugin.kt does not have access to it at compile time. My boss who is much smarter than me was kind enough to point this out to me even though he has never worked with gradle. Although I looked pretty dumb in front of him he still got me up and running by basically following the 'legacy' way of applying plugins in gradle.
plugins { // This is a runtime script preventing plugins declared here to be accessible in CustomPlugin.kt but is used to actually publish/release this plugin itself
id("net.researchgate.release") version "2.8.1"
kotlin("jvm") version "1.3.0"
`maven-publish`
}
repositories {
maven { url = uri("https://plugins.gradle.org/m2/") } // This is required to be able to import plugins below in the dependencies
jcenter()
}
dependencies {
compile(kotlin("stdlib"))
compile(kotlin("reflect"))
// These must be declared here (at compile-time) in order to access in CustomPlugin.kt
compile(group = "gradle.plugin.com.gorylenko.gradle-git-properties", name = "gradle-git-properties", version = "2.2.0")
compile(group = "gradle.plugin.com.google.cloud.tools", name = "jib-gradle-plugin", version = "1.7.0")
compile(group = "net.researchgate", name = "gradle-release", version = "2.8.1")
compile(group = "org.asciidoctor", name = "asciidoctor-gradle-plugin", version = "1.5.9.2")
compile(group = "org.jetbrains.dokka", name = "dokka-gradle-plugin", version = "0.9.18")
compile(group = "org.sonarsource.scanner.gradle", name = "sonarqube-gradle-plugin", version = "2.8")
implementation(gradleApi()) // This exposes the gradle API to CustomPlugin.kt
}
This allowed me to have access to jib and everything else in the CustomPlugin.kt.
The plugins jacoco and maven-publish are automatically accessible in the plugin project but still need to be added in the microservice project referencing the plugin. I was unable to find a workaround for this unfortunately.
I included the typical maven-publish plugin in the build.gradle.kts to push to nexus with the publishing task configurations in the build.gradle.kts as well so I could pull this from nexus in the microservice that wanted to use the plugin.
publishing {
publications {
create<MavenPublication>(project.name) {
from(components["java"])
pom {
scm {
connection.set("scm:git:git#github.com:diendanyoi54/${project.name}.git")
developerConnection .set("scm:git:git#github.com:diendanyoi54/${project.name}.git")
url.set("https://github.com/diendanyoi54/${project.name}/")
}
}
}
}
repositories {
maven {
val baseUrl = "https://${project.properties["nexus.host"].toString()}:${project.properties["nexus.port.jar"].toString()}/repository"
url = uri(if (version.toString().endsWith("SNAPSHOT")) "$baseUrl/maven-snapshots" else "$baseUrl/maven-releases")
credentials {
username = project.properties["nexus.user"].toString()
password = project.properties["nexus.password"].toString()
}
}
}
}
Lastly, you want to make sure you include the properties file that will tell the microservices where the plugin class is. In Intellij's IDEA, when typing the path to the implementation-class it auto completed for me.
The name of this file should reflect apply(plugin = "string") in the microservice's build.gradle.kts.
IMPLEMENTATION
This portion of the answer is going to reflect the microservice project that will be referencing the plugin. As stated above, jacoco and maven-publish still need to be added to the plugin block in the build.gradle.kts for some reason (I think because they are official gradle plugins).
To reference the plugin from the nexus server it was published to, the microservice must reference it in the buildscript.
buildscript { // Custom plugin must be accessed by this buildscript
repositories {
maven {
url = uri("https://${project.properties["nexus.host"].toString()}:${project.properties["nexus.port.jar"].toString()}/repository/maven-public")
credentials {
username = project.properties["nexus.user"].toString()
password = project.properties["nexus.password"].toString()
}
}
}
dependencies { classpath("com.company:kotlin-consolidated-plugin:1.0.0-SNAPSHOT") }
}
Lastly, the plugin must be applied using the properties file name referenced above.
apply(plugin = "com.company.kotlinconsolidatedplugin") // Custom plugin cannot reside in plugin declaration above
I created sample projects of these and posted them to Github so feel free to clone or take a look:
git#github.com:diendanyoi54/kotlin-consolidated-plugin.git
git#github.com:diendanyoi54/kotlin-consolidated-plugin-implementation.git
I was able to successfully able to use the github repo examples referenced above to accomplish what I needed with the publish task. Here is my custom plugin's build.gradle.kts.
plugins {
id("com.google.cloud.tools.jib") version "1.6.1"
id("org.sonarqube") version "2.7.1"
kotlin("jvm") version "1.3.0"
`maven-publish`
}
dependencies {
compile(kotlin("stdlib"))
compile(kotlin("reflect"))
implementation(gradleApi())
}
repositories {
jcenter()
}
publishing {
publications {
create<MavenPublication>(project.name) {
from(components["java"])
pom {
scm {
connection.set("scm:git:git#github.com:company/${project.name}.git")
developerConnection.set("scm:git:git#github.com:company/${project.name}.git")
url.set("https://github.com/company/${project.name}/")
}
}
}
}
repositories {
maven {
val baseUrl = "https://${project.properties["nexus.host"].toString()}:${project.properties["nexus.port.jar"].toString()}/repository"
url = uri(if (version.toString().endsWith("SNAPSHOT")) "$baseUrl/maven-snapshots" else "$baseUrl/maven-releases")
credentials {
username = project.properties["nexus.user"].toString()
password = project.properties["nexus.password"].toString()
}
}
}
}
Here is the CustomPlugin.kt class.
package com.company.gradlemicroserviceplugin
//import com.google.cloud.tools.jib.gradle.JibExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.plugins.PublishingPlugin
import org.gradle.testing.jacoco.tasks.JacocoReport
import java.net.URI
open class CustomPlugin : Plugin<Project> {
override fun apply(project: Project) {
// applySonar(project)
applyPublish(project)
// applyJib(project)
}
// private fun applySonar(project: Project) {
// project.pluginManager.apply("sonarqube")
// val task = project.task("jacocoTestReport") as JacocoReport
// task.reports = JacocoReport()
// jacocoTestReport { This was nested in the tasks declaration in build.gradle.kts so the fields below are the fields I'm trying to set in task.reports
// reports {
// html.isEnabled = false
// xml.isEnabled = true
// }
// }
// }
private fun applyPublish(project: Project) {
project.pluginManager.apply(PublishingPlugin::class.java)
val publishingExtension = project.extensions.findByType(PublishingExtension::class.java)
val mavenPublication = publishingExtension?.publications?.create(project.name, MavenPublication::class.java)
publishingExtension?.repositories?.maven {
val baseUrl = "https://${project.properties["nexus.host"].toString()}:${project.properties["nexus.port.jar"].toString()}/repository"
it.url = URI(if (project.version.toString().endsWith("SNAPSHOT")) "$baseUrl/maven-snapshots" else "$baseUrl/maven-releases")
it.credentials { cred ->
cred.username = project.properties["nexus.user"].toString()
cred.password = project.properties["nexus.password"].toString()
}
}
mavenPublication?.from(project.components.findByName("java"))
mavenPublication?.pom?.scm {
it.connection.set("scm:git:git#github.com:company/${project.name}.git")
it.developerConnection.set("scm:git:git#github.com:company/${project.name}.git")
it.url.set("https://github.com/company/${project.name}/")
}
}
// private fun applyJib(project: Project) {
// project.pluginManager.apply(JibPlugin::class.java)
//
// }
}
There are definitely areas of improvement on this but at least I got something working here. There is maven-publish logic in both build.gradle.kts because I push to the custom plugin to nexus and the maven-publish logic is in the CustomPlugin.kt class so the microservice that references this plugin can use it. However, I am unable to successfully setup Jib and Sonar. Jib doesn't give me access to anything in com.google.cloud.tools.jib.gradle preventing me from using the same approach as I used in maven-publish.
For Sonar I think I'm on the right track with retrieving the task by its name but I'm unable to set any fields that belong to task.reports because they are all final and this is necessary for Sonar to properly analyze Kotlin.
Applying built-in plugins
plugins {
java
id("jacoco")
}
You can also use the older apply syntax:
apply(plugin = "checkstyle")
Applying external plugins
plugins {
id("org.springframework.boot") version "2.0.1.RELEASE"
}
i am not good in kotlin but here is link to better understanding missing migration guide to the Gradle Kotlin DSL

Gradle Kotlin DSL: Define Kotlin version in unique place

For describing Gradle build scripts, we can use Kotlin via build.gradle.kts files. It's a common problem to globally define the Kotlin version to be used, both in the dependencies and also in the build plugin section (It's rather uncommon to have different versions in use for the given case).
Consider the following code (Gradle 4.3.1):
plugins {
var pluginVersion = "1.2.30"
kotlin("jvm").version(kotlinVersion)
// more
}
var dependencyVersion = "1.2.30"
dependencies {
compile(kotlin("stdlib", kotlinVersion))
compile(kotlin("reflect", kotlinVersion))
testCompile(kotlin("test", kotlinVersion))
// more
}
As you can see, the kotlin version (1.2.30 in this case) is defined twice: dependencyVersion and pluginVersion, which very often does not differ. Due to DSL restrictions, it is impossible to access the pluginVersion from outside the plugins block or access the dependencyVersion from within the plugins block.
How can the version string, "1.2.30" be extracted to a single place?
In later versions of Gradle you no longer need to specify the version of your kotlin(stdlib|reflect|test) dependencies, the Kotlin plugin will automatically configure them for you.
As for extracting the dependency to a single place, there are two main patterns:
define the constants you want to share in an object within buildSrc/src/main/kotlin/ and use that object in your build script, code from buildSrc is available to the whole script including the plugins block
use a system property, you can define a system property in gradle.properties by prefixing its name with systemProp. and you can access system properties via System.getProperties(), for example:
// build.gradle.kts
plugins {
val kotlinVersion by System.getProperties()
println("Kotlin version is $kotlinVersion")
}
// gradle.properties
systemProp.kotlinVersion=1.2.20
What I just stumbled upon was using Kotlin classes ins my build.gradle.kts.
I had to:
create a module called buildSrc with src/main/kotlin and a build.gradle.kts in its root.
(obsolete) include("buildSrc") in settings.gradle.kts
The buildSrc/build.gradle.kts is very minimal:
plugins {
`kotlin-dsl`
}
repositories {
jcenter()
}
In buildSrc/src/main/kotlin I've added a Config.kt
const val GROUP_ID = "my-company"
const val VERSION = "0.1.0-SNAPSHOT"
const val POM_NAME = "my-library-name"
const val POM_DESCRIPTION = "A library doing stuff."
const val POM_URL = "https://github.com/${GROUP_ID}/${POM_NAME}/"
const val POM_SCM_URL = POM_URL
const val POM_SCM_CONNECTION = "scm:git:git://github.com/${GROUP_ID}/${POM_NAME}.git"
const val POM_SCM_DEV_CONNECTION = "scm:git:ssh://git#github.com/${GROUP_ID}/${POM_NAME}.git"
const val POM_LICENCE_NAME = "The Apache Software License, Version 2.0"
const val POM_LICENCE_URL = "http://www.apache.org/licenses/LICENSE-2.0.txt"
const val POM_LICENCE_DIST = "repo"
const val POM_DEVELOPER_ID = "me"
const val POM_DEVELOPER_NAME = "meeee"
const val POM_DEVELOPER_EMAIL = "me#foo.com"
And a Dependencies.kt
#file:Suppress("MemberVisibilityCanBePrivate")
object Jvm {
const val version = "1.8"
}
object Kotlin {
const val version = "1.3.50"
const val stdlibJdk8 = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$version"
const val jvmId = "jvm"
const val kaptId = "kapt"
}
object MavenPublish {
const val id = "maven-publish"
}
object Arrow {
const val version = "0.10.1"
const val core = "io.arrow-kt:arrow-core:$version"
const val syntax = "io.arrow-kt:arrow-syntax:$version"
const val optics = "io.arrow-kt:arrow-optics:$version"
const val fx = "io.arrow-kt:arrow-fx:$version"
const val meta = "io.arrow-kt:arrow-meta:$version"
}
object Versions {
const val version = "0.27.0"
const val versions = "com.github.ben-manes:gradle-versions-plugin:$version"
const val id = "com.github.ben-manes.versions"
}
So I could use it in my root build.gradle.kts like
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin(Kotlin.jvmId) version Kotlin.version
kotlin(Kotlin.kaptId) version Kotlin.version
id(Versions.id) version Versions.version
id(MavenPublish.id)
}
group = GROUP_ID
version = VERSION
repositories {
mavenCentral()
jcenter()
}
dependencies {
implementation(Kotlin.stdlibJdk8)
implementation(Arrow.core)
implementation(Arrow.syntax)
kapt(Arrow.meta)
}
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = Jvm.version
}
publishing {
publications {
create<MavenPublication>("mavenJava") {
#Suppress("UnstableApiUsage")
pom {
name.set(POM_NAME)
description.set(POM_DESCRIPTION)
url.set(POM_URL)
licenses {
license {
name.set(POM_LICENCE_NAME)
url.set(POM_LICENCE_URL)
distribution.set(POM_LICENCE_DIST)
}
}
developers {
developer {
id.set(POM_DEVELOPER_ID)
name.set(POM_DEVELOPER_NAME)
email.set(POM_DEVELOPER_EMAIL)
}
}
scm {
connection.set(POM_SCM_CONNECTION)
developerConnection.set(POM_SCM_DEV_CONNECTION)
url.set(POM_SCM_URL)
}
}
}
}
}
I am quite happy with this, but when it comes down to automatically increment the version I may fall back to maintain it in the gradle.properties.
Edit:
It is no longer necessary (and allowed) to add buildSrc to the settings.gradle.kts, instead it will automatically get picked up if present.
You can extract the version from the plugin class:
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper
plugins {
kotlin("jvm") version "1.2.0"
}
val kotlinVersion = plugins.getPlugin(KotlinPluginWrapper::class.java).kotlinPluginVersion
Once you've defined a version for the Kotlin plugin all other Kotlin libraries will use the same version, and do not need a specific version set.
So the only place you need to set the version is when defining the classpath for the plugin, e.g.:
buildscript {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31")
}
}
If you then need the version for some other purpose (such as in a resolutionStrategy or just for information) you can get it from org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION
For example:
println("Kotlin version used is ${org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION}")
There's a workaround available, which searches the version defined for the kotlin plugin and assignes this one to the outer variable. The following demonstrates this:
val kotlinVersion: String? by extra {
buildscript.configurations["classpath"]
.resolvedConfiguration.firstLevelModuleDependencies
.find { it.moduleName == "kotlin-gradle-plugin" }?.moduleVersion
}
plugins {
kotlin("jvm").version("1.2.30")
//more
}
The variable kotlinVersion can then be used in the dependencies without further trouble.
Answer for #s1m0nw1 comment (too long for comment):
No you can't use buildSrc/src stuff in buildSrc/build.gradle. I had exactly this problem as I wrote android-based plugin and I need android gradle plugin dependency in buildsSrc but I also declare this dependency in project. So I had two different places and two versions to maintain.
I resolved this by creating gradle.properties file in buildSrc directory.
In it I've created prop androidGradlePluginVersion=3.6.0-rc02
buildSrc/build.gradle:
val androidGradlePluginVersion: String by project
dependencies {
implementation("com.android.tools.build:gradle:$androidGradlePluginVersion")
buildSrc/src/.../Versions.kt:
var ANDROID_PLUGIN = loadAndroidGradlePluginVersion()
Util for props:
val GRADLE_PROPERTIES = "buildSrc/gradle.properties"
val ANDROID_PLUGIN_VERSION_PROP = "androidGradlePluginVersion"
fun loadAndroidGradlePluginVersion(): String {
Properties().apply { load(FileInputStream(GRADLE_PROPERTIES)) }.let {
return it.getProperty(ANDROID_PLUGIN_VERSION_PROP)
}
error("Provide $ANDROID_PLUGIN_VERSION_PROP in $GRADLE_PROPERTIES")
}
Easy and fast workaroud:
In buildscript section set value as system property:
buildscript {
val version = "X.X.X"
System.setProperty("version", version)
}
In plugins section get this property:
plugins {
val version = System.getProperty("version")
}

Resources