Passing property to Gradle Tooling API ProjectConnection - gradle

I am using gradle tooling api and I encountered the following scenario.
There is a project that applies a certain plugin P which creates a task T only if the property shouldApplyP is passed.
Hence, if you will run ./gradlew tasks --all you won't see task T, but if you will run
./gradlew -PshouldApplyP tasks --all you will see task T.
In gradle tooling api, once a ProjectConnection has created I can do
connection.getModel(GradleProject.class).getTasks()
But I can't see this specific task. Is there a way to pass the project connection this property
-PshouldApplyP so it will be presented in the getTasks() method?

connection.newBuild()
.withArguments("-PshouldApplyP", "tasks --all")
.run()

Related

MyTask getting unwantedly executed when building

I want to print a link to our team's documentation if the developer types:
gradle help
using this task:
task help {
println "Full Documentation"
println "https://confluence.org.com/Help"
}
which it does.
However, I do not want it to execute when I run:
gradle build
Is the help task supposed to run whenever build is run? If not, why does this task get executed? As you can tell, my understanding of gradle is limited.
You are effectively overriding the built-in help task that Gradle provides. As a result, other tasks or plugins in your project may be referencing that task already or other parts in the overall Gradle build rely or use help in some fashion.
To summarize, do not override Gradle built-in tasks otherwise it will lead to unexpected/unintended consequences.
To fix, rename your task to something other than help.

Set task properties (or command line args) using Gradle Tooling API

I want to use the Gradle Tooling API to invoke Gradle from an Eclipse plugin through the Buildship plug-ins. I am able to run basic tasks without problems.
One of my use cases is to execute the gradle init task in a new project folder, but to work non-interactively I have to pass the --type command line argument (or set the type property) on the init task. I can't find any way in the tooling API to set the properties of a task or to pass a task-specific command line argument.
I have tried BuildLauncher.addArgument("--type", "plain") but this is interpreted as an argument to Gradle itself, which is invalid.
How can I pass the --type plain argument to the init task?
After reading the docs here, I discovered you can set the task arguments via the Gradle command line build arguments. The important bit in the docs is:
Also, the task names configured by BuildLauncher.forTasks(String...) can be overridden if you happen to specify other tasks via the build arguments.
In my case I wanted to run gradle tasks --all via the tooling api. To get this working, I don't specify the task to run via forTasks(), I simply do not call that method. I set the task to run as part of the arguments via withArguments(). (In this case the arguments will be tasks --all).
I assume this should work the same for the init task.

Gradle shouldRunAfter not available for a task

I created this repo to reproduce exactly what I'm seeing. I have a project that I'm building with Gradle. I would like to configure my Gradle build so that running:
./gradlew build
Has the exact same effect as running:
./gradlew clean build scalafmt shadowJar fizzbuzz
Meaning Gradle calls tasks in the following order:
clean
build (compile & run unit tests)
scalafmt (run a tool that formats my code to a styleguide)
shadowJar (creates a self-contained executable "fat" jar)
fizzbuzz (prints out "Fizzbuzz!" to the console)
According to the Gradle docs on ordering tasks, it seems that I can use shouldRunAfter to specify ordering on all tasks...
If you clone my repo above and then run ./gradlew build you'll get the following output:
./gradlew build
Fizzbuzz!
FAILURE: Build failed with an exception.
* Where:
Build file '/Users/myUser/thelab/idea-scala-hate-each-other/build.gradle' line: 65
* What went wrong:
A problem occurred evaluating root project 'idea-scala-hate-each-other'.
> Could not find method shouldRunAfter() for arguments [task ':build'] on cz.alenkacz.gradle.scalafmt.PluginExtension_Decorated#6b24ddd7.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 10.983 secs
So even though I specify fizzbuzz to run last...its running first! And there are (obviously) errors with the rest of my config. And I'm not sure how to "hook" clean so that even when I run ./gradlew build it first runs clean.
I'd be OK with a solution that requires me to write my own "wrapper task" to achieve the order I want, and then invoke it, say, via ./gradlew buildMyApp, etc. Just not sure how to accomplish what I want.
Update
I made some changes to build.gradle and am now seeing this:
./gradlew fullBuild
:compileJava UP-TO-DATE
:compileScala
:processResources UP-TO-DATE
:classes
:jar
:startScripts
:distTar
:distZip
:assemble
:compileTestJava UP-TO-DATE
:compileTestScala UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build
:clean
:fizzbuzz
Fizzbuzz!
:scalafmt
:shadowJar
:fullBuild
So when I run ./gradlew fullBuild task execution seems to be:
build (which calls compileJava through check)
clean
fizzbuzz
scalafmt
shadowJar
So the ordering is still wrong even given my most recent changes...
As Oliver already stated, you need to put the console output to a doFirst or doLast closure, otherwise it will be executed when the task is defined (during configuration phase).
The exception is caused by the fact that both extension properties and tasks are added to the scope of the Project object, but if both an extension property and a task with the same name (scalafmt in this case) exist, the extension property will be accessed. As the error message tells you, you are trying to access the shouldRunAfter method on an object of the type PluginExtension, where it does not exist. You need to make sure to access the task:
tasks['scalafmt'].shouldRunAfter build
Update
Actually, I thought that you only need the two specific problems to be solved, but already have a solution for your basic Gradle structure.
First of all, both shouldRunAfter and mustRunAfter do not actually cause a task to be executed, they only define the order if both tasks are executed (caused by command line or a task dependency). This is the reason why the tasks clean, scalafmt, shadowJar and even fizzbuzz are not executed if you call gradle build. So, to solve your first problem, you could let the build task, which you call explicitly, depend on them:
build.dependsOn 'clean', 'scalafmt', 'shadowJar', 'fizzbuzz'
But a task dependency will always run before the parent task, so all tasks will be executed before the build task. This should not be a problem, since the build task does nothing more than collecting task dependencies for all required build steps. You would also need to define the order not only between of your task dependencies, e.g. clean and the parent task build, but mainly between the existing task dependencies, e.g. compileJava. Otherwise clean could run after compileJava, which would delete the compiled files.
Another option would be to define a new task, which then depends on all the tasks you want to execute:
task fullBuild {
dependsOn 'clean', 'build', 'scalafmt', 'shadowJar', 'fizzbuzz'
}
This would still require to define the order between your tasks and the existing task dependencies, e.g.
compileJava.mustRunAfter 'clean'
[...]
Please note, that now you would have to call gradle fullBuild from command line. If you really need to only call gradle build via command line and still execute some tasks after the actual build task, you could use a little trick in your settings.gradle file:
startParameter.with {
if (taskNames == ['build']) {
taskNames = ['clean', 'build', 'scalafmt', 'shadowJar', 'fizzbuzz']
}
}
This piece of code checks the task name input you entered via command line and replaces it, if it only contains the build task. This way you would not have to struggle with the task order, since the command line tasks are executed consecutively.
However, this is not a clean solution. A good solution would include the definition of task dependencies for all tasks that really depend on each other and the invocation of multiple tasks via command line (which you want to avoid). Especially the hard connection between the clean and the build task bypasses a lot of useful features from the Gradle platform, e.g. incremental builds.
Regarding your second point in the update, it is wrong that the fizzbuzz task is running first. It is not running at all. The command line output is printed when the task is configured. Please move the println call to a doFirst / doLast closure:
task fizzbuzz {
doFirst {
println "Fizzbuzz!"
}
}
One of the possible causes of that error could be because Gradle couldn't compile the task of the method shouldRunAfter or mustRunAfter.
Also it could happen when there is another entity of other type (not a task) with the same name of the task of the method shouldRunAfter or mustRunAfter. In this case you could use the syntax tasks['task1_name'].shouldRunAfter tasks['task2_name'] or tasks['task1_name'].mustRunAfter tasks['task2_name'] to make sure Gradle is referencing a task type entity.

How can I set gradle project properties when using gradle tooling API?

I'm trying to use the tooling API to run a gradle task from groovy code. The following works:
ProjectConnection connection = GradleConnector.newConnector()
.forProjectDirectory(new File(System.properties.getProperty('user.dir')))
.connect()
connection.newBuild()
.forTasks('deploy')
.setStandardOutput(System.out)
.run()
But the task I want to run depends on project properties. For example, if I was running it from the command line, I'd use
gradle -Penv=local deploy
I can't figure out how, using the tooling API, to set those project values.
You can do:
connection.newBuild()
.withArguments('-Penv=local')
.forTasks('deploy')
.setStandardOutput(System.out)
.run()

Calling Gradle commands and tasks with parameters from Gradle Task

I have a current setup in my build.gradle that I'm trying to understand. I need a number of tasks to go off in a very specific order and execute all with one task call. The setup I want looks like this:
1.) Run liquibase changeset into predefined database
2.) Run a number of tests against database
3.) Rollback all changes made with the previous changeset
I want the database in a 'clean' state every time I test it. It should have only the changes I expect and nothing else. The liquibase is set up with the Gradle plugin for it and the changeset is applied/updated. However, I don't want to call the command manually. This will be something that needs to run in continuous integration, so I need to script it so I simply have our CI call one task and it then runs each task, in order, until the end. I'm unsure of how to call the Gradle command-line task from inside of itself (ie inside the build.gradle file) and then also pass parameters to it (since I'll need to call some type of rollback command task to get the database to be what it was before calling the update).
Right now, all I'm doing is calling the command line tasks like this:
$ gradle update
$ gradle test
$ gradle rollbackToDate -PliquibaseCommandValue=2016-05-25
Again, I can't call them by the command line alone. I need a custom task inside Gradle so that I could just call something like:
$ gradle runDatabaseTests
...And I would have it do everything I expect.
There is no gradle way to invoke/call a task from another task directly. What you can do instead is to use dependsOn or finalizedBy to setup task dependencies which will force the prereq tasks to run first.
If you declare a task:
task runDatabaseTests(dependsOn: [update, test, rollbackToDate]) << {
println "I depend on update, test and rollbackToDate"
}
when you call
gradle runDatabaseTests -PliquibaseCommandValue=2016-05-25
it will force update, test and rollbackToDate first. You can control the order in which they're run, if you care about that, by using mustRunAfter and/or shouldRunAfter

Resources