Why some Gradle subproject tasks are accessible from root project? - java

I couldn't find any explanation on this topic, everywhere it's just given as a fact, but I'm interested in how that works.
For example let's take 'build' task, that's provided by 'java' plugin in subproject (but not available in root project).
In root we can call 'gradle build' or 'gradle :mysubproject:build'
eqally and subprojects build task will be called.
However, in root project we cannot write 'build dependsOn
someOtherTask', because it's not available in root - java plugin
isn't added there.
What makes build task available in root? Is there some property that makes task available in parent projects?

If you call Gradle with task names as arguments in a multi-project build, each task with the specified name in any root or subproject will be executed. If you only want to execute a specific task of the given name, use task paths like :mySubProject:build for a subproject or :build for the root project.
Please note, that this has nothing to do with accessing a project in a build.gradle file. In this file, only the tasks of the current project are available. To access tasks in other projects, you could use their task container like rootProject.tasks['build'] or project(':sub').tasks['build']. However, you may run into issues, cause the tasks might not be created when you try to access them.
For task dependencies, you can simply use strings that contain absolute task paths, like dependsOn ':sub:build' or dependsOn ':build'. Using just 'build' will in this context only refer to the task with the specific name in the current project.

The build task is a lifecycle task (ie a task which doesn't actually do anything itself but it can dependsOn other tasks). See base plugins which states the following about the base plugin
Adds the standard lifecycle tasks
If you want the build task in the root project you'll need to add:
apply plugin: 'base'

Related

How to call a Gradle task in all subprojects?

Say, I have a hierarchy of Gradle projects and some of them have java plugin applied:
root
projA
projA1
projA2 (java)
projB
projB1 (java)
projB2
projB21 (java)
projB22 (java)
projC (java)
I want to execute the test task in all subprojects where this task exists: :projA:projA2:test, :projB:projB1:test and :projC:test. Probably I will add more projects in future and I don't want to manually support a list of all test tasks in all subprojects. How can I achieve it?
One thing that came to my mind is something like the following:
// In root I iterate over all subprojects and find the task by name causing
// its creation and configuration
tasks.register("testAll") {
dependsOn subprojects.findResults { it.tasks.findByName("test") }
}
I don't like this approach as it goes against task configuration avoidance style.
Another option is to iterate over subprojects and check if the java plugin is applied there:
// In root
tasks.register("testAll") {
dependsOn subprojects.findAll { it.plugins.hasPlugin("java") }.collect { it.tasks.named("test") }
}
It works but I have a filling that I miss something simpler...
EDIT 1: Sorry for that but I forgot one important detail - I need to run tests in a subtree of projects. Say, everything down the path :projB.
Unless I'm missing something, you want to run tests for all of your submodules.
You can just...do that.
./gradlew clean test
This will run the test task in all of the subprojects that have it sufficiently configured.
If you need to run the tasks in a specific subproject, from the root project you can specify the subproject you want to run the task.
./gradlew clean :projB:test
If your subprojects have a task that needs to run after test, then you can do this in your subprojects block.
subprojects {
myTask.dependsOn("test")
}

File dependency Gradle

I am a gmake user transitioning to Gradle. I have a multi-project structure, where one sub-project is a Java project and the other a home-brewed language. The home-brewed language does not use any Gradle plugins. Now I want to add a task that runs a Java program to generate XML when any of my home-brewed source files have been modified. In make, I would just declare a dependency on inputFile.mine or *.mine next to the target name, but I could not easily find how to do this basic thing with Gradle. Currently, I force the task to always execute using the potentially ugly work-around below. I want to replace this with some dependsOn *.mine . The Gradle user guide has a whole chapter dedicated to explaining different ways of specifying files, but I did not see how to declare a dependency.
task generateXML(type: Exec) {
generateXML.getOutputs().upToDateWhen({false}) // Force it to execute always
executable("java.exe")
args("-jar", "resources/generateXml.jar", "src/inputFile.mine")
}
Thanks for helping a newbie out.
You can define task inputs and outputs in Gradle.
For example:
task generateXML(type: Exec) {
inputs.file ("src/inputFile.mine")
executable("java.exe")
args("-jar", "resources/generateXml.jar", "src/inputFile.mine")
}
See https://docs.gradle.org/current/userguide/more_about_tasks.html and https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/TaskInputs.html for more information.
Side note: When you run your build with -i, Gradle will tell you what has happened during the up-to-date check.

How to embed Java code into Gradle build using JavaExec task

I have a Gradle-driven project to which I want to add a simple Java task. This task is very specific to the project and if it can be helped I don't want to develop it as a separate plugin. So the question is can I define such custom task within the same build.gradle I'm using for my project? Or is it inevitable that I need to package it as a separate project (plugin) and install to the local repo?
Also it's probably important to note that the original project is not Java related (no other Java code needs to be build)
P.S. Based on comments below:
I would like to add src/main/java/SomeUsefulStuff.java to the existing project and have that file compiled and used as a custom task. I do understand that it needs to be compiled each time I run the build but again - the code will be small. However it will have some external dependencies such as Commons IO
Thanks to RaGe who pointed to JavaExec this turned out to be pretty simple. Here's what you do:
Put your Java code in /src/main/java just as you would in the regular Gradle-driven Java project. Make sure it has main method in the file you are going to call
Add apply plugin: 'java' to the build.gradle
If your Java code has any dependencies on 3rd party libs add these to dependencies section
Add new task section to build.gradle like so:
task usefulStuff(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
main = 'com.me.gradle.UsefulStuff'
// arguments to pass to the application
args 'OhmyGod!'
}
Now you can refer to that task as any task in your build. For example imporantTask.dependsOn usefulStuff

Invoking custom tasks in gradle

I have a project with lot of sub-projects. Each sub project has its own build.gradle which contains the different tasks that needs to be run for that sub-project.
Some of the sub projects have their own specific tasks, not found in other sub projects.
Lets say sub project A has taskA and taskB as its specific tasks.
Issuing gradle clean build taskA taskB from the project A's directory invokes the tasks: taskA, taskB.
How can I modify the main build.gradle to invoke taskA and taskB when compiling sub-project A?
You can use the task dependencies for that. All you need is to make your task, which compiles your source, depending on this tasks. Let's assume, you have a compile task in your subproject, then all you need is to make this task depending on taskA and taskB, like:
compile.dependsOn taskA
compile.dependsOn taskB
It should be added into the build.gradle file root for subproject.
You can read about it in the the official user guide

Gradle: how to copy subproject jar to another subproject when task is run?

I have the following multiproject structure in Gradle:
- root-project
- acceptance-tests
- sub-java-proj
acceptance-tests has a task cucumber (provided by Gradle Cucumber-plugin) which runs the Cucumber features in the project. However, I need to do some bootstrapping before the features are run. In particular, before thecucumber task Gradle should do the following:
Run tests in sub-java-proj and if they pass produce its jar file (if not fail)
Copy the jar file from 1) into the directory accentance-tests/proglib
Run the Cucumber features like normal
Delete the jar file copied in 2)
I'm completely new to Gradle so I am not sure how to properly implement this. My first thought was to make acceptance-tests depend on sub-java-proj (with testRuntime config), but I feel this is an abuse of the dependency mechanism: the only reason to do this is to ensure sub-java-proj is compiled before acceptance-tests is run, but in reality this also messes with the classpath of acceptance-tests by adding the jar from sub-java-proj and there's also problems with transitive dependencies. In short: acceptance-tests should not have anything to do with sub-java-proj besides copying it's jar around.
Suggestions on how to accomplish this are very much appreciated.
Update
So far I have the followingin acceptance-tests/build.gradle:
task fix(dependsOn: ':sub-java-proj:jar', type: Copy) {
from tasks.getByPath(':sub-java-proj:jar')
to 'proglib'
}
task unfix(type: Delete) {
delete fix
}
cucumber.dependsOn fix
cucumber.finalizedBy unfix
However, this works except that unfix deletes the proglib directory too. It should only delete the jar file. Any ideas how to fix this?

Categories

Resources