Introduction
The most boring part of a library is the setting up part. Choosing the build systems, publishing builds, setting up code coverage, linting, etc etc. This blog is aimed at abstracting those concerns in the form of a simple file at the end of all this.
Choosing a build system
- Gradle
- Maven
Both Gradle and Maven have their own pros and cons. It is not in the scope of this article to delve into those details. However, I'd mention that Gradle tends to be a little bit faster while Maven has better support (in terms of IDE or plugins).
In this blog, we will only deal with Gradle.
Initial set-up
For a Kotlin project, our initial Gradle file looks something like this:
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.72'
}
group 'com.cueo'
version '1.0-SNAPSHOT'
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
Few pointers about the above configuration:
-
plugins
In order to build your Kotlin project with Gradle you need to set-up thekotlin-gradle
plugin. This is done by the line:id 'org.jetbrains.kotlin.jvm' version '1.3.72'
. (1.3.72
is the latest version at the time of writing). -
repositories
This is where you configure if you want to download the artifacts from Maven central, a Nexus proxy or your organisation's specific Nexus. Do NOT forget to addmavenLocal()
. This speeds up loading of the dependencies even if you do not have them cached in thebuild
folder. If some other project has a same dependency, it loads the dependency from Maven's.m2
folder. -
kotlinOptions.jvmTarget
insidecompileKotlin
andcompileTestKotlin
ensures the target JVM.
Linting
A linter forces (and sometimes formats) the code to follow a coding style. If you work on a project with several team members, linting ensures the project follows a specific coding style, eg: use spaces instead of tabs, space before brackets and braces, space after commas, etc.
Let's enable ktlint in our project.
An anti-bikeshedding Kotlin linter with built-in formatter.
plugins {
...
id 'org.jlleitschuh.gradle.ktlint' version '9.2.1'
}
That's it! Now you can run:
-
gradle ktlint
to check for errors. -
gradle ktlintFormat
to auto fix some of the fixable errors.
Adding the plugin also automatically attaches itself to the build
lifecycle.
Code coverage
We will use Gradle's Jacoco plugin for checking code coverage. Start by adding Jacoco to the list of plugins.
plugins {
...
id 'jacoco'
}
You can also make sure each every time gradle test
runs, a coverage report is generated by Jacoco.
test {
finalizedBy jacocoTestReport
}
Enable or disable Jacoco XML and HTML reports as follows:
jacocoTestReport {
reports {
xml.enabled true
html.enabled true
}
}
Publish to Nexus
Eventually you'd want to publish your library to Nexus so that other projects can add your library as a dependency. We will use Gradle's maven-publish
plugin to do that. You can find detailed description of the plugin here.
Add the plugin to build.gradle
:
plugins {
...
id 'maven-publish'
}
Configure the plugin:
publishing {
publications {
mavenJava(MavenPublication) {
artifactId = 'awesome-kotlin-lib'
from components.java
pom {
name = 'Awesome Kotlin Library'
description = 'An awesome library written in Kotlin built using Gradle.'
}
}
}
repositories {
maven {
def snapshotsRepoUrl = 'https://your-nexus-url.com/snapshots'
def releasesRepoUrl = 'https://your-nexus-url.com/releases'
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
credentials {
username = System.getenv('NEXUS_USERNAME')
password = System.getenv('NEXUS_PASSWORD')
}
}
}
}
The above configuration makes some assumptions about the maven repository:
- The URLs for SNAPSHOT and RELEASE builds are different. You might as well use the same endpoint for both SNAPSHOT and RELEASE builds.
- Your system or CI is configured with the Nexus username and password. Locally you can just do the following before running
gradle publish
:
export NEXUS_USERNAME='username'
export NEXUS_PASSWORD='unhackable-password'
Now gradle publish
will publish your jar to the Nexus given you have sufficient permissions.
Releases
maven-publish
is incomplete without a release plugin. While the former publishes the jar to the Nexus, the latter is responsible for bumping up the versions in Git and creating relevant tags. We will use gradle-release
plugin by ResearchGate. As usual, start by adding the plugin.
plugins {
...
id 'net.researchgate.release' version '2.6.0'
}
Configure the plugin with your Github username and token.
release {
svn {
username = System.getenv('GITHUB_USERNAME')
password = System.getenv('GITHUB_TOKEN')
}
}
Make sure each gradle release
is followed by publishing the new build to the Nexus.
tasks {
afterReleaseBuild {
dependsOn publish
}
}
Conclusion
In the end our build.gradle
file should look something like this:
plugins {
id 'org.jlleitschuh.gradle.ktlint' version '9.2.1'
id 'org.jetbrains.kotlin.jvm' version '1.3.72'
id 'maven-publish'
id 'jacoco'
id 'net.researchgate.release' version '2.6.0'
}
java {
withSourcesJar()
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
implementation 'org.slf4j:slf4j-api:1.7.30'
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.3.72'
testImplementation 'org.jetbrains.kotlin:kotlin-test:1.3.72'
testImplementation 'org.junit.jupiter:junit-jupiter:5.6.0'
testImplementation 'io.mockk:mockk:1.9.3.kotlin12'
}
group = 'com.cueo'
version = project.properties['version']
publishing {
publications {
mavenJava(MavenPublication) {
artifactId = 'awesome-kotlin-lib'
from components.java
pom {
name = 'Awesome Kotlin Library'
description = 'An awesome library written in Kotlin built using Gradle.'
}
}
}
repositories {
maven {
def snapshotsRepoUrl = 'https://your-nexus-url.com/snapshots'
def releasesRepoUrl = 'https://your-nexus-url.com/releases'
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
credentials {
username = System.getenv('NEXUS_USERNAME')
password = System.getenv('NEXUS_PASSWORD')
}
}
}
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
test {
useJUnitPlatform
finalizedBy jacocoTestReport
}
jacocoTestReport {
reports {
xml.enabled true
html.enabled true
}
}
release {
svn {
username = System.getenv('GITHUB_USERNAME')
password = System.getenv('GITHUB_TOKEN')
}
}
tasks {
afterReleaseBuild {
dependsOn publish
}
}
That's all, folks! You can now go ahead and write tons of libraries helping out all the developers out there.
Cheers!
Top comments (0)