Gradle task's custom options - gradle

I want to add several options to my custom Gradle task. So I did it:
open class MyTask: DefaultTask() {
#get: Input
#set: Option(
option = "option1",
description = "option1 description.")
var option1 = false
#TaskAction
fun run() {
// proceeding options:
if (option1) println("OPTION 1 IS ACTIVATED")
}
}
If I register this task, I can use command like this and option1 will be equal true:
./gradlew my_task --option1
But I want to add more options in my task. Is there an opportunity not to copy-paste many-many times this code to produce many Boolean options?
I found some Property<Boolean> declaration in Gradle Docs, but I cannot understand how it works, because there is no example at all.

Related

Gradle custom task option without value

I'm developing a custom gradle task and i'd like to have an option which does act like a flag and doesn't require a value.
I just want to check if it is set
Basically : I can use plugin either gradle my-task or gradle my-task --flag and be able to check if --flag is present or not to define plugin processing.
I cannot find any thing in the official documentation
Custom command line options for tasks are available since Gradle 4.6 via #Option annotation on task property setters. Documentation link: Declaring and Using Command Line Options.
According to the documentation, value-less command line options are supported via boolean properties.
boolean, Boolean, Property<Boolean>
Describes an option with the value true or false. Passing the option on the command line treats the value as true. For example --enabled equates to true. The absence of the option uses the default value of the property.
(Untested) Example:
import org.gradle.api.tasks.options.Option;
public class MyTask extends DefaultTask {
private boolean flag;
#Option(option = "flag", description = "Sets the flag")
public void setFlag(boolean flag) {
this.flag = flag;
}
#Input
public boolean isFlag() {
return flag;
}
#TaskAction
public void doWork() {
if (flag) {
getLogger().quiet("Flag is present");
}
}
}
In Kotlin you can write the following:
open class TestTask: DefaultTask() {
#get: Input
#set: Option(
option = "flag",
description = "test option.")
var flag = false
#TaskAction
fun run() {
if (flag) println("FLAG IS ACTIVATED")
else println("FLAG IS NOT ACTIVATED")
}
}
Then you should register this TestTask, and then you can call it with Gradle.
user#laptop:~/test-project$ ./gradlew my_task --flag
> Task :my_task
FLAG ACTIVATED
BUILD SUCCESSFUL in 533ms
1 actionable task: 1 executed
user#laptop:~/test-project$ ./gradlew my_task
> Task :my_task
FLAG IS NOT ACTIVATED
BUILD SUCCESSFUL in 328ms
1 actionable task: 1 executed

Set the properties in the configuration phase of a task in another task

I have spent the last few hours trying to find a solution for my requirement, without luck:
I have a task that has to run some logic in a certain path:
task run(type: MyPlugin) {
pathForPlugin = myPath //Defined as a property in another gradle file
}
I want to set the "pathForPlugin" property dynamically in another task because it has to be read from some configuration file.
task initPaths(type: PathFinder) {
configurationFile = 'C:\\myConfig.conf'
}
The myConfig.conf would look like this:
pathForPlugin = 'C:\\Correct\\Path'
The problem is that "initPaths" has to run before the configuration phase of "run".
I have tried several approaches for this (GradleBuild task, dependsOn, Using Properties in the Plugin for "Lazy Configuration") but every approach only takes effect in the Execution phase leading to the "pathForPlugin" always staying at its default value.
Is there some way i can realize this or should i look for another solution outside of the gradle build?
I found a solution for the problem:
Instead of defining a task "initPaths" i directly used the java class "Pathfinder" in the build script:
import mypackage.PathFinder;
new PathFinder(project).run()
You only have to make sure that this part is above the definition of the task where the properties are used.
I admit this is a bit of a "hacky" solution but it works fine for my requirement.
you can do like this:
ext {
myPath //use it as a global variable that you can set and get from different gradle tasks and files
}
task firstTask {
doLast {
ext.myPath = "your path"
}
}
task run(type: MyPlugin) {
doFirst { //executed on runtime not on task definition
pathForPlugin = ext.myPath //Defined as a property in another gradle file
}
}
//example 2 - create run task dynamic
task initPath {
doLast {
tasks.create(name: "run", type: MyPlugin) {
pathForPlugin = ext.myPath
}
}
}

Conditional logic in gradle build to only execute certain code when a specific task is running

So, I am using a plugin in my gradle build (the plugin is org.flywaydb.flyway but that is not really relevant). I want to validate the caller has passed in a runtime parameter when tasks from this plugin are executing but not when other tasks are executing.
I pass options to the flyway plugin based on a supplied parameter. I want an error to be returned when a flywayTask is being executed and no parameter is supplied. When a non-flyway task is being run, I do not want to validate if the parameter is supplied.
gradle -PmyParam=myValue flywayMigration
=> should run code and there should be no error
gradle flywayMigration
=> should run code and should produce error (as no parameter supplied)
gradle jar
=> should not run code and no error should be produced
I have been reading about gradle configuration and execution which is fine but I still can't find a way to only run the code when the flyway plugin is bveing executed OR specific flyway tasks are being executed.
This is my current code:
if(gradle.taskGraph.hasTask("flywayMigrate")) {
flyway {
def dbCode, dbUser, dbPassword, dbUrl
if (!project.hasProperty("db_env")) {
throw new GradleException("Expected db_env property to be supplied for migration task. Can be passed" +
" at command line e.g. [gradle -Pdb_env=ex1 flywayMigrate]")
} else {
// do stuff
}
user = balh
password = blah
url = blah
driver = 'oracle.jdbc.OracleDriver'
cleanDisabled = true
baselineOnMigrate = true
baselineVersion = '1.0.0'
}
}
To be clear, I only want this code:
if (!project.hasProperty("db_env")
to run for flyway tasks.
The code above throws this error:
Task information is not available, as this task execution graph has not been populated.
I've tried a few things here, any advice would be appreciated.
It's not really clear to me, what exactly do you want to do in case if this property is provided, but I think, you can do it without accesing task graph, just try to use doFirst Closure of the flywayMigrate task. Just something like this:
flywayMigrate.doFirst {
if(!project.hasProperty("db_env")) {
throw ...
} else {
//Do something
}
}
And leave your plugin configuration free of any additional logic.
As for exception, have you tried to wait until graph is ready? It's usualy done as follows:
gradle.taskGraph.whenReady {taskGraph ->
if(gradle.taskGraph.hasTask("flywayMigrate")) {
...
}
}
Update: to answer the question from the comments
if I can attach doFirst to multiple tasks?
Yes, you can use somthing like:
//declare task names
def names = ["taskA", "taskB", "taskC"]
tasks.findAll {it ->
//filter tasks with names
if (it.name in names)
return it
}.each { it ->
//add some extra logic to it's doFirst closure
it.doFirst {
println 'hello'
}
}
Just check, that all the tasks are exists before this configuration.

How to make Gradle run tasks in certain order?

Let's say that I have created separate tasks for running integration and acceptance tests in my gradle build script. When I run the build task I want to run testing tasks before in this order: unit tests(test task), integration tests (intergationTest task) and acceptance tests (acceptanceTest task). Is this possible and how?
You are looking for "should run after" described in Gradle documentation - http://www.gradle.org/docs/current/userguide/more_about_tasks.html
Here's how you can do it without creating artificial dependencies:
https://caffeineinduced.wordpress.com/2015/01/25/run-a-list-of-gradle-tasks-in-specific-order/
TLDR; version:
//--- build aliases : define a synonym here if you want a shortcut to run multiple targets
def buildAliases = [
'all' : ['clean', 'assemble', 'runProvisioner', 'stopTomcat', 'installTomcat', 'deployToTomcat', 'startTomcat'],
'rebuild' : ['clean', 'assemble']
]
def expandedTaskList = []
gradle.startParameter.taskNames.each {
expandedTaskList << (buildAliases[it] ? buildAliases[it] : it)
}
gradle.startParameter.taskNames = expandedTaskList.flatten()
println "\n\n\texpanded task list: ${gradle.startParameter.taskNames }\n\n"
This is what I did on my projects.
check.dependsOn integTest
integTest.mustRunAfter test
tasks.withType(Pmd) {
mustRunAfter integTest // Pointing to a task
}
tasks.withType(FindBugs) {
mustRunAfter tasks.withType(Pmd) // Pointing to a group of tasks under Pmd
}
tasks.withType(Checkstyle) {
mustRunAfter tasks.withType(FindBugs)
}
It helped me to order tasks by group.
I created this helper method based on a solution that I found on Gradle forum.
Task.metaClass.runFirst = { Task... others ->
delegate.dependsOn(others)
delegate.mustRunAfter(others[0])
for (def i=0; i < others.size() - 1; i++) {
def before = others[i]
def after = others[i+1]
after.mustRunAfter(before)
}
}
Then you can create tasks X, A, B and C and use like this:
X.runFirst A, B, C
The first answer in the list turned out to be great for me. I used
X.shouldRunAfter Y
UPDATE: While using this "solution" for a short while i found out, that it does not work 100% as intended. I don't know why though. Any help to make it work would be appreciated. Maybe it does not work properly when there is more than one task in the Set?! While testing i did add some dummy-Tasks which only printed a text inbetween each of the other tasks and all seemed to be ok.
After some attempts with other solutions i came up with this solution:
It uses the mustRunAfter command to chain Sets of Tasks into the required order.
I'm working with Sets instead of individual Tasks, because i got circular dependency issues otherwise, since some tasks already depended on each other.
Also important to note: the isNotEmpty() check was essential, as it would otherwise break the enforced ordering if an ampty set was passed to the method.
tasks.register("cleanAndGenerate") {
var lastTasks: Set<Task> = setOf()
fun findTasks(taskName: String): Set<Task> {
return if (taskName.startsWith(":")) { // task in specific sub-project
setOf(tasks.findByPath(taskName)!!)
} else { // tasks in all (sub-)projects
getTasksByName(taskName, true)
}
}
fun dependsOnSequential(taskName: String) {
val tasks = findTasks(taskName)
tasks.forEach { task -> task.mustRunAfter(lastTasks) }
dependsOn(tasks)
if (tasks.isNotEmpty()) {
lastTasks = tasks
}
}
dependsOnSequential(":project1:clean") // task in specific sub-project
dependsOnSequential(":project2:clean")
dependsOnSequential("task1") // tasks in all (sub-)projects
dependsOnSequential("task2")
// add more as needed
}

How to pass arguments from command line to Gradle

I'm trying to pass an argument from command line to a Java class. I followed this post: http://gradle.1045684.n5.nabble.com/Gradle-application-plugin-question-td5539555.html but the code does not work for me (perhaps it is not meant for JavaExec?). Here is what I tried:
task listTests(type:JavaExec){
main = "util.TestGroupScanner"
classpath = sourceSets.util.runtimeClasspath
// this works...
args 'demo'
/*
// this does not work!
if (project.hasProperty("group")){
args group
}
*/
}
The output from the above hard coded args value is:
C:\ws\svn\sqe\sandbox\selenium2forbg\testgradle>g listTests
:compileUtilJava UP-TO-DATE
:processUtilResources UP-TO-DATE
:utilClasses UP-TO-DATE
:listTests
Received argument: demo
BUILD SUCCESSFUL
Total time: 13.422 secs
However, once I change the code to use the hasProperty section and pass "demo" as an argument on the command line, I get a NullPointerException:
C:\ws\svn\sqe\sandbox\selenium2forbg\testgradle>g listTests -Pgroup=demo -s
FAILURE: Build failed with an exception.
* Where:
Build file 'C:\ws\svn\sqe\sandbox\selenium2forbg\testgradle\build.gradle' line:25
* What went wrong:
A problem occurred evaluating root project 'testgradle'.
> java.lang.NullPointerException (no error message)
* Try:
Run with --info or --debug option to get more log output.
* Exception is:
org.gradle.api.GradleScriptException: A problem occurred evaluating root project
'testgradle'.
at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:54)
at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:127)
at org.gradle.configuration.BuildScriptProcessor.evaluate(BuildScriptProcessor.java:38)
There is a simple test project available at http://gradle.1045684.n5.nabble.com/file/n5709919/testgradle.zip that illustrates the problem.
This is using Gradle 1.0-rc-3. The NullPointer is from this line of code:
args group
I added the following assignment before the task definition, but it didn't change the outcome:
group = hasProperty('group') ? group : 'nosuchgroup'
Any pointers on how to pass command line arguments to Gradle appreciated.
project.group is a predefined property. With -P, you can only set project properties that are not predefined. Alternatively, you can set Java system properties (-D).
As noted in a comment, my solution is superceded by the newer built-in --args option in gradle. See this answer from #madhead or this similar question.
Building on Peter N's answer, this is an example of how to add (optional) user-specified arguments to pass to Java main for a JavaExec task (since you can't set the 'args' property manually for the reason he cites.)
Add this to the task:
task(runProgram, type: JavaExec) {
[...]
if (project.hasProperty('myargs')) {
args(myargs.split(','))
}
... and run at the command line like this
% ./gradlew runProgram '-Pmyargs=-x,7,--no-kidding,/Users/rogers/tests/file.txt'
My program with two arguments, args[0] and args[1]:
public static void main(String[] args) throws Exception {
System.out.println(args);
String host = args[0];
System.out.println(host);
int port = Integer.parseInt(args[1]);
my build.gradle
run {
if ( project.hasProperty("appArgsWhatEverIWant") ) {
args Eval.me(appArgsWhatEverIWant)
}
}
my terminal prompt:
gradle run -PappArgsWhatEverIWant="['localhost','8080']"
As of Gradle 4.9 Application plugin understands --args option, so passing the arguments is as simple as:
build.gradle
plugins {
id 'application'
}
mainClassName = "my.App"
src/main/java/my/App.java
public class App {
public static void main(String[] args) {
System.out.println(args);
}
}
bash
./gradlew run --args='This string will be passed into my.App#main arguments'
or in Windows, use double quotes:
gradlew run --args="This string will be passed into my.App#main arguments"
You can use custom command line options in Gradle:
./gradlew printPet --pet="Puppies!"
Custom command line options were an incubating feature in Gradle 5.0 but became public in Gradle 6.0.
Java solution
Follow the instructions here:
import org.gradle.api.tasks.options.Option;
public class PrintPet extends DefaultTask {
private String pet;
#Option(option = "pet", description = "Name of the cute pet you would like to print out!")
public void setPet(String pet) {
this.pet = pet;
}
#Input
public String getPet() {
return pet;
}
#TaskAction
public void print() {
getLogger().quiet("'{}' are awesome!", pet);
}
}
Then register it:
task printPet(type: PrintPet)
Now you can do:
./gradlew printPet --pet="Puppies!"
output:
Puppies! are awesome!
Kotlin solution
open class PrintPet : DefaultTask() {
#Suppress("UnstableApiUsage")
#set:Option(option = "pet", description = "The cute pet you would like to print out")
#get:Input
var pet: String = ""
#TaskAction
fun print() {
println("$pet are awesome!")
}
}
then register the task with:
tasks.register<PrintPet>("printPet")
If you need to check and set one argument, your build.gradle file would be like this:
....
def coverageThreshold = 0.15
if (project.hasProperty('threshold')) {
coverageThreshold = project.property('threshold').toString().toBigDecimal()
}
//print the value of variable
println("Coverage Threshold: $coverageThreshold")
...
And the Sample command in windows:
gradlew clean test -Pthreshold=0.25
I have written a piece of code that puts the command line arguments in the format that gradle expects.
// this method creates a command line arguments
def setCommandLineArguments(commandLineArgs) {
// remove spaces
def arguments = commandLineArgs.tokenize()
// create a string that can be used by Eval
def cla = "["
// go through the list to get each argument
arguments.each {
cla += "'" + "${it}" + "',"
}
// remove last "," add "]" and set the args
return cla.substring(0, cla.lastIndexOf(',')) + "]"
}
my task looks like this:
task runProgram(type: JavaExec) {
if ( project.hasProperty("commandLineArgs") ) {
args Eval.me( setCommandLineArguments(commandLineArgs) )
}
}
To pass the arguments from the command line you run this:
gradle runProgram -PcommandLineArgs="arg1 arg2 arg3 arg4"
There's a great example here:
https://kb.novaordis.com/index.php/Gradle_Pass_Configuration_on_Command_Line
Which details that you can pass parameters and then provide a default in an ext variable like so:
gradle -Dmy_app.color=blue
and then reference in Gradle as:
ext {
color = System.getProperty("my_app.color", "red");
}
And then anywhere in your build script you can reference it as course anywhere you can reference it as project.ext.color
More tips here: https://kb.novaordis.com/index.php/Gradle_Variables_and_Properties
Here is a solution for Kotlin DSL (build.gradle.kts).
I first try to get the variable as a property and if it was null try to get it from OS environment variables (can be useful in CIs like GitHub Actions).
tasks.create("MyCustomTask") {
val songName = properties["songName"]
?: System.getenv("SONG_NAME")
?: error("""Property "songName" or environment variable "SONG_NAME" not found""")
// OR getting the property with 'by'. Did not work for me!
// For this approach, name of the variable should be the same as the property name
// val songName: String? by properties
println("The song name: $songName")
}
We can then pass a value for the property from command line:
./gradlew MyCustomTask -PsongName="Black Forest"
Or create a file named local.properties at the root of the project and set the property:
songName=Black Forest
We can also add an env variable named SONG_NAME with our desired value and then run the task:
./gradlew MyCustomTask
pass a url from command line keep your url in app gradle file as follows
resValue "string", "url", CommonUrl
and give a parameter in gradle.properties files as follows
CommonUrl="put your url here or may be empty"
and pass a command to from command line as follows
gradle assembleRelease -Pcommanurl=put your URL here

Resources