Gradle Goodness: Command Line Options For Custom Tasks
Gradle added an incubation feature to Gradle 4.6 to add command line options for custom tasks.
This means we can run a task using Gradle and add command line options to pass information to the task.
Without this feature we would have to use project properties passed via the -P
or --project-property
.
The good thing about the new feature is that the Gradle help
task displays extra information about the command line options supported by a custom task.
To add a command line option we simply use the @Option
annotation on the setter method of a task property.
We must make sure the argument for the setter method is either a boolean
, Boolean
, String
, enum
, List<String>
or List<enum>
.
The @Option
annotation requires an option
argument with the name of the option as it must be entered by the user.
Optionally we can add a description
property with a description about the option.
It is good to add the description, because the help
task of Gradle displays this information and helps the user of our custom task.
Let’s start with a custom task that opens a file in the default application associated for the type of file.
The task has own property file
of type File
. Remember we cannot create an command line option for all property types.
To add a command line option we must overload the setFile
method to accept a String
value.
This setter method is used to expose the file
property as option.
package mrhaki.gradle
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.options.Option
import groovy.transform.CompileStatic
import java.awt.Desktop
/**
* Open a file or URI with the associated application.
*/
@CompileStatic
class OpenFile extends DefaultTask {
/**
* File to open.
*/
@Input
File file
/**
* Set description and group for task.
*/
OpenFile() {
description = 'Opens file with the associated application.'
group = 'Help'
}
/**
* Overload the setter method to support a String parameter. Now
* we can add the @Option annotation to expose our file property
* as command line option.
*
* @param path The object to resolve as a {@link File} for {@link #file} property.
*/
@Option(option = 'file', description = 'Set the filename of the file to be opened.')
void setFile(final String path) {
this.file = project.file(path)
}
/**
* Check if {@link Desktop} is supported. If not throw exception with message.
* Otherwise open file with application associated to file format on the
* runtime platform.
*
* @throws GradleException If {@link Desktop} is not supported.
*/
@TaskAction
void openFile() {
if (Desktop.isDesktopSupported()) {
Desktop.desktop.browse(new URI("file://${file.absolutePath}"))
} else {
throw new GradleException('Native desktop not supported on this platform. Cannot open file.')
}
}
}
We use the task in the following build file.
We create a task openReadme
and set the file
property value in the build script.
We also create the task open
, but don’t set the file
property.
We will use the command line option file
for this task:
task open(type: mrhaki.gradle.OpenFile)
task openReadme(type: mrhaki.gradle.OpenFile) {
file = project.file('README')
}
To run the open
task and set the file
command line option we use a double-dash as prefix:
$ gradle open --file=README
We can more information about the supported options for our task by invoking the help
task. We define the task name with the option task
:
$ gradle help --task=open
> Task :help
Detailed task information for open
Path
:open
Type
OpenFile (mrhaki.gradle.OpenFile)
Options
--file Set the filename of the file to be opened.
Description
Opens file with the associated application.
Group
Help
BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed
To define a set of valid values for an option we must add a method to our class that returns a list of valid values annotated with the @OptionValues
annotation.
We must set the task property name as argument for the annotation.
Then when we invoke Gradle’s help
task we see a list of valid values.
Validation of the property value is not done with the annotation, it is only for informational purposes.
So validation must be added to the task code by ourselves.
We rewrite our OpenFile
task and add the requirement that only files in the project directory can be opened and the filename must be in upper case.
The method availableFiles
return all files in the project directory with their name in upper case.
The annotation @OptionValues
is added to the method.
In the task action method openFile
we check if the file is valid to be opened
package mrhaki.gradle
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.options.Option
import org.gradle.api.tasks.options.OptionValues
import groovy.transform.CompileStatic
import java.awt.Desktop
/**
* Open a file or URI with the associated application.
*/
@CompileStatic
class OpenFile extends DefaultTask {
/**
* File to open.
*/
@Input
File file
/**
* Set description and group for task.
*/
OpenFile() {
description = 'Opens file with the associated application.'
group = 'Help'
}
/**
* Check if {@link Desktop} is supported. If not throw exception with message.
* Otherwise open file with application associated to file format on the
* runtime platform.
*
* @throws GradleException If {@link Desktop} is not supported or
* if filename not all uppercase in project dir.
*/
@TaskAction
void openFile() {
if (!fileAllUpperCaseInProjectDir) {
throw new GradleException('Only all uppercase filenames in project directory are supported.')
}
if (Desktop.isDesktopSupported()) {
Desktop.desktop.browse(new URI("file://${file.absolutePath}"))
} else {
throw new GradleException('Native desktop not supported on this platform. Cannot open file.')
}
}
private boolean isFileAllUpperCaseInProjectDir() {
getAllUpperCase()(file.name) && project.projectDir == file.parentFile
}
/**
* Overload the setter method to support a String parameter. Now
* we can add the @Option annotation to expose our file property
* as command line option.
*
* @param path The object to resolve as a {@link File} for {@link #file} property.
*/
@Option(option = 'file', description = 'Set the filename of the file to be opened.')
void setFile(final String path) {
this.file = project.file(path)
}
/**
* Show all files with filename in all uppercase in the project directory.
*
* @return All uppercase filenames.
*/
@OptionValues('file')
List availableFiles() {
project.projectDir.listFiles()*.name.findAll(allUpperCase)
}
private Closure getAllUpperCase() {
{ String word -> word == word.toUpperCase() }
}
}
Let’s run the help
task and see that available values are shown this time:
$ gradle help --task=open
> Task :help
Detailed task information for open
Path
:open
Type
OpenFile (mrhaki.gradle.OpenFile)
Options
--file Set the filename of the file to be opened.
Available values are:
README
Description
Opens file with the associated application.
Group
Help
BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed
Written with Gradle 4.7.