How to include the source set output of one subproject into another? - java

I have the following project structure where all three subprojects use the java-library plugin and the root project only has an allprojects block that sets the group and version information.
Root project 'project'
+--- Project ':project-a'
+--- Project ':project-b'
+--- Project ':project-c'
project-a has no dependencies
project-c depends on project-a via api(project(":project-a"))
project-b depends on project-c (and transitively on project-a) via api(project(":project-c"))
What I want to achieve is the following:
When project-c is built via the build task, the jar file that is procduced should include all classes from
project(":project-a").sourceSets["main"].output
Similarly, when project-b is built, the jar should include all classes from both
project(":project-a").sourceSets["main"].output and
project(":project-b").sourceSets["main"].output
What I've tried is writing
project(":project-c").evaluationDependsOn(":project-a")
project(":project-b").evaluationDependsOn(":project-c")
in the root project build.gradle.kts and then using
tasks.jar { from(project(":project-a").sourceSets["main"].output) }
and
tasks.jar {
from(project(":project-a").sourceSets["main"].output)
from(project(":project-c").sourceSets["main"].output)
}
in project-c's and project-b's build.gradle.kts, respectively.
While this solution works, it feels pretty hacky. From my current understandig this should somehow be possible using Gradle configurations. So, my question is if there is any better way to achieve what I want to do?
EDIT:
After some reading, I've realised that the calls to evaluationDependsOn are not necessary if configuring the jar task is done lazily as was done in this example (and described here).

Related

Gradle multi-project build order using Kotlin script

I use Kotlin DSL script (.kts) for building. There the structure of my project is:
Root project 'demo'
+--- Project ':backend'
\--- Project ':frontend'
I need to build project frontend first, than backend. I tried
include(":frontend")
include(":backend)
and
include(":frontend", ":backend")
with and without : in settings.gradle.kts of root project, but still the order of build is alphabetical - backend, than frontend.
View source code on GitHub
Do you have any ideas what is wrong?
There is nothing wrong. If you don't specify any inter-project dependencies, Gradle will execute them in alphabetical order. This should be fine if the two projects are unrelated, as they are now.
But let's say you like to build the frontend (using node) and then include those resources in the backend (using Spring Boot). Then you will need to make the backend depend on frontend project. Then Gradle will honor the dependency graph and build the frontend first.
There are many ways to do that. One is to use the java plugin in the frontend to build a jar file of your frontend resources. You can then make a normal project dependency to it. You could also make a dependency directly into the frontend project's "internal" build tasks, but that is a bit frowned upon. Or you could declare your own artifact, or do a it in a bunch of other different ways.
For the first approach, you can build a jar file of your frontend resources like this:
plugins {
// ...
id("java")
}
java {
// Required to make the jar artifact compatible with your backend, which is configured for Java 1.8
targetCompatibility = JavaVersion.VERSION_1_8
}
tasks.named("jar", Jar::class) {
dependsOn("assembleFrontend")
from("$buildDir/dist")
into("static")
}
Then in the backend, depend on it like this:
dependencies {
// ...
runtimeOnly(project(":frontend"))
}
There are a few other things wrong with your build script as well.
The runtime configuration is deprecated; use runtimeOnly instead (for your spring-boot-devtools dependency).
A multi-project should only have a single settings.gradle file, but you have one in each project. Delete them except for the one in the root folder.
You have declared the org.siouan.frontend plugin twice: once using the recommended way and once using the "old" way. Remove the latter (everything in the buildscript block and the apply statement.
Also, while I am not familiar with the org.siouan.frontend plugin, it appears it does not declare inputs and outputs for you - probably because it is very generic. So to avoid running npm each time you build your backend (as you now have a dependency to the frontend), you should declare proper inputs and outputs for the frontend tasks like installFrontend and assembleFrontend.

How to stop a project dependency being replaced by another project in Gradle?

I have a multi-project setup and I am defining a project as a dependency in the build.gradle file. But that project is being replaced by another project in the same hierarchy. How do I stop this from happening?
Consider the project structure as:
A/B/C1/D/src
A/B/C2/D/src
In the build.gradle file inside C1/D, I have defined the project C2/D as a dependency. But then on calling the build task, the dependency C2/D gets replaced by C1/D which is resulting in the following error:
Circular dependency between the following tasks:
:B:C1:D:compileJava
\--- :B:C1:D:compileJava (*)
The build runs fine if I call :C1:build but on calling the build task the error appears.
On running the :B:C1:D:dependencies I can see the following:
+--- project :B:C2:D -> project :B:C1:D (*)
How can I stop this from happening?
Since the signatures (class names with packages) are the same, gradle takes code from the same project in build task. I would suggest using different names for the code in both projects (for example impl infix).
Otherwise you need to have a lot of resources (time and willingness) and use separate classloaders for the resources explicitly.

JAR added to Gradle project but no entry in build.gradle

I am new to the Java tools and stuff, so please be gentle.
I see that someone has added the Simple Logging Façade 4 Java (SLF4J) logging capability to a project I am looking at in IntelliJ IDEA. The project is a Gradle project and when I see the Project Structure -> Module -> Dependencies, I do see a dependency for the SLF4 jars.
I also see these listed in the External Libraries node in the treeview in the Project window.
However, when I open the build.gradle file, I see no entry for slfj. How is that? How would this library have been added to the project?
slf4j is a transitive dependency of some other library.
Use ./gradlew dependencies on the root folder of your project to see the dependencies graph.
slf4j ist most likely a transitive dependency of one of the declared dependencies.
To list the dependency tree, you can use gradlew dependencies.
To list the dependency tree for a specific configuration, you can use gradlew dependencies --configuration runtime.
In your situation you can also use the other way around and use dependencyInsight task instead like gradlew dependencyInsight --configuration runtime --dependency org.slf4j:slf4j-api to see which declared dependencies depen on the given dependency in the given configuration.

Is it possible to group dependencies in maven?

My goal is to create a distribution package (zip) with the following layout:
- application.jar
- libWebService
- web-service.jar
- libUtil
- guava.jar
...
The application.jarreferences the libraries in the separate lib folders in its MANIFEST.MF. The point with this layout is, that the dependencies are grouped semantically but are all normal maven dependencies defined in the pom.xml of the module that produces the application.jar.
Is there a simple way to create such kind of distrubtion with maven?
I know that I can use the maven-dependency-plugin (https://maven.apache.org/plugins/maven-dependency-plugin/) and its goal copy-dependencies to copy all dependencies of the current module (incl. its transitive dependencies) into a given output folder. With the exclude/include options I could even filter the dependencies and by invoking the plugin multiple times I could create the desired folder structure, although such kind of solutions would not be easy to maintain. But this solution still lacks the feature that the MANIFEST.MF file has to refer to the different folders. The maven-jar-plugin allows me to define one classpathPrefix for the complete jar but not a separate one for each dependency.

Gradle sub-project classes dependency

I've got several sub-projects in my gradle project:
Project
Common
Consumer1
Consumer2
.....
ConsumerN
My first - and main goal – is to include classes from Common project into resulting jar of every ConsumerN projects. So I can develop and test shared part (DB logic, some utils) independently in Common project and next other projects will get this classes (already tested) and include them into their jars.
Second goal is to make IntelliJ Idea to understand such dependency and work with it correctly.
Would you please suggest the "most conceptual and right way" to do this in gradle.
Assume You have the following project structure:
root
build.gradle
common
m1
m2
m3
settings.gradle
First of all You need to set a multimodule project - it's done in settings.gradle (this is a normal gradle script) and its content is as follows:
include 'm1', 'm2', 'm3', 'common'
Per project settings are done in dedicated build.gradle files, but the settings You asked can be done globally - in root build.gradle. Here's its content:
subprojects {
apply plugin: 'java'
}
subprojects.findAll { it.name != 'common' }.each {
configure(it) {
dependencies {
project(':common')
}
}
}
The question is what artifacts are produced from mN modules. If these are jar files You need to use fatjar or shadow plugin. If there are web applications war plugin is what You need.
Some further reading.
IntelliJ IDEA should handle these dependencies while importing the project using gradle wrapper.

Categories