How to Make Kotlin Gradle Plugin not Manage Version - gradle

When I use Kotlin Gradle Plugin and consume Kotlin BOM at the same time it seems that the plugin version takes in priority for kotlin-stdlib, how do I make the plugin not manage my dependency?
plugins {
kotlin("jvm") version "1.2.50"
}
dependencies {
implementation(platform("some-bom:0.3")) // this has API dependency to kotlin-bom 1.3.50
implementation(kotlin("stdlib")) // this resolves to 1.2.50 (plugin version) instead of 1.3.50 (BOM version)
}
Project is using Gradle 5.2.1

I can't reproduce it, it looks like a bug. As a workaround you can enforce a particular version by specify isForce = true:
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.2.50") {
isForce = true
}
implementation("org.jetbrains.kotlin:kotlin-stdlib-common:1.3.10"){
isForce = true
}
isForce is not transitive, so you have to specify all the transitive kotlin dependencies explicitly with isForce flag true or create a virtual platform:
open class KotlinAlignmentRule : ComponentMetadataRule {
override fun execute(ctx: ComponentMetadataContext) {
ctx.details.run {
if (id.group == "org.jetbrains.kotlin") {
belongsTo("org.jetbrains.kotlin:kotlin-platform:${id.version}")
}
}
}
}
And add it to your dependencies block:
dependencies {
components.all(KotlinAlignmentRule::class.java)
implementation(platform("some-bom:0.3"))
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.2.50") {
isForce = true
}
}
This last solution is the best imo.

Related

Gradle 7.0 Version Catalog for maven bom

I have published maven bom and imported it in top level build.gradle.kts as:
allProjects {
dependencies {
implementation(platform("com.example:some-dependencies:1.2.3"))
}
}
And then in libs.versions.toml:
[libraries]
some-bom = { group = "com.example", name="some-dependencies", version="1.2.3" }
When I change first code sample to:
allProjects {
dependencies {
implementation(platform(libs.some.bom))
}
}
I get:
Could not resolve: javax.xml.bind:jaxb-api
Could not resolve: org.springframework.boot:spring-boot-starter-test
...
Is there any way to use Gradle 7 version catalogs with boms?
In my case, it just worked. I'm working on Android project and my script is just like below:
//libs.versions.toml
[libraries]
deps_okhttp_bom = "com.squareup.okhttp3:okhttp-bom:4.9.1"
deps_okhttp_lib = { module ="com.squareup.okhttp3:okhttp" }
deps_okhttp_logging_interceptor = { module= "com.squareup.okhttp3:logging-interceptor"}
//build.xml
dependencies {
implementation platform(libs.deps.okhttp.bom)
implementation libs.deps.okhttp.lib
implementation libs.deps.okhttp.logging.interceptor
}
In your example, you just added dependency for BOM. But as BOM is just an spec sheet which describes versions for each libraries, you need to add dependencies for specific libraries.

How to define dependency version only once for whole Gradle multi-module project?

I made a decision to migrate from Dependency Management Plugin to Gradle built-in BOM import support. Since Gradle built-in BOM import support has better performance But
I run into the issue:
I cannot find alternatives for dependency and dependencySet in native Gradle:
dependencyManagement {
dependencies {
dependency("org.springframework:spring-core:4.0.3.RELEASE")
}
}
//or
dependencyManagement {
dependencies {
dependencySet(group:'org.slf4j', version: '1.7.7') {
entry 'slf4j-api'
entry 'slf4j-simple'
}
}
}
and then I could use dependency without version
dependencies {
compile 'org.springframework:spring-core'
}
How can I get the same behavior in naive Gradle? I mean: I'd like to define a version once as I did it when using Dependency Management Plugin
Solution below helps to avoid versions copy-paste. However it isn't the same with Dependency Management plugin.
For Gradle Kotlin Dsl:
You can create buildSrc with you own code, when you can place any constants.
Algorithm:
Create folder buildSrc/src/main/kotlin
Create file buildSrc/src/main/kotlin/Versions.kt with content:
object Versions {
const val junitVersion = "5.5.5" // just example
}
Create file buildSrc/build.gradle.kts with content:
plugins {
`kotlin-dsl`
}
Use the following syntax in your gradle.kts files:
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:${Versions.junitVersion}")
}
For Gradle Groovy:
Create file gradle.properties
Put versions there with syntax like okhttp_version=4.2.0
Use the following syntax in your gradle files:
dependencies {
compile group: 'com.squareup.okhttp3', name: 'okhttp', version: okhttp_version
}
You can do so on the gradle.properties file. I.e.:
# APPLICATION PROPERTIES
name=projectName
group=com.domain
version=1.0.0
description=A brief description
gradleScripts=https://raw.githubusercontent.com/hexagonkt/hexagon/1.2.0/gradle
# DEPENDENCIES VERSIONS
kotlinVersion=1.3.61
kotlinCoroutinesVersion=1.3.2
Or in settings.gradle if you don't want to create another file:
rootProject.name = "hexagon-contact-application"
gradle.rootProject {
allprojects {
version = "1.0.0"
group = "org.hexagonkt"
description = "Contact application backend api"
}
extensions.gradleScripts = "https://raw.githubusercontent.com/hexagonkt/hexagon/1.0.18/gradle"
extensions.kotlinVersion = "1.3.50"
extensions.kotlinCoroutinesVersion = "1.3.2"
extensions.hexagonVersion = "1.0.21"
extensions.logbackVersion = "1.2.3"
extensions.bcryptVersion="0.8.0"
extensions.javaJwtVersion="3.8.2"
}
And if you want to avoid adding the version variable to all related dependencies, you can create a method in the build file:
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.50'
}
apply from: "$gradleScripts/kotlin.gradle"
apply from: "$gradleScripts/service.gradle"
apply from: "$gradleScripts/junit.gradle"
defaultTasks("test")
mainClassName = 'com.hexagonkt.contact.ApplicationKt'
applicationDefaultJvmArgs = ["-Xms64M", "-Xmx2G", "-XX:+UseNUMA", "-XX:+UseParallelGC"]
dependencies {
httpkt(it, "http_server_jetty")
httpkt(it, "store_mongodb")
httpkt(it, "hexagon_web")
implementation("at.favre.lib:bcrypt:$bcryptVersion")
implementation("com.auth0:java-jwt:$javaJwtVersion")
testImplementation("com.hexagonkt:port_http_client:$hexagonVersion")
}
private void httpkt(final def dependencies, final String artifact) {
dependencies.implementation("com.hexagonkt:$artifact:$hexagonVersion")
}

Gradle - access other plugin version

I'm want to obtain other plugin version from my own plugin. I can't find a way to do that via PluginContainer nor PluginManager.
For example for build.gradle.kts:
plugins {
id("some.plugin") version "1.2.3"
id("my.plugin") version "3.4.5"
}
From the code of "my.plugin" I would like to read that "some.plugin" is in version "1.2.3".
If you want to access other plugins versions you could do something like:
plugins {
id("base")
id("org.hidetake.ssh") version "2.10.1"
id("com.jfrog.artifactory") version "4.29.2"
}
apply plugin: MyPlugin
class MyPlugin implements Plugin<Project> {
#Override
void apply(Project target) {
// retrieve plugins dependencies from buildscript classpath
DependencySet pluginDependencies = target.buildscript.configurations.getByName("classpath").allDependencies
// use the found plugins dependencies
pluginDependencies.forEach { Dependency classpathDep ->
println(" Plugin dependency found: $classpathDep.name, version=$classpathDep.version")
}
}
}
This will prompt:
> Configure project :
Plugin dependency found: org.hidetake.ssh.gradle.plugin, version=2.10.1
Plugin dependency found: com.jfrog.artifactory.gradle.plugin, version=4.29.2
> Task :prepareKotlinBuildScriptModel UP-TO-DATE
BUILD SUCCESSFUL in 434ms
nop ... still 2022 this is a 🚧
even google ships tons of lines on it
even your api() declaration (with version range)
nor can I see that dependency constraints will help us
in combination
like
class YourPlugin : Plugin<Project> {
override fun apply(project: Project) {
project
.buildscript.dependencies.constraints
.add("classpath", "com.example:other-plugin") {
it.version {
it.require("[1.0, 2.0)")
}
}
}
}
because you can't add constraints in this gradle phase
ending with
> Cannot change dependencies of dependency configuration ':app:classpath' after it has been resolved.
tldr;
so I don't see anything else than
iterate all project build dependencies checking for version
await depending plugin provides a version
https://docs.gradle.org/current/userguide/toolchains.html is JVM specific and even not addresses this aspect
btw, I was not successful when trying to use the version constraints for comparison
if (project.pluginManager.hasPlugin("com.example.other") == false) {
throw DependencyVerificationException("This plugin needs com.example.other")
}
project
.buildscript
.configurations
.getByName("classpath")
.withDependencies {
it.find {
it.group == "com.example" &&
it.name == "other-plugin"
}
?.version
?.let {
if (DefaultImmutableVersionConstraint.of("1.0") /*no clue how to compare with*/ == it)
else throw DependencyVerificationException("This plugin needs com.example.other at least with version 1.0")
} ?: throw DependencyVerificationException("This plugin needs com.example.other")
}
btw. because it is related: for testing different versions there are indeed some things out there like https://github.com/ajoberstar/gradle-stutter

How do I use the native JUnit 5 support in Gradle with the Kotlin DSL?

I want to use the built-in JUnit 5 with the Gradle Kotlin DSL, because during build I get this warning:
WARNING: The junit-platform-gradle-plugin is deprecated and will be discontinued in JUnit Platform 1.3.
Please use Gradle's native support for running tests on the JUnit Platform (requires Gradle 4.6 or higher):
https://junit.org/junit5/docs/current/user-guide/#running-tests-build-gradle
That links tells me to put
test {
useJUnitPlatform()
}
in my build.gradle, but what is the syntax for build.gradle.kts?
My current build file is
import org.gradle.api.plugins.ExtensionAware
import org.junit.platform.gradle.plugin.FiltersExtension
import org.junit.platform.gradle.plugin.EnginesExtension
import org.junit.platform.gradle.plugin.JUnitPlatformExtension
group = "com.example"
version = "0.0"
// JUnit 5
buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath("org.junit.platform:junit-platform-gradle-plugin:1.2.0")
}
}
apply {
plugin("org.junit.platform.gradle.plugin")
}
// Kotlin configuration.
plugins {
val kotlinVersion = "1.2.41"
application
kotlin("jvm") version kotlinVersion
java // Required by at least JUnit.
// Plugin which checks for dependency updates with help/dependencyUpdates task.
id("com.github.ben-manes.versions") version "0.17.0"
// Plugin which can update Gradle dependencies, use help/useLatestVersions
id("se.patrikerdes.use-latest-versions") version "0.2.1"
}
application {
mainClassName = "com.example.HelloWorld"
}
dependencies {
compile(kotlin("stdlib"))
// To "prevent strange errors".
compile(kotlin("reflect"))
// Kotlin reflection.
compile(kotlin("test"))
compile(kotlin("test-junit"))
// JUnit 5
testImplementation("org.junit.jupiter:junit-jupiter-api:5.2.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.2.0")
testRuntime("org.junit.platform:junit-platform-console:1.2.0")
// Kotlintest
testCompile("io.kotlintest:kotlintest-core:3.1.0-RC2")
testCompile("io.kotlintest:kotlintest-assertions:3.1.0-RC2")
testCompile("io.kotlintest:kotlintest-runner-junit5:3.1.0-RC2")
}
repositories {
mavenCentral()
mavenLocal()
jcenter()
}
(The following is some blabla because this question 'contains mostly code').
I tried to find documentation on how to customize tasks in the Kotlin DSL, but I couldn't find any. In normal Groovy you can just write the name of the task and then change things in the block, but the Kotlin DSL doesn't recognise the task as such, unresolved reference.
Also, this question is related but asks for creating of new tasks, instead of customize existing tasks: How do I overwrite a task in gradle kotlin-dsl
Here is a solution for normal Gradle.
[Edit april 2019] As Pedro has found, three months after I asked this question Gradle actually created a user guide for the Kotlin DSL which can be visited at https://docs.gradle.org/current/userguide/kotlin_dsl.html
They also added a migration guide from Groovy to Kotlin at https://guides.gradle.org/migrating-build-logic-from-groovy-to-kotlin/
Answer:
The syntax you ask for is
tasks.test {
// Use the built-in JUnit support of Gradle.
useJUnitPlatform()
}
which I figured out from this example file from the Kotlin DSL GitHub, or you can use
tasks.withType<Test> {
useJUnitPlatform()
}
which is used in the this official userguide which was created a couple of months after this answer was written (thanks to Pedro's answer for noting this).
But in any case you actually are still using the buildscript block, which is a bit deprecated itself, use the new plugins DSL instead (docs). New build.gradle.kts becomes
group = "com.example"
version = "0.0"
plugins {
val kotlinVersion = "1.2.41"
application
kotlin("jvm") version kotlinVersion
java // Required by at least JUnit.
// Plugin which checks for dependency updates with help/dependencyUpdates task.
id("com.github.ben-manes.versions") version "0.17.0"
// Plugin which can update Gradle dependencies, use help/useLatestVersions
id("se.patrikerdes.use-latest-versions") version "0.2.1"
}
application {
mainClassName = "com.example.HelloWorld"
}
dependencies {
compile(kotlin("stdlib"))
// To "prevent strange errors".
compile(kotlin("reflect"))
// Kotlin reflection.
compile(kotlin("test"))
compile(kotlin("test-junit"))
// JUnit 5
testImplementation("org.junit.jupiter:junit-jupiter-api:5.2.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.2.0")
testRuntime("org.junit.platform:junit-platform-console:1.2.0")
// Kotlintest
testCompile("io.kotlintest:kotlintest-core:3.1.0-RC2")
testCompile("io.kotlintest:kotlintest-assertions:3.1.0-RC2")
testCompile("io.kotlintest:kotlintest-runner-junit5:3.1.0-RC2")
}
repositories {
mavenCentral()
mavenLocal()
jcenter()
}
tasks {
// Use the native JUnit support of Gradle.
"test"(Test::class) {
useJUnitPlatform()
}
}
(Since the Gradle Kotlin DSL has almost no documentation at all except a few (undocumented) example files on GitHub, I'm documenting a few common examples here.)
(Complete example project at GitHub, self-promotion...)
Adding on top of accepted answer, it is also possible to use typed task configuration like:
tasks.withType<Test> {
useJUnitPlatform()
}
Update:
Gradle docs for reference here. Specifically Example 19 which has:
tasks.withType<JavaCompile> {
options.isWarnings = true
// ...
}
this worked for me till now...
plugins {
kotlin("jvm") version "1.7.10"
}
group = "org.example"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
testImplementation(kotlin("test"))
testImplementation("org.junit.jupiter:junit-jupiter:5.9.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0")
}
tasks.test {
useJUnitPlatform()
}
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}

Don't use later library version from transitive dependency in Gradle

in my Android project I use
compile 'com.squareup.okhttp:okhttp:2.2.0'
I need okhttp in version 2.2.0 for my code to work properly. But I have a problem when I add
compile('io.intercom.android:intercom-sdk:1.1.2#aar') {
transitive = true
}
Because inside intercom-sdk there is okhttp dependency again for later version:
compile 'com.squareup.okhttp:okhttp:2.4.0'
Which results that my code uses that later version 2.4.0 instead of 2.2.0 I want. Is there please any way how in my module I can use 2.2.0 which I specified and let intercom to use its 2.4.0?
You can use something like this:
compile('io.intercom.android:intercom-sdk:1.1.2#aar') {
exclude group: 'com.squareup.okhttp', module: 'okhttp'
}
However pay attention. If the library uses methods that are not present in the 2.2.0 release, it will fail.
You should define a resolution strategy to set a specific version. This will guarantee you will get the correct version you wish no matter what the transitive dependency versions are:
allProjects {
configurations.all {
resolutionStrategy {
eachDependency { DependencyResolveDetails details ->
if (details.requested.name == 'okhttp') {
details.useTarget('com.squareup.okhttp:okhttp:2.2.0')
}
}
}
}
}
In newer versions of Gradle you can use:
allProjects {
configurations.all {
resolutionStrategy.force 'com.squareup.okhttp:okhttp:2.2.0'
}
}

Resources