GradleBuild changes path for invoked tasks - gradle

In my root build.gradle, I've defined a GradleBuild task like this:
project.task('distribute', type : GradleBuild) {
startParameter = gradle.startParameter.newInstance()
tasks = [":subprojectA:displayInfo"]
}
Given that my root project directory called root,
when I run the distribute task, the task that is executed is
:root:subProjectA:displayInfo
How is it that root becomes what looks like a subproject and why does the gradle build task not execute the correct task like this:
:subProjectA:displayInfo

This is actually not an issue. Even though the gradle output indicates that root is a subproject, the correct task is still executed.

Related

Create task for dependency lock in Gradle >5.0

I want to create a task which to execute
dependencies --update-locks ':'
I had a configuration:
dependencyLocking {
lockAllConfigurations()
}
I try with
task lockDependencies {
dependsOn = ['dependencies','--update-locks *:*']
}
But have:
What went wrong: Could not determine the dependencies of task ':lockDependencies'.
Task with path '--update-locks :' not found in root project
You cannot pass Gradle command line parameters as a task dependency, that's what your error above is about.
The state of writing locks, either with --write-locks or --update-locks, is something that happens really early in the build lifecycle.
You can somewhat control it from a task with the following:
* Create a placeholder task in your build script
* In the settings.gradle(.kts) query the requested tasks from the command line, and if it is there, mutate the start parameters:
if (startParameter.taskNames.contains('placeHolder')) {
startParameter.setWriteDependencyLocks(true)
}
Note that this is not an option if you are trying to lock the classpath of the build itself, which is one of the motivations behind using a command line flag.
Note also that this just allows replacing a flag, like --update-locks *:* with a task invocation like updateLocks but will not work if that task is wired as a dependency of other tasks, as it needs to be requested explicitly. And doing the start parameter mutation after the task graph is computed is too late in the lifecycle.
The best way to do this in my opinion is to add inside the build.gradle file the following code:
dependencyLocking {
lockAllConfigurations()
}
task commitLockDependencies {
'git add /gradle/dependency-locks '.execute()
}
init {
dependsOn('commitLockDependencies')
}
And then inside the settings.gradle the following line:
startParameter.setWriteDependencyLocks(true)
Working with gradle 7.1.

Gradle disable automatic subproject execution for specific task

I have a multi-project Gradle build and I customised the "run" task to do something a bit different in the root project.
However, I don't want it to call the "run" task of each sub-project after completion, as it does now. But this behaviour should only be for this task, I want every other task to be recursively executed as is the default, but the run task not. I also cannot disable the run task globally for every subproject, because it does have a purpose in each subproject when executed on its own.
In the root build.gradle, consider the following (full example here):
gradle.taskGraph.whenReady { graph ->
def hasRootRunTask = graph.hasTask(':run')
if (hasRootRunTask) {
graph.getAllTasks().each { task ->
// look for :abc:run, :def:run etc
def subRunTask = (task.path =~ /:.+:run/)
if (subRunTask) {
println "TRACER skipping ${task.path} because ':run' was specified"
task.enabled = false
}
}
}
}
This will check the task graph for :run. When it exists, then :abc:run (that is, a subproject task) will be disabled.
Example output for root run task:
$ gradle -q run
TRACER skipping :abc:run because ':run' was specified
TRACER skipping :def:run because ':run' was specified
TRACER executing run for path :
Example output for run task in abc subproject on its own:
$ gradle -q :abc:run
TRACER executing run for path :abc

Depend on multiple gradle tasks in multi project build

Currently I have task which starts Google Cloud server, runs tests and stops server. It is defined at root project:
buildscript {...}
allprojects {...}
task startServer (dependsOn: "backend:appengineStart") {}
task testPaid (dependsOn: "app:connectedPaidDebugAndroidTest") {}
task stopServer (dependsOn: "backend:appengineStop") {}
task GCEtesting {
dependsOn = ["startServer",
"testPaid",
"stopServer"]
group = 'custom'
description 'Starts GCE server, runs tests and stops server.'
testPaid.mustRunAfter 'startServer'
stopServer.mustRunAfter 'testPaid'
}
I tried multiple ways to write it something like this, short with only one task. I didn't get how to refer to task from another project and call mustRunAfter on it. This doesn't work (I also tried to refer from Project.tasks.getByPath, root.tasks, etc):
task GCEtesting {
dependsOn = ["backend:appengineStart",
"app:connectedPaidDebugAndroidTest",
"backend:appengineStop"]
group = 'custom'
description 'Starts GCE server, runs tests and stops server.'
"app:connectedPaidDebugAndroidTest".mustRunAfter "backend:appengineStart"
"backend:appengineStop".mustRunAfter "app:connectedPaidDebugAndroidTest"
}
Is it possible? What is correct syntax to make this work?
It looks like your problem is that you're treating dependsOn as meaning "invoke this task". It actually means "ensure the result of the depended on task is available before the dependent task starts". That's why your first solution didn't work: statements like testPaid.mustRunAfter only affect the actions of the testPaid task itself, not its dependencies.
Anyway, you can get the behaviour you want using dependsOn and finalizedBy, but they have to be declared in the build file of the app subproject.
app/build.gradle:
task connectedPaidDebugAndroidTest {
//
//...
//
dependsOn 'backend:appengineStart' // Ensure appengineStart is run at some point before this task
finalizedBy 'backend:appendginStop' // Ensure appengineStop is run at some point after this task
}
Then, you can run your tests simply with gradle app:connectedPaidDebugAndroidTest. If you really want to define a task in the root project to run the tests, then that's easy too:
build.gradle:
task GCEtesting {
dependsOn = "app:connectedPaidDebugAndroidTest"
}

How to call a Gradle subproject’s task from a Gradle test?

I have a project that includes a subproject like so:
Root Project
|----gradle.build
|----SubProject
|----|----gradle.build
The SubProject here contains a copy script that I need called when the root project’s test command is called.
So I have attempted to call the SubProject’s task in the Root project like this:
Task myTest(type: Test) {
Project(‘:SubProject’).tasks.myCopyTask.execut()
}
However, this results in an error, “Could not get unknown property ‘myCopyTask’ for task set.”
Do you know how this call should be done, and what the proper syntax should be?
There a multiple things not working in your example:
You should never call execute on tasks! NEVER! Tasks are called by Gradles task system automatically and calling execute may break this system.
The closure ({ }) you use when creating a task is for configuration. It is not executed when the task gets executed, but when it is created.
Subprojects in Gradle are created and evaluated after the root project is created and evaluated. So tasks from subprojects do not even exist when the root project gets evaluated.
You can solve all these problems by using the dependsOn method with absolute task paths:
task myTest (type: Test) {
dependsOn ':Subproject:myCopyTask'
}

Task with path 'build' not found in root project

I have a multiproject and after the last subproject is built, I'd like to process all jars.
Therefore I created a task in the root-project:
task install(dependsOn: 'build', type: Copy) {
doLast {
println "exec install task"
}
}
Upon calling ./gradlew install in the root directory, I'm facing this error:
FAILURE: Build failed with an exception.
* What went wrong:
Could not determine the dependencies of task ':install'.
> Task with path 'build' not found in root project 'foo'.
However, calling ./gradlew tasks shows me these tasks:
:tasks
------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------
Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
...
How can I achieve the desired functionality?
I assume, that your root project organizes the build, but does not define build action taken by itself. The build task is often defined by language plugins (in most cases via apply plugin: 'java'), but if your root project does not use any of them, it won't have a build task.
The description of the help task tasks, which you used, says:
Displays the tasks runnable from root project 'projectReports' (some of the displayed tasks may belong to subprojects).
The help task followes the same logic as the task activation via the command line. You can provide a task name and any task with the name in any subproject will be executed (thats why gradle build works).
But if you define a dependsOn dependency, the given string is evaluated as task path for a single task. Since each task name can only be used once in a project, the name is unique for tasks in the root project, but many tasks could be found, if subprojects would be considered. Therefor, one can use the syntax :<projectName>:<taskName> to identify tasks in subprojects.
Now, let's face your specific problem: If the install task should depend on the build task of one single subproject, you could use dependsOn ':<mySubproject>:build'. But I assume you want the install task to depend on each subproject build task, so I'd like to propose this approach:
task install(type: Copy) {
dependsOn subprojects*.tasks*.findByName('build').minus(null)
doLast {
println "exec install task"
}
}
This way, for each registered subproject, findByName('build') is called and the result (the found task or null) is put into a list, which is then used as task dependency list. I added the .minus(null) part to remove null entries from the list, because I am not sure how Gradle handles such entries in a dependency collection. If you are sure, that each subproject provides a build task, you can use getByName('build'), too.
EDIT: OP found the optionally recursive getTasksByName method, which suits this case even better than iterating over all subprojects manually:
dependsOn getTasksByName('build', true)

Resources