I was looking for a way to create a jar, that contains everything I need to have a fully working app that contains a frontend and a backend. A fatJar was the solution, but how can I create a gradle task in a kotlin/multiplatform project that creates this fatJar? I could not find a solution that worked out of the box. I had to modify and extend the found tasks to get my wanted result.
Asked
Active
Viewed 748 times
1 Answers
2
So here is my task that works for me:
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack
import org.gradle.jvm.tasks.Jar
val kotlinVersion = "1.4.0"
val serializationVersion = "1.0.0-RC"
val ktorVersion = "1.4.0"
plugins {
kotlin("multiplatform") version "1.4.0"
application //to run JVM part
kotlin("plugin.serialization") version "1.4.0"
id("com.github.johnrengelman.shadow") version "6.1.0"
}
group = "com.bantolomeus"
version = "0.1"
repositories {
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") }
mavenCentral()
jcenter()
maven("https://kotlin.bintray.com/kotlin-js-wrappers/") // react, styled, ...
}
kotlin {
jvm {
withJava()
}
js {
browser {
binaries.executable()
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$serializationVersion")
implementation("io.ktor:ktor-client-core:$ktorVersion")
// implementation("com.github.jengelman.gradle.plugins:shadow:6.1.0")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
val jvmMain by getting {
dependencies {
implementation("io.ktor:ktor-serialization:$ktorVersion")
implementation("io.ktor:ktor-server-core:$ktorVersion")
implementation("io.ktor:ktor-server-netty:$ktorVersion")
implementation("ch.qos.logback:logback-classic:1.2.3")
implementation("io.ktor:ktor-websockets:$ktorVersion")
implementation("org.litote.kmongo:kmongo-coroutine-serialization:4.1.1")
}
}
val jsMain by getting {
dependencies {
implementation("io.ktor:ktor-client-js:$ktorVersion") //include http&websockets
//ktor client js json
implementation("io.ktor:ktor-client-json-js:$ktorVersion")
implementation("io.ktor:ktor-client-serialization-js:$ktorVersion")
implementation("org.jetbrains:kotlin-react:17.0.1-pre.148-kotlin-1.4.21")
implementation("org.jetbrains:kotlin-react-dom:17.0.1-pre.148-kotlin-1.4.21")
implementation(npm("react", "17.0.1"))
implementation(npm("react-dom", "17.0.1"))
}
}
}
}
application {
mainClassName = "ServerKt"
}
// include JS artifacts in any JAR we generate
tasks.getByName<Jar>("jvmJar") {
val taskName = if (project.hasProperty("isProduction")) {
"jsBrowserProductionWebpack"
} else {
"jsBrowserDevelopmentWebpack"
}
val webpackTask = tasks.getByName<KotlinWebpack>(taskName)
dependsOn(webpackTask) // make sure JS gets compiled first
from(File(webpackTask.destinationDirectory, webpackTask.outputFileName)) // bring output file along into the JAR
}
tasks {
withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions {
jvmTarget = "1.8"
}
}
}
distributions {
main {
contents {
from("$buildDir/libs") {
rename("${rootProject.name}-jvm", rootProject.name)
into("lib")
}
}
}
}
// Alias "installDist" as "stage" (for cloud providers)
tasks.create("stage") {
dependsOn(tasks.getByName("installDist"))
}
tasks.getByName<JavaExec>("run") {
classpath(tasks.getByName<Jar>("jvmJar")) // so that the JS artifacts generated by `jvmJar` can be found and served
}
// tasks to create an executable jar with all components of the app
tasks.getByName<ShadowJar>("shadowJar") {
val webpackTask = tasks.getByName<KotlinWebpack>("jsBrowserProductionWebpack")
dependsOn(webpackTask) // make sure JS gets compiled first
from(File(webpackTask.destinationDirectory, webpackTask.outputFileName)) // bring output file along into the JAR
archiveBaseName.set("shadow")
mergeServiceFiles()
manifest {
attributes["Main-Class"] = "ServerKt"
}
}
// task to create executable jar during build task
//tasks {
// build {
// dependsOn(shadowJar)
// }
//}
I commented out the last part to not always create the shadowJar (fatJar) during the build process, because that simply needs time and is not needed in my case. The relevant task is the last one that is not outcommented. I also postet my whole build.gradle.kts file because you need some of the imports, plugins and dependencies (everything that is related to shadowJar from jengelman).
I hope that someone else that has the same problem find this helpful.

Bantolomeus
- 51
- 3
-
the gradle code looks quite sophisticated. Is this all necessary for a showcase of a minimum multiplattform project? Less code might be more relevant? Also, could you please share links/docs that helped you find your solution? (or if they didn’t help, that’d be also good to know). Thanks for sharing! :) – Husterknupp Mar 30 '21 at 07:47