Gradle shadowjar packages all submodules into a single jar - kotlin

Original issue: https://github.com/johnrengelman/shadow/issues/744
I recently converted my project to a multi-module project, but I'm having trouble using shadowjar's minimize feature. Here is my project structure:
Root project 'shadow-jar-test'
+--- Project ':api'
\--- Project ':core'
This is a multi-module project, core depends on the api module, and both core and api have some third-party dependencies(For example Kotlin std library). And, I implemented these 2 modules in the root project's build script and want to package them into a jar.
The problem is, when I add minimize() to the shadowjar task, it reduces all my code, it seems that it thinks that the subproject is also a third-party dependency.
shadowJar {
relocate 'kotlin', 'lib.kotlin'
relocate 'org.jetbrains', 'lib.org.jetbrains'
// minimize will remove the :core and :api module?
minimize()
}
I also tried add exclude statement into minimize:
minimize {
exclude(project(":core"))
exclude(project(":api"))
}
This will indeed package core and api, but the third-party libraries that core and api depend on are still not packaged
Shadow Version
7.0.0
Gradle Version
7.1
Expected Behavior
Properly minimize dependencies
Actual Behavior
It also minimized my project code?!
Gradle Build Script(s)
I created a example project to reproduce it:
https://github.com/re-ovo/shadow-jar-test
This is the root project build script:
https://github.com/re-ovo/shadow-jar-test/blob/master/build.gradle

Related

Skeleton for Kotlin/native multiproject with dll library for backend and frontend app using it

I'm trying to create a multiproject using Kotlin/native and gradle in IDEA that consists of:
A backend subproject library. I want to use this library in frontend Kotlin app and also produce a native DLL that can be later used in other software. I doubt I'll need any platform specific behavior -- the most I'll interact with the system is read, write and watch a file for changes.
A frontend jvm app in Kotlin using this library as required dependency. To be more precise I'm going to write a glfw app in Kotlin that will use this lib, but that's a detail you don't have to bother with.
I want to be able to:
build DLL on it's own
build an app that depends on library and rebuilds if needed when the lib changed.
I made a hyperlink trip over gradle docs, JetBrains examples and repos but I don't quite understand how to make a multiproject like that. Can someone provide a minimal working example of such a Hello World project?
Right now this works for me in the initial stage of the project:
Use gradle init with basic type and kotlin DSL to generate a wrapper project
Add 2 modules with New > Module > Gradle > Kotlin/Multiplatform and Kotlin/JVM. That should add include("...") entries to root settings.gradle.kts At this point those modules will be empty with a single build.gradle.kts scripts
Root build.gradle.kts can be deleted - both library and app use their own
In the Multiplatform library setup at least one target for example like that:
// rootProject/library/build.gradle.kts
// ...
kotlin {
jvm() // used by jvm app
sourceSets { /*...*/ }
}
Now there should be no gradle errors and IDEA will detect the project properly - refresh gradle configuration (CTRL+SHIFT+O)
Create source directories for each module (because the modules rn): IDEA should hint the names of corresponding source sets (src/<target>/<kotlin|resources> etc.)
So now just to link the app and library together in the the app's buildscript add the implementation(project(":library")) dependency
Of course don't forget to configure the library target for example using linuxX64("native") { binaries { sharedLib {/*...*/ } } } block in kotlin plugin when trying to generate a DLL
Now it should mostly work. The structure of a project I'm working on rn looks like that:

How to add dependencies to a kotlin library?

I am trying to build a kotlin library for discord bots, which can be found at https://github.com/TheDrone7/discord-kt , published to jcenter (bintray link - https://bintray.com/thedrone7/discordKt/discord-kt). The library has a few dependencies of it's own as well.
When I add my own library to my test app, the library's dependencies were not installed and I started getting some errors. Is there a way to specify the library's dependencies so that they get automatically installed when a user uses my library?
EDIT: -
So basically my test app's build.gradle.kts file's dependencies section is given below
dependencies {
// Use the Kotlin JDK 8 standard library.
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.theDrone:discordKt:0.0.1a")
// Use the Kotlin test library.
testImplementation("org.jetbrains.kotlin:kotlin-test")
// Use the Kotlin JUnit integration.
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
}
And my library is dependent on the following packages: -
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.0
org.java-websocket:Java-WebSocket:1.4.0
com.beust:klaxon:5.0.5
org.slf4j:slf4j-jdk14:1.7.26
now when I run my test app, it shows gives error that there is no class named WebSocketClient which is a part of the org.java-websocket:Java-WebSocket:1.4.0 package and is also the base of my entire library.
When I add the listed packages to my test app's dependencies, it works perfectly fine. So is there a way that I could define in my library that the apps using it will also automatically depend on the packages my library depends on?
You declared the Java-WebSocket library as a dependency of your library using the implementation configuration.
This configuration means: I need that for my code to work, but it's an implementation detail and it's thus not part of my public API, so users of my library won't have access to it in their compile classpath.
So Gradle, when it generates the pom.xml file for your library, adds Java-WebSocket as a runtime dependency, and not as a compile dependency.
Read the java-library plugin documentation, which explains all of that in details. Once you have understood it, use api instead of implementation in your library's build.gradle.kts file for the dependencies that are part of your API, and should thus be compile dependencies and not runtime dependencies:
api("org.java-websocket:Java-WebSocket:1.4.0")

Access classes from other module

I have an minecraft forge workspace that is made by gradle, I have an project in which I have an module with the mod I'm working on and one module with my library mod, I want to somehow access classes from my library mod from the other module, I imported modules by using their build.gradle and now I have 2 different modules but I can't access one module from another one.
You need to add a dependency from the working module on the library module. If this is a multi-module Gradle build where both projects are modules, you can simply add a project(':library') dependency. If those are separate Gradle builds, you need to add a normal dependency like 'your.group:library-module:1.0' and then either install the library module to some repository like the local maven repository with the mavenPublish plugin to be able to use it from the working module, or use a composite build to replace the dependency by the automatically built result of the library module. For more information on how to do either, you should read the Gradle Userguide.

How to specify library dependencies for an IntelliJ IDEA plugin?

I am developing a plugin for IntelliJ IDEA. The way I am going about this is by creating a plugin project in IDEA, then packaging this into a jar with appropriate META-INF/plugin.xml, and installing the plugin from the jar.
The problem is that I would like to add a dependency on org.scala-lang:scala-library:2.11.0. I have this specified as a library dependency in the IDEA project, but this information never seems to get passed along to the generated JAR.
How can I include this information in such a way that IntelliJ IDEA will recognize it?
As far as I understand, you want to bundle some library (e.g. scala library) with your plugin.
This is pretty simple.
Go to Project Settings, select module and go to Dependencies tab. Set scope for the library you want to bundle to 'Compile'. In this example it is 'checker-framework' library. 'groovy-2.3.6' library will not be bundled due to its scope set to 'Provided'. Save changes.
Prepare plugin for deployment
Then you got plugin, zipped, ready for deployment (uploading to repo or installing locally) in the root of project. It will contain lib folder with all necessary jars.
The officially supported plugin dependency management solution is to use Gradle with the gradle-intellij-plugin, via Gradle's dependencies or the intellij.plugins entry points. If you want to add a dependency on an artifact (ex. hosted on Maven Central), then configure dependencies just as you normally would in a Gradle based project:
buildscript {
repositories {
mavenCentral()
}
}
dependencies {
compile("org.scala-lang:scala-library:2.11.0")
}
The intellij.plugins entry point will add an artifact in the current project, as well as a <depends> tag to your plugin.xml file. To install an external plugin alongside your own, for example if you are using the Plugin Extensions feature (suppose the plugin is hosted on the JetBrains Plugin Repository), use the following snippet:
plugins {
id "org.jetbrains.intellij" version "0.2.13"
}
intellij {
//...
plugins "org.intellij.scala:2017.2.638"
}

How to attach sources to auto-generated Gradle-based dependencies in IntelliJ IDEA 13.0 in a way that will survive next Gradle projects refresh?

Is there a simple way to attach sources to auto-generated Gradle-based dependencies with IntelliJ IDEA 13.0 that won't be erased on next Gradle refresh?
For example, my build.gradle has such entry:
project(":projectName") {
dependencies {
compile files("c:/Program Files (x86)/groovy-2.2.1/embeddable/groovy-all-2.2.1.jar")
// more stuff here
}
}
Thus when I click Refresh all Gradle projects
I get a nice dependency set looking like so:
but there are no sources attached and if I do attach them manually, on next refresh they are erased.
I have sources for many different libraries, sometimes in jar file, sometimes directly in the file system (e.g. my groovy install has sources in c:\Program Files (x86)\groovy-2.2.1\src\).
Some of the dependencies I use can be downloaded from maven central repo, but in my build.gradle all the dependencies are configured to be taken from my local file system.
Thanks!
Konrad
The only easy solution is to get the dependencies straight from a Maven repository (either Maven Central or an inhouse repository). If that's not an option for you, you'll have to configure sources via a hook such as idea.module.iml.withXml or idea.module.iml.whenMerged (after applying the idea plugin to allprojects). You can find details on these APIs in the Gradle Build Language Reference and the Gradle User Guide.