What does kotlin-bom library do? - gradle

I've created new Kotlin project under Gradle. By default it sets this dependencies to the Kotlin-library project. And I wonder what does this kotlin-bom lib do ?
dependencies {
// Align versions of all Kotlin components
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
// Use the Kotlin JDK 8 standard library.
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// Use the Kotlin test library.
testImplementation("org.jetbrains.kotlin:kotlin-test")
// Use the Kotlin JUnit integration.
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
}

The kotlin-bom artifact is a dependency-only POM that aligns all the Kotlin SDK libraries with the same version.
See the POM content for version 1.5.31 for an example. It uses the POM's dependencyManagement section to depend on the same version of all the Kotlin SDK artifacts, like kotlin-stdlib, kotlin-stdlib-jdk8, kotlin-reflect, kotlin-test, etc.
Description of a BOM:
Software bill of materials (BOMs) don’t specify a dependency on a
module or file, but instead are a list of version constraints for
other components. They define what is called a platform, which is
basically a list of components with specific versions that are known
to play well together and/or form a useful unit of functionality. It’s
worth mentioning that not all of the dependencies listed in the BOM
actually have to be included in your projects — it’s basically a way
of saying “If you use any of these modules, use this version”.
-- A deep dive into an initial Kotlin build.gradle.kts

Related

Defining common dependencies and plugin versions for different projects using Gradle Kotlin DSL

I have a collection of related Gradle projects, not all of them in the same repository (hence not sharing the same gradle.properties or settings.gradle.kts files).
My objective is having my main build scripts (build.gradle.kts files) completely clean from dependency version numbers and at the same time avoiding to specify the same version number in more than once settings or properties file.
I am looking for the best practices to accomplish this, taking into consideration that these version numbers can appear both in Gradle dependencies or plugin declarations and often need to be kept in sync between them.
As a use case, lets configure projects that require spring boot.
In this case, I can use a Gradle platform project to define common dependency versions. As an example, the build.gradle.ktsof my platform project looks a bit like this:
plugins {
`java-platform`
}
javaPlatform {
allowDependencies()
}
dependencies {
api(platform("org.springframework.boot:spring-boot-dependencies:2.3.0.RELEASE"))
constraints {
api("io.mockk:mockk:1.10.0")
api("org.junit.jupiter:junit-jupiter:5.6.2")
api("io.kotest:kotest-runner-junit5-jvm:4.0.6")
...
}
}
So I am conveniently specifying all the spring boot dependencies + my own dependencies in one single platform project.
But other projects which use spring boot dependencies also require the org.springframework.boot plugin, where its version should better be the same than the spring boot dependency required in the platform project.
As said at the beginning, those projects are located in other repositories, hence they do not share the same configuration files as the platform project.
In those projects I am adding in their settings.gradle.kts file the following block:
pluginManagement {
plugins {
id("org.springframework.boot").version("2.3.0.RELEASE")
...
}
}
It works, since after importing the platform project I can keep my main build file clean from version numbers, as intended (neither the spring boot dependencies nor the spring boot plugin itself will require anymore to specify their versions).
However, this just does not feel good. Now I have the same version of spring boot (2.3.0.RELEASE) hard-coded in both my platform project and in the settings file of the project using the Spring boot plugin. Now I have the problem of keeping both numbers, located in distinct projects, synchronized.
Which is the "correct" Gradle way to solve this? Is there a way to make a Gradle platform project to also specify plugin version numbers in a similar way preferred dependency versions can be declared there?

Gradle dependency scopes

I was experimenting a little bit with Gradle but as mainly being a Maven user, some dependency scopes are confusing me …
Some are identical to Maven:
compile -> compile
runtime -> runtime
compileOnly -> provided
but I also encounter implementation which sounds like the parent element of Maven POM but then again also not.
Can somebody explain me what is implementation and some other if they exist and I didn’t mention here (test versions of above are clear no need to explain)?
And if implementation is not like parent on Maven, how can we have the parent POM effect of Maven in Gradle?
As commented, please have a look at the documentation or even at this recent webcast (disclaimer: I am co-presenting that webcast)
As for the Maven comparison, view migrating / learning from Maven to Gradle the same as moving from subversion to git: while some vocabulary is the same, understanding the model of the later helps more than comparing.
In short:
Do not use compile or runtime in Gradle, they are deprecated.
implementation relates to dependencies that are required to compile and run your application.
compileOnly and runtimeOnly should be self-explanatory in the context of the above
The java-library plugin adds the api configuration which is reserved for dependencies that consumers of your library will need to compile.
There is no direct equivalent to a Maven parent pom. Whether you are talking about plugins, build config or dependencies, the way to centralize are different. Have a look at the documentation on multi-project authoring.

Missing Dependency in dependent project

I have an android project with following structure:
-- Calendar
------app (app module)
----------build.gradle (module level)
------build.gradle (project level)
------Commons(A common project which i reuse across various projects)
----------common (common module in Commons project)
-------------build.gradle (for only common module level)
----------build.gradle(for Commons Project)
Now the problem is that if I compile Commons, deploy it to bintray and then use it as implementation 'com.amitkma.Commons:common:1.0.0' in app module, all the dependencies (which are implemented in common build.gradle) is available to use in app module also. But if I use it like following
implementation project(:Commons:common), only dependencies provided using api is available to use.
I want to know what is the difference between api and implementation with respect to module is compiled or used directly like above?
There are two things at play here:
The separation of api and implementation when declaring dependencies. In short dependencies in the implementation scope are only visible at runtime because they are an implementation detail. Such libraries will use the runtime scope in their published Maven metadata. More information in the documentation
Before Gradle 5.0, the Maven metadata scopes compile and runtime were mixed by Gradle and thus all runtime dependencies did appear on the compile classpath. Gradle 5 changes this
So when you publish your component, the limitation of Gradle 4.x means that your common dependencies are available to app. Note that moving to Gradle 5 will cause a breakage there, as documented.
And when you use the project directly, the separation is properly enforced.
The fix is to simply promote dependencies that are part of the common api to the api configuration in common and for the runtime ones, declare them in app as after all they are directly required by it.

How Springboot lock the libs' version, like Rails's Gemfile.lock?

I understand there is library dependency management in Springboot so that the correct version of starter kits will be picked for the Springboot version. However I do not fully understand how the third-party libs' versions are controlled.
For example, in build.gradle file, we can use a lib like this:
implementation('org.liquibase:liquibase-core') .
I know that Gradle's logic is to pick the latest version if no version is specified. I am not sure if there are any tools like a lock file to lock down ALL the versions used by this application, or we have to specify the version like:
compileOnly 'org.projectlombok:lombok:1.18.4' ?
So that we can be confident that all the libs used in the team are identical.
The best practice recommended by gradle is to declare dependencies without versions and use dependency constraints.
dependencies {
implementation 'org.liquibase:liquibase-core'
}
dependencies {
constraints {
implementation 'org.liquibase:liquibase-core:3.6.2'
}
}

What is the difference between an app dependency and a module dependency/plugin?

When using some 3rd party libraries, I add a dependency to my module's build.gradle file.
compile 'com.android.support:appcompat-v7:24.1.1'
Or I add a plugin
apply plugin: 'com.neenbedankt.android-apt'
Some other times, the library requires adding a dependency to my app's build.gradle file.
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
What is the difference between these dependencies and plugins?
Why can't they all be set in a single build.gradle file?
All suggestions are appreciated, I'm having trouble searching for info on this
Three things. Gradle plugin, module dependency, a build dependency which is placed on the classpath of the build tool.
A plugin is how Gradle knows what tasks to use. There are many plugins. For more info, see Gradle - Plugin Documentation
A dependency is a library that is compiled with your code. The following line makes your module depend on the Android AppCompat V7 library. For the most part, you search Maven or Jcenter for these.
compile 'com.android.support:appcompat-v7:24.1.1'
The classpath setting is needed for Gradle, not your app. For example, this allows this includes the Gradle Build Tools for Android into the classpath, and allows Gradle to build apps.
classpath 'com.android.tools.build:gradle:2.1.2'
Why can't they all be in one build.gradle file?
They probably can be. It is simply more modular to not.
I got this answer from a colleague, and this helped me understand. "A gradle plugin is like the tools you use to build the app. The dependencies are the libraries included in the app. A gradle plugin is usually the tasks - like ktlint, etc."
I didn't understand this myself so here is what i found. My answer is based on gradle build tool.
Plugins:
Add additional tasks, repositories, new DSL elements, configuration for classpaths/build/run or dependency management for subsequent development. Plugins are developed for a larger scope of development like java, kotlin or spring-boot.
Dependencies:
modules/libraries for tasks like http, serialization or database are dependencies stored remotely at repositories or locally that are needed at runTime, test or build are resolved by gradle in a configured fashion.
Sources:
Spring boot gradle plugin: https://github.com/spring-projects/spring-boot/tree/master/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin
Gradle documentation on plugins/dependencies: https://docs.gradle.org/current/userguide/plugins.html
https://docs.gradle.org/current/userguide/core_dependency_management.html
Remote repositories:
https://mvnrepository.com/
In simple words:
Plugins are used to add some additonal features to the software/tools(like Gradle). Gradle will use the added plugins at the time of building the App.
Dependecies are used to add some addtional code to your source code, so a dependency will make some extra code (like Classes in Java) in the form of library available for your source code.

Resources