Gradle subprojects: how do I execute `check` only on main project? - gradle

I added a subproject to my gradle project and now whenever I execute the target check, it also runs the tests on my subproject.
I was hoping that I could run the check-task on the main project by specifying it as absolute task as
gradle :check
or
gradle ::check
but both commandlines still execute all tests.
Any idea?

There is no way around this, so you would need to restructure your build (I think check task of the parent is by definition a aggregate of the sub-projects). You can have sub-projects depending on each other, I assume that might be what you want to model in your build.
Let's assume you have the sub-projects core and myModule.
If you want myModule to depend on (and use the output of) core, you add a project dependency to myModule:
dependencies {
compile project(':core')
}
This will also setup the task dependencies correctly.

you can access the task-graph of a subproject from the main projects `build.gradle' and disable tasks.
This way you can disable the task when you call it on the main project, but it will still be enabled when you call the build file of the subproject directly:
project('subproject') {
//disable 'check' and 'test' task
gradle.taskGraph.whenReady {
tasks.find {it.name=="check"}.enabled=false
tasks.find {it.name=="test"}.enabled=false
}
}

Related

Running tasks from an included gradle build in order

I have a gradle composite build project, which runs tasks from an included build. I am not able to make changes to the included build.
For a certain task, I want to first clean the included build and then build it. I.e., I want to do (loosely):
some-project:clean
some-project:build
where some-project is the included build. I tried doing this by setting up tasks which depend on those external tasks:
task cleanIncludedBuild {
dependsOn gradle.includedBuild("some-project").task("clean")
}
task buildIncludedBuild {
dependsOn cleanIncludedBuild
dependsOn gradle.includedBuild("some-project").task("build")
}
However, since there is no ordering between dependencies, this execution order is valid as declared:
some-project:build
some-project:clean
cleanIncludedBuild
buildIncludedBuild
Though this is of course not what I'm after.
I thought of doing something like
gradle.includedBuild("some-project").task("build").dependsOn cleanIncludedBuild
but the value returned by task() above is of type IncludedBuildTaskReference, which essentially are read-only references to the underlying task, and do not allow adding dependencies.

Conditionally skip subproject in multi-module Gradle build

Consider the following Gradle project structure:
- root
|- build.gradle
|- barProject
|- build.gradle
|- fooProject
|- build.gradle
where the root build.gradle configures its subprojects like so:
subprojects {
apply plugin: 'java'
//bunch of other stuff
}
Now when I call gradlew build on the root project it automatically configures both subprojects and then builds them - all well and good.
I also know that I can skip a specific task with various means (onlyIf(), [taskName].enabled = false, etc.) but when I utilize any of those on build Gradle still runs all the dependent tasks (compileJava, processResources, classes, etc.) until finally hitting build which it then skips.
My question is, is there any way to have Gradle stop and go to the next subproject right after the configuration phase?
EDIT:
To clarify; each subproject has a property called skip that is evaluated in the configuration phase.
Now when I call gradlew build on the root project I want Gradle to, ideally, check that property and if it's true then to completely skip the corresponding project.
Executing external task 'build'...
//check project.skip -> IF true -> skip project, go to :foo ELSE continue
:bar:compileJava
:bar:processResources UP-TO-DATE
:bar:classes
:bar:jar
:bar:startScripts
:bar:distTar
:bar:distZip
:bar:assemble
:bar:compileTestJava UP-TO-DATE
:bar:processTestResources UP-TO-DATE
:bar:testClasses UP-TO-DATE
:bar:test UP-TO-DATE
:bar:check UP-TO-DATE
:bar:build
//check project.skip -> IF true -> skip project, go to end ELSE continue
:foo:compileJava
:foo:processResources UP-TO-DATE
:foo:classes
:foo:jar
:foo:startScripts
:foo:distTar
:foo:distZip
:foo:assemble
:foo:compileTestJava UP-TO-DATE
:foo:processTestResources UP-TO-DATE
:foo:testClasses UP-TO-DATE
:foo:test UP-TO-DATE
:foo:check UP-TO-DATE
:foo:build
BUILD SUCCESSFUL
I hope this makes more sense
Well, first of all: An easy solution would be calling exactly the task(s) you need. In simple Gradle builds, task names are unique. However, in multi-project builds, each project can have a task with a specific name. This is the reason why Gradle introduced task paths. Task paths combine the unique project paths with the intra-project unique task names: :projectX:taskY.
Using project paths, you can easily specify the project-specific task you want to execute: :build for the build task in the root project and :<subproject>:build for the build task in a subproject. If a task name, e.g. build, is provided for a multi-project build, Gradle will search through any project (root and subs) for a task with the specified name and execute them all. This explains the current behaviour.
The task names for execution are managed by a StartParameter object of the Gradle Settings. These Settings can be modified in your settings.gradle file, where you also include subprojects:
include ':foo', ':bar'
startParameter.excludedTaskNames += ':foo:build'
This line excludes the build task of the foo subproject from the execution. You could add the subproject task path (even independent from the task name) to the excluded task names, if a specific condition is met. But, I did not find a way to access the Settings in your build file, so this solution can not be used during configuration phase. However, if your condition is solely based on project / system properties, they can be accessed from settings.gradle.
Another solution for the configuration phase came to my mind, but it's basically what you already mentioned, because it simply skips the tasks:
if (project.skip) {
project.tasks.all { task -> task.enabled = false }
}
This way, all tasks from the project (not only the build task) will be skipped.
Please consider, that tasks can also be executed, because they create a dependency for another project. Even gradle :bar:build will execute the task :foo:jar and its task dependencies, if foo is a project dependency of bar. You can disable this behaviour with the Gradle command line options -a or --no-rebuild or the following entry in your settings.gradle:
startParameter.buildProjectDependencies = false

Creating a post build copy task with Gradle

I am struggling with the Gradle build lifecycle; specifically with the split between the configuration and execution phases. I have read a number of sections in the Gradle manual and have seen a number of ideas online, but have not found a solution to the following problem:
I want to run a specific task to produce an artifact at the end of my java-library-distribution build that is a flattened version of the runtime configuration jars. That is, I only want to produce the artifact when I run the specific task to create the artifact.
I have created the following task:
task packageSamplerTask(type: Tar, dependsOn: distTar) {
description "Packages the build jars including dependencies as a flattened tar file. Artifact: ${distsDir}/${archivesBaseName}-${version}.tar"
from tarTree("${distsDir}/${archivesBaseName}-${version}.tar").files
classifier = 'dist'
into "${distsDir}/${archivesBaseName}-dist-${version}.tar"
}
Although this task does produce the required artifact, the task runs during gradle's configuration phase. This behavior has the following consequences:
Irrespective of which task I run from the command line, this packageSamplerTask task is always run, often unnecessarily; and
If I clean the project, then the build fails on the next run because $distsDir doesn't exist during the configuration phase (obviously).
It appears that if I extend the Copy task in this manner I'm always going to get this kind of premature behavior.
Is there a way to use the << closure / doLast declarations to get what I want? Or is there something else I'm missing / should be doing?
Update
After further work I have clarified my requirements, and resolved my question as follows (specifically):
"I want to package my code and my code's dependencies as a flat archive of jars that can be deployed as a jMeter plugin. The package can then be installed by unpacking into the jMeter lib/ext directory, as is. The package, therefore, must not include the jMeter jars (and their dependencies) which are used for building and testing"
Because Gradle doesn't appear to support the Maven-like provided dependency management, I created a new configuration for my package which excludes the jMeter jars.
configurations {
jmpackage {
extendsFrom runtime
exclude group: 'org.apache.jmeter', name: 'ApacheJMeter_core', version: '2.11'
exclude group: 'org.apache.jmeter', name: 'ApacheJMeter_java', version: '2.11'
}
}
And then created the following task (using the closure recommendation from Peter Niederwieser):
task packageSamplerTask(type: Tar, dependsOn: assemble) {
from { libsDir }
from { configurations.jmpackage.getAsFileTree() }
classifier = 'dist'
}
This solution appears to work, and it allows me to use just theGradle java plugin, too.
The task declaration is fine, but the flattening needs to be deferred too:
...
from { tarTree("${distsDir}/${archivesBaseName}-${version}.tar").files }
Also, the Tar file should be referred to in a more abstract way. For example:
from { tarTree(distTar.archivePath).files }
First your task isn't executed in the configuration phase but like EVERY task it is configured in that phase. And your closure is just a configuration of your task (a Configuration closure, not an Action closure). That is why your code is "executed" in the configuration phase".
If you want your code to be executed in the execution phase have to write it in a doLastclosure or doFirst. But in your case it is better to keep it in a configuration closure, because you are configuring your task.
To make sure your build doesn't fail because of the missing folder, you can create it with distsDir.mkdirs().

Gradle - can I include task's output in project dependencies

I have a task that generates java sources and a set of jars from these sources (say, project a). I would like to export these jars to dependent projects (say, project b). So here's roughly what I have right now:
//a.gradle
configurations{
generatedJars
}
task generateJars(type: JavaExec) {
//generate jars ...
outputs.files += //append generated jars here
}
dependencies{
generatedJars generateJars.outputs.files
}
//b.gradle
dependencies{
project(path: ':a', configuration: 'generatedJars')
}
It works OK, except that adding generateJars.outputs.files as a dependency does not tell gradle that it has to run generateJars task when there are no jars generated yet. I have tried adding the task itself as a dependency hoping that it would work in the same way as it does when you add a jar/zip task to an artifact configuration (e.g. artifacts{ myJarTask }), but it throws an error telling me that I cannot do that. Of course I can inject the generateJars task somewhere in the build process before :b starts evaluating, but that's clumsy and brittle, so I would like to avoid it.
I feel like I should be adding the generated jars to artifacts{ ... } of the project, but I am not sure how to make them then visible to dependent projects. Is there a better way of achieving this?
Dependent projects (project b) will need to do setup IntelliJ IDEA module classpath to point to project a's generated jars. Something rather like this (pseudo-code):
//b.gradle
idea{
module{
scopes.COMPILE.plus += project(path: ':a', configuration: 'generatedJars').files
}
}
So far I have tried simply adding a project dependecy on :a's generatedJars in :b, but Idea plugin simply adds module :a as a module-dependency and assumes that it exports its generated jars (which is probably a correct assumption), therefore not adding the generated jars to :b's classpath.
Any help would be greatly appreciated!
First, do you need a separate configuration? That is, do you have clients of a that should not see the generated Jars? If not, you can add the generated Jars to the archives configuration, which will simplify things.
Second, the correct way to add the generated Jars to the configuration is (instead of the dependencies block):
artifacts {
generatedJars generateJars
}
This should make sure that the generateJars task gets run automatically when needed.
Third, I'd omit the += after outputs.files, although it might not make a difference. You should also add the necessary inputs.
Fourth, why do you need a JavaExec task to generate the Jars? Can you instead add the generated sources to some source set and let Gradle build them?
Fifth, IDEA doesn't have a concept corresponding to Gradle's project configuration dependencies. Either an IDEA module fully depends on another module, or not at all. You have two options: either use a module dependency and make the generated sources a source folder of the depended-on module (preferably both in the Gradle and the IDEA build), or pass the generated Jars as external dependencies to IDEA. In either case, you should probably add a task dependency from ideaModule to the appropriate generation task. If this still doesn't lead to a satisfactory IDEA setup, you could think about moving the generation of the Jars into a separate subproject.
For my use case, I had a C++ project which generated some native libraries which my java project needed to load in order to run.
In the project ':native' build.gradle:
task compile(type: Exec, group: 'build') {
dependsOn ...
outputs.files(fileTree('/some/build/directory') {
include 'mylib/libmy.so'
})
...
}
In project java application build.gradle:
configurations {
nativeDep
}
// Add dependency on the task that produces the library
dependencies {
nativeDep files(project(':native').tasks.findByPath('compile'))
}
// Unfortunately, we also have to do this because gradle will only
// run the ':native:compile' task if we needed the tasks inputs for another
// task
tasks.withType(JavaCompile) {
dependsOn ':native:compile'
}
run {
doFirst {
// Use the configuration to add our library to java.library.path
def libDirs = files(configurations.nativeDep.files.collect {it.parentFile})
systemProperty "java.library.path", libDirs.asPath
}
}

Gradle - Add additional task to existing task

I'm working on a project that uses EJB2s. The created EJB Jars require additional processing by the application server before they're bundled in the war/ear and deployed.
I have created a custom task that works to do the additional processing if I invoke it explicitly (gradle ejbDeploy), but am having trouble fitting it into the gradle multi-project lifecyle. I need to somehow add it to the build graph to execute automatically after the jar task.
My first attempt was to add it to jar with
jar.doLast{
ejbDeploy.execute()
}
which seems to work for arbitrary code blocks, but not for tasks
What's the recommended solution for this? I see three approaches:
Hook into the build graph and add it explicitly after the jar
task.
Set it up somehow in jar.doLast{}
Set it up as a prerequisite for the WAR task execution
Is there a recommended approach?
Thanks!
I would go for approach #3 and set it up as a dependency of the war task, e.g.:
war {
it.dependsOn ejbDeploy
...
}
I'm new to Gradle, but I would say the answer really depends on what you're trying to accomplish.
If you want to task to execute when someone runs the command gradle jar, then approach #3 won't be sufficient.
Here's what I did for something similar
classes {
doLast {
buildValdrConstraints.execute()
}
}
task buildValdrConstraints(type: JavaExec) {
main = 'com.github.valdr.cli.ValdrBeanValidation'
classpath = sourceSets.main.runtimeClasspath
args '-cf',valdrResourcePath + '/valdr-bean-validation.json'
}
Add the following, and then ejbDeploy will be executed right after jar, but before war
jar.finalizedBy ejbDeploy
See Gradle Docs. 18.11. Finalizer tasks

Resources