Tool Executions Tasks and Execution Specifications
Gradle script authors are quite aware of Exec and JavaExec tasks as well as the projects extensions exec and javaexec. Implementing tasks or extensions to support specific tools can involve a lot of work. This is where this set of abstract classes come in to simplify the work to a minimum and allowing plugin authors to think about what kind of tool functionality to wrap rather than implementing heaps of boilerplate code.
Wrapping an external tool within a gradle plugin usually have three components:
-
Execution specification
-
Project extension
-
Task type
How to implement these components are described in the following sections.
As from Grolifant 2.0, the original classes in `org.ysb33r.grolifant.api.v4.runnable from Grolifant 1.1 has been deprecated and replaced with a much simpler, but more declarative solution.
The classes in org.ysb33r.grolifant.api.v4.exec that were deprecated since Grolifant 1.1 have been removed.
Please see the upgrading guide for converting to the new classes.
|
Execution specifications
Execution specifications are used for configuring the necessary details for running an external process. The latter will then be used by a task type of a project extension.
The interfaces provide the configuration DSL, whereas the abstract classes from the basis of implementing your own wrappers for external execution. In all cases you need to implement the appropriate execution specification and then implement a task class that uses that specification. Under the hood the classes will take care of putting of configuring an ExecSpec and then executing it.
In most cases a AbstractExecSpec is the minimum what you’ll need to set up.
If you want to implement tasks around an executable that uses commands then you need AbstractCommandExecSpec.
AN example of this is something like terraform
or `packer.
If you want to implement tasks around an executable that executes scripts then you need AbstractScriptExecSpec.
Configuring an executable
All tasks (and execution specifications) are configured the same way. Let’s assume that you have something called gitExec
with which you want to run git
commands.
Your Groovy-DSL will look something like this.
gitExec {
entrypoint { (1)
executable = 'git'
addEnvironmentProvider( project.provider { -> [ GIT_COMMITER_NAME : 'ysb33r@domain.example' ]}) (2)
}
runnerSpec { (3)
args 'log', '--oneline'
}
process { (4)
}
}
1 | Configures anything that can be set in ProcessForkOptions and well as addEnvironmentProvider. |
2 | addEnvironmentProvider allows extertnal prvoiders to add additional environment variables into the execution environment.
These are added after the normal environment settings have been added. |
3 | Configures everything related to arguments. |
4 | Configures what is needed during and after execution of the process. It is essentially the same as hat can be configured in BaseExecSpec. |
Configuring a command-based executable
The command and its parameters are configured via an addition cmd
block.
For instance you might have implemented Git as a command-based executable.
You can then configure it as follows.
gitExec {
entrypoint {
executable = 'git'
addEnvironmentProvider( project.provider { -> [ GIT_COMMITER_NAME : 'ysb33r@domain.example' ]})
}
runnerSpec {
args '-C', '/my/project'
}
cmd {
command = 'log' (1)
args '--oneline' (2)
}
}
1 | Configures the command. |
2 | Configures arguments related to the command. |
The above example will result in a command line of git -C /my/project log --oneline
.
Configuring a script-based executable.
In a similar fashion it is possible to wrap scripts. For instance, you might be wrapping CRuby. You can then configure it as follows.
cruby {
entrypoint {
executable = 'ruby'
}
script { (1)
name = 'rb.env' (2)
path = '/path/to/rb.env' (3)
}
}
1 | Everything script-related is configured in the script block. |
2 | Use name to simply provide the script name.
In this case the implementation that you provide mut resolve the script in a suitable fashion.
For instance, a Ruby implementation might add -S to the command-line in this case |
3 | Provide the path to the script. In this case the path must exist and it will be passed as-is or resolved for a canonical location. |
Configuring the post-process
process {
ignoreExitValue = true (1)
output { (2)
capture() (3)
captureAndForward() (4)
captureTo( project.provider { -> file('build/this-output.txt') }) (5)
forward() (6)
noOutput() (7)
}
errorOutput { (8)
capture()
captureAndForward()
captureTo( project.provider { -> file('build/this-output.txt') })
forward()
noOutput()
}
afterExecute { (9)
}
}
1 | Whether to ignore the exit value. |
2 | Configure the standard output. |
3 | Capture the output. |
4 | Capture the output and forward the output to console. |
5 | Write the output to the file. |
6 | Forward the output to console. |
7 | Do not produce any output. |
8 | Configure the error output. |
9 | Add one of more closures/actions to process output.
This is called when the output or error output is captured.
It is passed a ExecOutput instance. |