Modifying gradle task in a subproject - gradle

So, let's say I have this kind of project structure:
Root
projA
projA1-api
projA2-core
projA3-main
projB
projB1-api
projB2-core
projB3-main
projC
projC1-api
projC2-core
projC3-main
and so on...
Some of the subprojects (the ones ending in "-main") have war and spring boot plugins applied to them, the root build.gradle file does not.
That means that projects projA3-main, projB3-main and projC3-main have the bootWar task which disables the war task.
Now, the problem lies within the fact that our jenkins pipeline builds wars by executing the gradle war command, which sucks and can't be changed.
So, what I want to do is to modify the war task in each of the subprojects containing the war plugin by setting it to depend on the bootWar task. I can do this by adding war.dependsOn bootWar into the subprojects' build.gradle files and that works, but I want to extract that code into the root build.gradle.
Also, I want the war task to also execute another task (let's call it customPrintTask) defined in the root build.gradle which just prints stuff into some file.
To summarize:
Root build.gradle doesn't have, war, bootWar or Spring Boot plugins, but has the customPrintTask task
"-main" subprojects have bootWar and spring boot plugins, and they also have the war plugin, but because I am using Spring Boot 2+ gradle plugin, the war task does not generate the war.
Basically, I want something like this:
allprojects {
if (project.plugins.hasPlugin("war")) {
war.dependsOn bootWar
war.finalizedBy customPrintTask
}
}
I hope that makes sense.

Yo, figured it out.
allprojects {
tasks.withType(War) {
if (it.name != "bootWar") {
dependsOn(customPrintTask)
dependsOn(":" + it.project.name.replace("-main", "") + ":" + it.project.name + ":bootWar")
}
}
}
I know no one cares, but this way I iterated over every task with the type War and if the tasks name is not bootWar(because of circular dependency) then depend on my custom print task and also depend on the bootWar task which is located in the subproject I am currently iterating over, which is the reason it looks dumb.
It's pretty simple, not sure how I missed this one...looks extremely ugly, but it works!

Related

Gradle - copy jar to another folder as part of the 'jar' task

I have a multi project gradle setup. I want to simply copy the generated jar file of any project anytime the jar is rebuild and thought this would work in my root project's subprojects closure:
task copyJarToGenerated(type: Copy) {
from jar
into "../my-generated-jars/"
}
copyJarToGenerated.mustRunAfter jar
But if I run the 'clean' task then 'jar' task of any sub project, my jar is generated under build/libs as usual but not copied.
Looking at the gradle output, it only runs compileJava, processResources, classes then jar. It isn't running copyJarToGenerated.
The method mustRunAfter does not define a task dependency, it just enforces a specific order. It basically says 'if both tasks are executed in a build (for whatever reason), then they are executed in the specified order'.
So you need to define the task dependency on your own:
jar.finalizedBy copyJarToGenerated
You could also just add copyJarToGenerated as a dependency of the lifecycle task build:
build.dependsOn copyJarToGenerated
Since you specify the task jar in the method from of your task, it is registered as a task input and therefor registered as a task dependency implicitly. So you won't need to define order with mustRunAfter anymore.
You may also consider using the property destinationDirectory of the task jar instead of creating a new task at all.

Gradle include jar produced by another project in war

Currently I have two projects with gradle build.gradle. The first is going to create a fat jar file, which I would like to include in a war file. I thought compiling it would be enough, but it doesn't seem to be ending up in the /lib directory of my war file. Anyone have thoughts I am quite new to gradle.
dependencies {
compile project(':JarProject')
providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
providedCompile 'org.apache.tomcat:tomcat-jsp-api:7.0.55'
}
war {
archiveName 'WarProject.war'
from 'JarProject/build/libs'
webXml = file('src/web.xml')
}
Does the second project war need to be in providedRuntime? Or should I publish the jar from the other project in the local maven repo and include it that way?
The War task essentially behaves like a CopyTask with regards to stuff it packs in the war, so the documentation on working with files is useful. In essence, I think you need something like (untested):
from fileTree('JarProject/build/libs') {
into("lib")
}
That being said, using mavenLocal() and publishing there also works, but it can lead to unexpected results when the war includes some old version from local, picking up the jar explicitly from the file system like above is better.
I think the elegant solution would be to use multi project builds and project level dependencies. You would have the two builds as separate projects of the same Gradle build and add the "jar project" as a regular compile dependency.
How have you declared the dependency? I assume you have a multi-project build with subprojects A and B, both using the War plugin. I made an experiment using Gradle 2.4 and if I declare B/build.gradle like this:
apply plugin: 'war'
dependencies {
compile project(':A')
}
then B.war contains WEB-INF/lib/A.jar. If you correctly follow conventions of Gradle War plugin (place web resources in A/src/main/webapp/ and code-related resources in A/src/main/resources/), then A.jar should contain what you want.
see this

How to make bootRepackage depends on jar not war when using Gradle War Plugin

Without Gradle War Plugin, bootRepackage task depends on jar task but with Gradle War Plugin, it depends on war task.
How can I change it to depend on jar task even though I'm using Gradle War Plugin?
UPDATE:
I'm using war task to create a war file including documents to be deployed to a documentation server and I want to use bootRepackaged jar file to provide a service. My war task depends on asciidoctor task which depends on test task (I'm using Spring REST Docs.) but I don't want to run asciidoctor task or test task when using bootRepackage task.
I solved my problem with the following setup:
ext {
mainClassName = 'com.izeye.throwaway.Application'
}
task myBootRepackage(type: BootRepackage, dependsOn: jar) {
}
but I'm not sure this is a good practice.
This is a sample project having the above configuration:
https://github.com/izeye/spring-boot-throwaway-branches/tree/war
You should have been able to do this:
bootRepackage {
withJarTask jar
}
While this correctly causes the jar task's jar to be repackaged, it doesn't remove the dependency on the war task. This is another symptom of this Spring Boot issue.
Until this issue has been resolved, the approach that you've taken – declaring your own BootRepackage task and manually configuring the tasks that it depends upon – is your best option.

How to add a war build to a distribution

We have a gradle project that does a distTar to create a tar distribution file.
Each dependency project builds good placing all the dependencies and project jar files into the distribution libs folder.
We need to add a project that builds a war file. By adding the project as a dependency it adds all the war dependencies ... we need just the war file from the project war task.
Also the war project war task is not being executed.
Instead the jar task is being executed from distTar of the distribution project.
How do we execute the project war task and add just the war file to a "gui" folder in the tar distribution?
To get the built war file into the distribution add a task which the distTar depends. Have the depends on task depend on the other project war task. Take the output files and put them in the distribution. The key is to do the into/from in a doLast so the depends on other project war completes before the into/from for the distribution.
// create a task depends on other project war
// put into the distribution gui folder the war.output.files
// the doLast gives visibility to the war.outputs.files
// after the depends on is complete
task otherProjectWar(dependsOn: ':OtherProject:war') {
doLast {
applicationDistribution.into("gui") {
from( project(':OtherProject').war.outputs.files )
}
}
}
// add depends on for distTar
distTar {
dependsOn otherProjectWar
...
}
I believe the proper approach is to include desired task outputs to the distribution. Here is sample script that adds jar built by the project to the main distribution (for war use war outputs instead of jar outputs).
apply plugin: 'java'
apply plugin: 'distribution'
distributions {
main {
contents {
from jar.outputs
}
}
}
Running the distribution task then correctly resolves dependencies for both zip and tar.
> gradlew distTar
:clean
:compileJava
:processResources NO-SOURCE
:classes
:jar
:distTar
You'll need to add a dependency to the output of the war task. By default, ProjectDependency simply adds a dependency to the default configuration of that project.
dependencies {
compile project(':myproject').war.outputs.files
}
Edit: To clarify, compile probably isn't the best configuration to add this dependency to, this is just an example. If indeed all this project is doing is aggregating dependencies into a single distribution archive then the configuration is likely irrelevant.

Gradle batch task that invokes subproject and other tasks in order

I am writing gradle 1.4 build file for multimodule project. So there is root build.gradle that defines something like:
subprojects {
apply plugin: 'java'
...
which defines build task for all submodules. Submodules are included in settings.gradle and each module has its build file with defined dependencies.
Everything by-the-book, so far:) Now, in the main build file I've added some additional project-scope tasks, like: aggregateJavadoc (collects all javadocs into one) or bundleJar (creates bundle jar from all classes), etc. Each on works when invoked manually.
Now I need a task release that will
build all submodules (as invoked from command line - meaning, i dont want to manually write execute() for each submodule)
invoke additional tasks (using execute() I presume).
I tried dependsOn but the order of listed tasks is not followed. Also, dependent modules seems to be executed after release task execution. I tried several other ideas and failed.
Question: what would be the best way to create such batch task, that has to invoke something on all submodules and additionally to perform some more tasks? What would be the best gradle-friendly solution? Thanx!
It happened that this can be solved with simple dependency management.
So, we have many modules. Now, lets create additional tasks that depends on modules being build:
task aggregateJavadoc(type: Javadoc) {
dependsOn subprojects.build
task bundleJar(type: Jar) {
dependsOn subprojects.build
Finally, our release task would simply look like this:
task release() {
dependsOn subprojects.build
dependsOn aggregateJavadoc
dependsOn bundleJar
...
}
This will build subprojects first; not because it is listed first, but because additional tasks depends on building. Order of additional task is not important. This make sense the most to me.
EDIT
if one of your subprojects (i.e. modules) is non-java module, then you will have a problem building this project. What I do is to group submodules, like this:
def javaModules() {
subprojects.findAll {it.name.contains('jodd-')}
}
and then instead to refer to subprojects, use javaModules everywhere! For example:
configure(javaModules()) {
apply plugin: 'java'
...
and
task prj {
dependsOn javaModules().build
}
btw, I am using this 'dummy' task prj for dependsOn on all those additional projects that depends on building, to prevent repeating.

Resources