Building a software artifact is a complex process involving various activities such as compiling source code, running automated tests, packaging distributable files, and so on. These activities are further split into many steps, often dependent on the execution order, fetching dependent artifacts, resolving configuration variables, and so on. Executing all these activities manually is cumbersome and often error-prone. A good build automation tool helps us reduce the effort and time it takes to build correct artifacts in a repeatable manner.
gradle --version
or gradle -v
GRADLE_OPTS
environment variable with acceptable flags to tune the JVM.JAVA_OPTS
environment variable. but his affects the setting for all the Java programs on a machine.gradle
command is issued from the root of a project directory with some tasks to be executed. like gradle help
gradle tasks
command.build.gradle
file and declare a task by adding the following line task helloWorld
gradle -q helloWorld
:
task helloWorld << {
println "Hello, World!"
println ("Hello, World!")
println ("Hello, World!");
System.out.println("Hello, World!")
System.out.println("Hello, World!");
System.out.println "Hello, World!";
System.out.println "Hello, World!"
}
// The project object has several properties and methods and it is available in our build scripts
// Assign value to description property.
project.description = 'Simple project' // or
project.setDescription('Simple project')
// DSL to create a new task using
// Groovy << operator.
task simple << {
println 'Running simple task for project ' +
project.description
}
// Or Use create method to add new task instead of Groovy << operator
project.getTasks().create('simple') {
println 'Running simple task for project ' + project.description
}
task first {
doFirst {
println 'Running first'
}
}
task second {
doLast { Task task ->
println "Running ${task.name}"
}
}
// OR
task second {
doLast {
// Using implicit 'it' closure parameter.
// The type of 'it' is a Gradle task.
println "Running ${it.name}"
}
}
// Or
task second {
doLast { Task task ->
// Using explicit name 'task' as closure parameter.
// We also defined the type of the parameter.
// This can help the IDE to add code completion.
println "Running ${task.name}"
}
}
// Here we use the << operator
// as synonym for the doLast method.
task third << { taskObject ->
println 'Running ' + taskObject.name
}
org.gradle.api.Action
interface.
The Action interface has one method: execute. This method is invoked when the task is executed.
The following piece of code shows a re-implementation of the first task in our build script:task first {
doFirst(
new Action() {
void execute(O task) {
println "Running ${task.name}"
}
}
)
}
dependsOn
method for a task or dependsOn
property.// We assign the task closure
// to a variable. We can reuse
// the variable name in our task definitions.
def printTaskName = { task ->
println "Run ${task.name}"
}
// We use the variable with the closure.
task third(dependsOn: 'second') << printTaskName
task second(dependsOn: 'first') << printTaskName
task first << printTaskName
def printTaskName = { task ->
println "Run ${task.name}"
}
task second << printTaskName
// We use the dependsOn method
// with a closure.
second.dependsOn {
// We use the Groovy method findAll
// that returns all tasks that
// apply to the condition we define
// in the closure: the task name
// starts with the letter 'f'.
project.tasks.findAll { task ->
task.name.contains 'f'
}
}
task first << printTaskName
task beforeSecond << printTaskName
defaultTasks 'first', 'second'
task first {
doLast {
println "I am first"
}
}
task second {
doFirst {
println "I am second"
}
}
defaultTasks 'second'
// Use description property to set description.
task first(description: 'Base task') << {
println "I am first"
}
task second(
dependsOn: first,
description: 'Secondary task') << {
println "I am second"
}
// Define name of task
// as a variable.
def simpleTask = 'simple'
// Variable is used for the task name.
task(simpleTask) << { task ->
println "Running ${task.name}"
}
// Name of task as variable.
def simpleTask = 'simple'
// Using Groovy GString with
// ${} expression to use variable
// as task name.
task "${simpleTask}" << { task ->
println "Running ${task.name}"
}
// Or use loops to create multiple tasks.
['Dev', 'Acc', 'Prod'].each { environment ->
// A new task is created for each element
// in the list ['Dev', 'Acc', 'Prod'].
task "deployTo${environment}" << { task ->
println "Deploying to ${environment}"
}
}
def printTaskName = { task ->
println "Running ${task.name}"
}
// Use tasks project variable to get access
// to the TaskContainer object.
// Then we use the create method of
// TaskContainer to create a new task.
project.tasks.create(name: 'first') << printTaskName
// Let Gradle resolve tasks to project variable.
tasks.create(name: 'second', dependsOn: 'first') << printTaskName
task first(description: 'First task')
task second(description: 'Second task')
tasks.addRule(
"Pattern: desc<TaskName>: " +
"show description of a task.") { taskName ->
if (taskName.startsWith('desc')) {
// Remove 'desc' from the task name.
def targetTaskName = taskName - 'desc'
// Uncapitalize the task name.
def targetTaskNameUncapitalize =
targetTaskName[0].toLowerCase() +
targetTaskName[1..-1]
// Find the task in the project we search
// the description for.
def targetTask =
project.tasks.findByName(
targetTaskNameUncapitalize)
if (targetTask) {
task(taskName) << {
println "Description of task ${targetTask.name} " +
" -> ${targetTask.description}"
}
}
}
}
// Create a simple task.
task simple << { task ->
println "Running ${task.name}"
}
// The simple task is available as
// project property.
simple.description = 'Print task name'
// We can invoke methods from the
// Task object.
simple.doLast {
println "Done"
}
// We can also reference the task
// via the project property
// explicitly.
project.simple.doFirst {
println "Start"
}
// Create simple task.
task simple << {
println "Hello ${message}"
}
// We set the value for
// the non-existing message
// property with the task extension
// support.
simple.ext.message = 'world'
<<
.
instead of adding actions, we have configured our task. The closure we use is then interpreted as a configuration closure.
All methods and properties in the closure are applied to the task.doFirst
and doLast
methods.
We cannot use the left-shift operator <<
.def printTaskName = { task ->
println "Running ${task.name}"
}
task 'one' {
// Invoke doFirst method to add action.
doFirst printTaskName
}
// Assign action through left-shift operator (<<).
task 'two' << printTaskName
task 'three' {
// This line will be displayed during configuration
// and not when we execute the task,
// because we use the configuration closure
// and forgot the << operator.
println "Running three"
}
defaultTasks 'one', 'two'
import static java.util.Calendar.*
task longrunning {
// Only run this task if the
// closure returns true.
onlyIf { task ->
def now = Calendar.instance
def weekDay = now[DAY_OF_WEEK]
def weekDayInWeekend = weekDay in [SATURDAY, SUNDAY]
return weekDayInWeekend
}
// Add an action.
doLast {
println "Do long running stuff"
}
}
// Create a new File object.
def file = new File('data.sample')
task handleFile {
// Use Spec implementation to write
// a conditon for the onlyIf method.
onlyIf(new Spec() {
boolean isSatisfiedBy(task) {
file.exists()
}
})
doLast {
println "Work with file ${file.name}"
}
}
// Define closure with the task actions.
def printTaskName = { task ->
println "Running ${task.name}"
}
// Create first task.
task first << printTaskName
// Use doFirst method with closure
// that throws exception when task
// is executed during work hours.
first.doFirst {
def today = Calendar.instance
def workingHours = today[Calendar.HOUR_OF_DAY] in 8..17
if (workingHours) {
throw new StopExecutionException()
}
}
// Create second task that depends on first task.
task second(dependsOn: 'first') << printTaskName
task listDirectory {
def dir = new File('assemble')
// Set value for enabled task property.
enabled = dir.exists()
// This is only executed if enabled is true.
doLast {
println "List directory contents: " +
dir.listFiles().join(',')
}
}
Skipping from the command line
we have defined the rules to skip a task in the build file. However, we can use the –exclude-tasks (-x)
command-line option if we run the build. for example : gradle third -x second
We want the task to be executed only if the source file has changed, or the output file is missing, or has changed since the last run of the task.
task convert {
def source = new File('source.xml')
def output = new File('output.txt')
doLast {
def xml = new XmlSlurper().parse(source)
output.withPrintWriter { writer ->
xml.person.each { person ->
writer.println "${person.name},${person.email}"
}
}
println "Converted ${source.name} to ${output.name}"
}
}
// change the definition of our task so that Gradle can determine whether the task needs to be executed based on
// changes in the input file or output file of the task.
// A task has the properties inputs and outputs that are used for this purpose.
// rewrite our task to make it support Gradle's incremental build feature
// We can use the --rerun-tasks command-line option to ignore the incremental build feature.
task convert {
def source = new File('source.xml')
def output = new File('output.txt')
// Define input file
inputs.file source
// Define output file
outputs.file output
doLast {
def xml = new XmlSlurper().parse(source)
output.withPrintWriter { writer ->
xml.person.each { person ->
writer.println "${person.name},${person.email}"
}
}
println "Converted ${source.name} to ${output.name}"
}
}
// If these methods are not appropriate for our build, we can even use the upToDateWhen method for the outputs property.
// We pass a closure or implementation of the org.gradle.api.specs.Spec interface to define a predicate that determines
// whether the output of the task is up to date.
project.version = '1.0'
task createVersionDir {
def outputDir = new File('output')
// If project.version changes then the
// task is no longer up-to-date
inputs.property 'version', project.version
outputs.dir outputDir
doLast {
println "Making directory ${outputDir.name}"
mkdir outputDir
}
}
task convertFiles {
// Define multiple files to be checked as inputs.
// Or use inputs.dir 'input' to check a complete directory.
inputs.files 'input/input1.xml', 'input/input2.xml'
// Use upToDateWhen method to define predicate.
outputs.upToDateWhen {
// If output directory contains any file which name
// starts with output and has the xml extension,
// then the task is up-to-date.
// We use the Groovy method any to check
// if at least one file applies to the condition.
// The ==~ syntax is a Groovy shortcut to
// check if a regular expression is true.
new File('output')
.listFiles()
any { it.name ==~ /output.*\.xml$/ }
}
doLast {
println "Running convertFiles"
}
}
gradlew
shell script for Linux/Mac OS X,
a gradlew.bat
batch script for Windows, and a few helper files.gradle wrapper
task
and should be checked into the version control system (VCS) along with project sources.
Instead of using the system-wide gradle command, we can run the builds via the wrapper script.gradle wrapper
or gradle wrapper --gradle-version 5.0
gradlew helloWorld
and we can use the arguments and flags exactly in the same way as we pass to the gradle command.gradle --help
.gradle -q tasks
.gradle -q properties
.gradle -q dependencies
.gradle -q projects
.gradle -q model
.gradle --project-dir [project-root-dir] --build-file [build-file] -q [task-name]
.--profile
command-line option to record the time that certain tasks take to complete.
The data is saved in an HTML file in the build/reports/profile
directory.--offline
command-line option to instruct Gradle to not access any network during the build.gradle --gui
.build.gradle
file and add the following code line to it:apply plugin: 'java'
src/main/java
directory.gradle compileJava
and the compiled classes go into build/classes/main
relative to the project root..jar
file for our application run the following task: gradle build
and the jar will be in the build/libs
directory.build
task, Gradle also invokes the compileJava
and other dependent tasks
before actually executing the build task. So, we don’t need to explicitly call compileJava
here to compile classes.archivesBaseName
property in the build.gradle file. asarchivesBaseName = "my-app"
gradle –q tasks
command.java
plugin is one such plugin.src/test/java
directory relative to the project root.compileTestJava
task, which is exactly the same as compileJava
, but compiles the test source files.apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
testCompile 'junit:junit:4.12'
}
gradle test
--rerun-tasks
flag on the command line so that all task actions can run.application
plugin to our build by apply plugin: 'application'
mainClassName
and run.args
attributes.application
plugin adds the run
task to our build.gradle run
or gradle -q run
.gradle -q run -PrunArgs=world
and access it by project.runArgs
.distZip
, which packages the application along with OS-specific start scripts: gradle distZip
.distributions.main.baseName = 'someName'
apply plugin: 'idea'
or apply plugin: 'eclipse'
.$ gradle eclipse
$ gradle idea
.idea/
*.iml
*.ipr
*.iws
.classpath
.project
.settings/
build.gradle
file for Building a Web Application in the root of the project:apply plugin: 'war'
repositories {
mavenCentral()
}
dependencies {
providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
}
providedCompile
configuration (scope) tells Gradle not to package the servlet API with the application,
as it will already be available on the container where the application will be deployed.
The providedCompile
configuration is added by the war
plugin (it also adds providedRuntime
).gradle war
.gradle tasks --all
./build/libs/hello-web.war
.jar -tf build/libs/hello-web.war
or use any the standard zip/unzip tools.Gretty
plugin can be found at a Gradle plugin portal,
This plugin adds numerous tasks to the build and supports various versions of Tomcat and Jetty.plugins {
id "org.akhikhl.gretty" version "2.0.0"
}
war
plugin’s application inside this block. For internal plugins,
we don’t need to specify a version. It will look as follows:plugins {
id "org.akhikhl.gretty" version "1.2.4"
id "war"
}
gretty {
servletContainer = 'tomcat8'
port = 8080
}
or
gretty {
servletContainer = 'jetty9'
port = 9080
}
dependencies
section in the build file.dependencies {
compile 'org.springframework:spring-core:4.0.6.RELEASE'
compile group:'org.springframework', name:'spring-core', version:'4.0.6.RELEASE'
}
configurationName dep1, dep2, dep3,...
runtime 'org.slf4j:slf4j-nop:1.7+'
to indicate a dynamic version of the library.gradle dependencies
runtime ('org.slf4j:slf4j-nop:1.7+') {
transitive = false
}
runtime ('org.slf4j:slf4j-nop:1.7.2') {
force = true
}
compile, runtime, testCompile, testRuntime
.providedCompile, providedRuntime
.repositories {
mavenCentral() // shortcut to maven central
mavenLocal() // shortcut to maven local (typically ~/.m2)
jcenter() // shortcut to jcenter
maven {
url "http://repo.company.com/maven"
}
ivy {
url "http://repo.company.com/ivy"
}
flatDir { // jars kept on local file system
dirs 'libDir'
}
}
def a = 10
and the type of a is decided at the runtime depending on what type of object it points to. or Integer b = 10
int c = 10
println c.getClass() // print => class java.lang.Integer
def name = "Gradle"
println "$name is an awesome build tool"
def number = 4
println "number is even ? ${number % 2 == 0 }"
def multilineString = '''\
Hello
World
'''
println multilineString
def r = /(\d)+/
println r.class
def pattern = ~/(\d)+/
println pattern.class // print > class java.util.regex.Pattern
// or
if ("groovy" ==~ /gr(.*)/)
println "regex support rocks"
def cl1 = {
println "hello world!"
}
cl1.call()
def cl2 = { n ->
println "value of param : $n"
}
cl2.call(101)
3.times(cl2)
3.times { println it * it }
def aList = [] // In Groovy, [] is actually a Java's List instance and not an array.
println aList.getClass()
def anotherList = ['a','b','c']
def list = [10, 20, 30] + [40, 50]
list << 60
list = list – [20, 30, 40]
list -= [20,30,40]
list.each {println it}
def aSet = [1,2,3] as Set
println aSet.class // print > class java.util.LinkedHashSet
TreeSet anotherSet = [1,2,3]
println anotherSet.class
// Adding elements to a set is just like a list using an indirection operator
aSet << 4
aSet << 3
println aSet // [1, 2, 3, 4]
// Map can be declared using the map literal [:]:
def a = [:]
def tool = [version:'2.8', name:'Gradle', platform:'all']
println tool.name
println tool["version"]
println tool.get("platform")
tool.version = "2.9"
tool["releaseDate"] = "2015-11-17"
tool.put("platform", "ALL")
int sum(int a, int b) {
return a + b;
}
def sum(a, b) {
a + b // we omitted the return statement as the evaluation of the last expression is automatically returned by a method.
}
sum(1,2)
sum 1, 2
def divide(number, by=2) {
number/by
}
println divide (10, 5)
println divide (10)
// Methods with map parameters/named parameters
def method(Map options) {
def a = options.a ?: 10
def b = options.b ?: 20 // options.a ? options.a : 10
}
method([a:10,b:20])
// We can omit the square brackets ([]) because maps have special support in the method call
method(a:10, b:20)
// The order of parameters is not important and all the parameters need not be passed.
// Also, the parenthesis wrapping is optional, just like any method call:
method b:30, a:40
method b:30
// varags are denoted by ..., but providing the type is optional:
def sumSquares(...numbers) {
numbers.collect{ it * it }.sum()
}
sumSquares 1, 2, 3
// Closures are important and, hence, Groovy has a special syntax for closures
// if the closure is the last parameter of a method signature:
// the third call is the special syntactical support in which the parenthesis just wraps the other parameters,
// while the closure is written outside the parenthesis, as if it were a method body.
def myMethod (param, cls) {
...
}
myMethod(1,{ ... })
myMethod 2, {... }
myMethod(3) {...}
class Person {
def name, age
}
// In addition to the default constructor, classes in Groovy get a special constructor,
// which takes the map of properties of the class. Here is how we use it:
def person = new Person(name:"John Doe", age:35)
println person.age
person.age = 36
println person.age
// We can provide our own getters and/or setter for the desired fields,
// which will take precedence over the generated one
void setAge(age){
if (age < 0)
throw new IllegalArgumentException("age must be a positive number")
else
this.age = age
}
// We can add an instance and static methods to classes just like we do in Java:
def speak(){
println "${this.name} speaking"
}
static def now(){
new Date().format("yyyy-MM-dd HH:mm:ss")
}
apply plugin: 'java' // apply is a method.
apply(plugin: 'java')
apply([plugin: 'java'])
// the apply method is implicitly applied on the project object.
// So, we can also call it on the project object's reference:
project.apply([plugin: 'java'])
project.apply plugin: 'java'
project.repositories {
mavenCentral()
}
project.dependencies {
testCompile 'junit:junit:4.11'
}
// or
project.repositories({...})
project.dependencies({...})
// Project Properties:
description = "a sample project"
version = "1.0"
task printProperties << {
println project.version
println project.property("description")
}
ext.abc = "123"
task printExtraProperties << {
println project.abc
println project.property("abc")
println project.ext.abc
}
project.task "myTask"
project.task("myTask")
Task task(String name)
Task task(String name, Closure configureClosure)
Task task(Map<String, ?> args, String name)
Task task(Map<String, ?> args, String name, Closure configureClosure)
someTask.doLast({
println "this should be printed when the task is run"
})
someTask {
doLast {
println "third line that should be printed"
}
}
someTask << {
println "the action of someTask"
}
// $ gradle compile dist
task compile << {
println 'compling the source'
}
task dist(dependsOn: compile) << {
println "preparing a jar dist"
}
task distUsingTemp << {
println ("preapring dist using a temp dir")
}
task cleanup << {
println("removing tmp dir")
}
distUsingTemp.finalizedBy cleanup
cleanup.onlyIf { file("/tmp").exists()}
build.mustRunAfter clean
build.shouldRunAfter clean
10.times { number ->
task "dynamicTask$number" << {
println "this is dynamic task number # $number "
}
}
defaultTasks "myTaskName", "myOtherTask"
task copyDocumentation(type:Copy) {
from file("src/docs/html")
into file("$buildDir/docs")
}
class Print extends DefaultTask {
@Input
String message = "Welcome to Gradle"
@TaskAction
def print() {
println "$message"
}
}
task welcome(type: Print)
task thanks(type: Print) {
message = "Thanks for trying custom tasks"
}
task bye(type: Print)
bye.message = "See you again"
thanks.dependsOn welcome
thanks.finalizedBy bye
include ':repository', ':services', ':web-app'
include 'repository', 'services', 'web-app'
gradle projects
allprojects {
task whoami << {println "I am ${project.name}"}
}
// or
allprojects {
task("describe${project.name.capitalize()}") << {
println project.name
}
}
subprojects {
apply plugin: 'java'
}
gradle -q tasks --all
in this case.java
plugin will only be available on subprojects, whereas tasks such
as help tasks will be available on all projects.project(':services') {
dependencies {
compile project(':repository')
}
}
dependencies {
compile project(':services')
}
The gradle plugin provides the flexibility to configure the plugin based on the project’s need as the following code snippt shows:
apply plugin:'java'
task cleanDir << {
delete "build"
delete "dist"
}
task createDirs(dependsOn:'cleanDir') << {
def classes = file("build/classes")
def dist = file("dist")
classes.mkdirs()
dist.mkdirs()
}
compileJava {
File classesDir = file("build/classes")
FileTree srcDir = fileTree(dir: "src")
source srcDir
destinationDir classesDir
}
task createJar(type: Jar) {
destinationDir = file("dist")
baseName = "JavaProject-1.0"
from "build/classes"
}
createJar.dependsOn compileJava
compileJava.dependsOn createDirs
apply plugin
statement like: apply plugin: '<plugin name>'
src/main/java
,
test code location should be src/test/java
dependencies{
compile(' org.apache.logging.log4j: log4j-core:1.2')
}
- Repositories configuration. This is the location from where you download dependencies.
In Gradle, you can mention the repository using the following syntax: ``` repositories {
maven {
url "http://companylocalrepository.org"
} } ```
gradle –b build.gradle uploadArchives
command to upload the artifacts.
As a part of life cycle, it will build and upload the artifacts.flatDir
.maven-publish
plugin to have more control over the publishing process.
It gives you many additional flexibilities along with the default publish tasks.
A user can modify the POM file, publish multiple modules, and so on.
You can find more details at https://docs.gradle.org/current/userguide/publishing_maven.html.java plugin
:
apply plugin: 'java'
version=1.0
repositories {
mavenCentral()
}
dependencies {
compile ('log4j:log4j:1.2.16')
}
uploadArchives {
repositories {
maven {
credentials {
username "user1"
password "user1"
}
url "http://company.private.repo"
}
}
}
Java plugin
to your build file, Gradle provides you a javadoc
task.gradle clean javadoc
. It will generate the basic Java docs at {project_root}\build\docs\javadoc
.apply plugin:'java'
repositories {
mavenCentral()
}
dependencies {
testCompile 'org.testng:testng:6.8.21'
}
test {
ignoreFailures = true
useTestNG(){
suites("src/test/resources/testng.xml")
}
}
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Suite1" verbose="1" >
<listeners>
<listener class-name="org.testng.reporters.EmailableReporter" />
</listeners>
<test name="Smoke Test">
<groups>
<run>
<exclude name="Integration" />
<include name="Smoke" />
</run>
</groups>
<classes>
<class name="com.packtpub.ge.ch7.HashTest">
</class>
</classes>
</test>
</suite>
src/integrationTest/java
, for integration test cases,
and you can configure the same in your Gradle build scripts. The updated build script would be as follows:apply plugin: 'java'
sourceSets {
integrationTest {
java.srcDir file('src/integrationTest/java')
resources.srcDir file('src/integrationTest/resources') // to add the resources
}
}
task runIntegrationTest(type: Test) {
testClassesDir = sourceSets.integrationTest.output.classesDir
classpath = sourceSets.integrationTest.runtimeClasspath
}
integrationTestCompile
and integrationTestRuntime
.integrationTestCompile
can be used to configure dependencies required to compile the test cases and
integrationTestRuntime
can be used to configure dependencies required to execute the test cases.gradle dependencies
dependencies {
// other configuration dependencies
integrationTestCompile 'org.hibernate:hibernate:3.2.3.ga'
}
gradle runIntegrationTest
.build.gradle
file has the following content:apply plugin: 'java'
apply plugin: 'jacoco'
repositories {
mavenCentral()
}
dependencies {
testCompile 'org.testng:testng:6.8.8'
}
test{
systemProperty "url",System.properties['url']
useTestNG()
}
jacocoTestReport.dependsOn test
{build dir}/reports/jacoco/test/html
directory and an HTML report file will be generated.jacocoTestReport
task to generate the code coverage report: gradle clean jacocoTestReport
.jacoco {
toolVersion = "<Required-Version>"
reportsDir = file("Path_to_Jacoco_ReportDir")
}
jacocoTestReport {
reports {
xml.enabled false
html.destination "<Path_to_dircectory>"
}
}
apply plugin: 'java'
apply plugin: "sonar-runner"
repositories {
mavenCentral()
}
version = '1.0'
repositories {
mavenCentral()
}
sonarRunner {
sonarProperties {
property "sonar.host.url", "http://<IP_ADDRESS>:<PORT>"
property "sonar.jdbc.url",
"jdbc:h2:tcp://<IP_ADDRESS>:<PORT>/sonar"
property "sonar.jdbc.driverClassName", "org.h2.Driver"
property "sonar.jdbc.username", "sonar"
property "sonar.jdbc.password", "sonar"
}
}
sonarRunner
task for code analysis. After successful execution of this task,
you will find the report hosted on the Sonar server: gradle clean sonarRunner
apply plugin: CustomPlugin
class CustomPlugin implements Plugin<Project> {
void apply(Project project) {
project.task('task1') << {
println "Sample task1 in custom plugin"
}
project.task('task2') << {
println "Sample task2 in custom plugin"
}
}
}
task2.dependsOn task1
apply plugin:<plugin name/plugin class>
(if a plugin is implemented in the same script or in the buildSrc directory).task1.doLast {
println "Added more functionality to task1"
}
task2.dependsOn task1
customplugin.properties
inside the src/main/resources/META-INF/gradle-plugins
directory.buildscript {
repositories {
flatDir {dirs "../CustomPlugin/build/libs/"}
}
dependencies {
classpath group: 'ch8', name: 'CustomPlugin',version: '1.0'
}
}
apply plugin: 'customplugin'
// Create the CustomPluginExtension.groovy class as follows:
class CustomPluginExtension {
def location = "/plugin/defaultlocation"
}
// register this class to your plugin class:
class CustomPlugin implements Plugin<Project> {
void apply(Project project) {
def extension = project.extensions.create("customExt",CustomPluginExtension)
project.task('task1') << {
println "Sample task1 in custom plugin"
println "location is "+project.customExt.location
}
}
}
// To change this field to some other value, add customExt closure
// to your build.gradle file with a different value configured for the location:
buildscript {
repositories {
flatDir {dirs "../CustomPlugin/build/libs/"}
}
dependencies {
classpath group: 'ch8', name: 'CustomPlugin',version: '1.0'
}
}
apply plugin: 'customplugin'
customExt {
location="/plugin/newlocation"
}
// Use String for file reference.
File wsdl = file('src/wsdl/sample.wsdl')
// Use File object for file reference.
File xmlFile = new File('xml/input/sample.xml')
def inputXml = project.file(xmlFile)
// Or a URI instance.
def uri = new URI('file:/README')
def readmeFile = file(uri)
// Use a closure to determine the
// file or directory name.
def fileNames = ['src', 'web', 'config']
def configDir = file {
fileNames.find { fileName ->
fileName.startsWith('config')
}
}
// Use Callable interface.
def source = file(new Callable<String>() {
String call() {
'src'
}
})
// Suppose, we want to work with a directory named config in our build script. The directory must be present, otherwise the build will stop:
def dir = project.file(new File('config'), PathValidation.DIRECTORY)
// Check file or directory exists.
def readme = project.file('README', PathValidation.EXISTS)
// Check File object is really a file.
def license = project.file('License.txt', PathValidation.FILE)
// Use String instances.
def multiple =
files('README', 'licence.txt')
// Use File objects.
def userFiles =
files(new File('README'), new File('INSTALL'))
// We can combine different argument types.
def combined = files('README', new File('INSTALL'))
// We can pass a URI or URL object, just as we could with the file() method:
def urlFiles =
files(new URI('file:/README'),
new URL('file:/INSTALL'))
// We can also use an array, Collection, or Iterable object with file names
// or another ConfigurableFileCollection instance as an argument:
// Use a Collection with file or directory names.
def listOfFileNames = ['src', 'test']
def mainDirectories = files(listOfFileNames)
// Use an array.
// We use the Groovy as keyword to
// force an object to a certain type.
mainDirectories = files(listOfFileNames as String[])
// Or an implementation of the Iterable interface.
mainDirectories = files(listOfFileNames as Iterable)
// Combine arguments and pass another file collection.
def allDirectories = files(['config'], mainDirectories)
// We can also use a closure or instance of the Callable interface to define a list of files, as follows:
import java.util.concurrent.Callable
def dirs = files {
[new File('src'), file('README')]
.findAll { file ->
file.directory
}
}
def rootFiles = files(new Callable<List<File>>() {
def files = [new File('src'),
file('README'),
file('INSTALL')]
List<File> call() {
files.findAll { fileObject ->
fileObject.file
}
}
})
// we can pass a Task object as an argument to the files() method. The outputs property of the task is used to determine
// the file collection or we can directly use the TaskOutputs object instead of letting Gradle resolve
// it via the outputs property of the Task object.
// To get the file collection object in our build script,
we simply pass the Task instance as an argument to the files() method:
task convert {
def source = new File('source.xml')
def output = new File('output.txt')
// Define input file
inputs.file source
// Define output file
outputs.file output
doLast {
def xml = new XmlSlurper().parse(source)
output.withPrintWriter { writer ->
xml.person.each { person ->
writer.println "${person.name},${person.email}"
}
}
println "Converted ${source.name} to ${output.name}"
}
}
// Get the file collection from
// the task outputs property.
def taskOutputFiles = files(convert)
// Alternatively we could use
// the outputs property directly.
taskOutputFiles = files(convert.outputs)
// The ConfigurableFileCollection interface has useful methods to manipulate the collection, for example,
// we can use + and - operators to add or remove elements from the collection, respectively:
// Define collection.
def fileCollection = files('README', 'INSTALL')
// Remove INSTALL file from collection.
def readme = fileCollection - files('INSTALL')
// Add new collection to existing collection.
def moreFiles =
fileCollection +
files(file('config',
PathValidation.DIRECTORY))
// To get the absolute path names for the elements in ConfigurableFileCollection, we can use the asPath property.
task collectionPath << {
def fileCollection = files('README', 'INSTALL')
println fileCollection.asPath
}
// To get the File objects that make up the file collection, we can use the files property.
def fileCollection = files('README', [new File('INSTALL')])
// Get all elements as File objects.
def allFiles = fileCollection.files
// Or use casting with as keyword.
def fileObjects = fileCollection as File[]
def singleFileCollection = files('INSTALL')
// Get single file as File object.
def installFile = singleFileCollection.singleFile
// we can apply a filter to our file collection with the filter() method.
task filterFiles << {
def rootFiles = files('INSTALL', 'README')
// Filter for files with a txt extension.
def smallFiles = rootFiles.filter { file ->
file.name.endsWith 'txt'
}
rootFiles = rootFiles + files('LICENSE.txt')
// smallFiles now contains 2 files:
// INSTALL and LICENSE
}
// Create file tree with base directory 'src/main'
// and only include files with extension .java
def srcDir = fileTree('src/main').include('**/*.java')
// Use map with arguments to create a file tree.
def resources =
fileTree(dir: 'src/main',
excludes: ['**/*.java', '**/*.groovy'])
// Create file tree with project directory as base
// directory and use method include() on tree
// object to include 2 files.
def base = fileTree('.')
base.include 'README', 'INSTALL'
// Use closure to create file tree.
def javaFiles = fileTree {
from 'src/main/java'
exclude '*.properties'
}
// To filter a file tree, we can use the filter() method as we do with file collections, but we can also use the matching() method.
We pass a closure to the matching() method or an instance of the org.gradle.api.tasks.util.PatternFilterable interface.
We can use include, includes, exclude, and excludes methods to either include or exclude files from the file tree, as follows:
def sources = fileTree {
from 'src'
}
def javaFiles = sources.matching {
include '**/*.java'
}
def nonJavaFiles = sources.matching {
exclude '**/*.java'
}
def nonLanguageFiles = sources.matching {
exclude '**/*.scala', '**/*.groovy', '**/*.java'
}
def modifiedLastWeek = sources.matching {
lastWeek = new Date() - 7
include { file ->
file.lastModified > lastWeek.time
}
}
// We can use the visit() method to visit each tree node
def testFiles = fileTree(dir: 'src/test')
testFiles.visit { fileDetails ->
if (fileDetails.directory) {
println "Entering directory ${fileDetails.relativePath}"
} else {
println "File name: ${fileDetails.name}"
}
}
def projectFiles = fileTree(dir: 'src/test')
projectFiles.visit(new FileVisitor() {
void visitDir(FileVisitDetails details) {
println "Directory: ${details.path}"
}
void visitFile(FileVisitDetails details) {
println "File: ${details.path}, size: ${details.size}"
}
})
// To copy files in Gradle, we can use the Copy task
task simpleCopy(type: Copy) {
from 'src/xml'
into 'definitions'
}
// uses the include() and exclude() methods to select the set of files to be copied:
// Define a closure with ANT-style
// pattern for files.
def getTextFiles = {
'**/*.txt'
}
task copyTask(type: Copy) {
// Copy from directory.
from 'src/webapp'
// Copy single file.
from 'README.txt'
// Include files with html extension.
include '**/*.html', '**/*.htm'
// Use closure to resolve files.
include getTextFiles
// Exclude file INSTALL.txt.
exclude 'INSTALL.txt'
// Copy into directory dist
// resolved via closure.
into { file('dist') }
}
// Another way to copy files is with the Project.copy() method. The copy() method accepts a CopySpec interface implementation
task simpleCopy << {
// We use the project.copy()
// method in our task. We can
// leave out the project reference,
// because Gradle knows how to
// resolve it automatically.
copy {
from 'src/xml'
into 'definitions'
}
}
task archiveDist(type: Zip) {
from 'dist'
// Create output filename.
// Final filename is:
// dist-files-archive-1.0-sample.zip
baseName = 'dist-files'
appendix = 'archive'
extension = 'zip'
version = '1.0'
classifier = 'sample'
}
// By using task type Zip we instruct
// Gradle to create an archive
// in ZIP format.
task archiveFiles(type: Zip) {
from 'dist'
// Copy files to a directory inside the archive.
into 'files'
// Set destination directory for ZIP file.
// $buildDir refers to default Gradle
// build directory 'build/'.
destinationDir = file("$buildDir/zips")
// Set complete filename at once.
archiveName = 'dist-files.zip'
}
// To create a TAR archive with the optional gzip or bzip2 compression, we must use the tarFiles task
task tarFiles(type: Tar) {
from 'dist'
// Set destination directory.
destinationDir = file("$buildDir/tarballs")
// Set filename properties.
baseName = 'dist-files'
// Default extension for tar files
// with gzip compression is tgz.
extension = 'tar.gz'
// Use gzip compression.
compression = Compression.GZIP // or Compression.BZIP2
}
version = '1.0'
group = 'Sample'
description = 'Sample build file to show project properties'
task defaultProperties << {
println "Project: $project"
println "Name: $name"
println "Path: $path"
println "Project directory: $projectDir"
println "Build directory: $buildDir"
println "Version: $version"
println "Group: $project.group"
println "Description: $project.description"
println "AntBuilder: $ant"
}
// Define new property.
ext.customProperty = 'custom'
// Or we can use ext{} script block.
ext {
anotherCustomProperty = 'custom'
}
task showProperties {
ext {
customProperty = 'override'
}
doLast {
// We can refer to the property
// in different ways:
println customProperty
println project.ext.customProperty
println project.customProperty
}
}
task showProperties {
doLast {
println "Version: $version"
println "Custom property: $customProperty"
}
}
// Run By: $ gradle -Pversion=1.1 -PcustomProperty=custom showProperties
task showProperties {
doLast {
println "Version: $version"
println "Custom property: $customProperty"
}
}
// run: gradle -Dorg.gradle.project.version=2.0 -Dorg.gradle.project.customProperty=custom showProperties
task showProperties {
doLast {
println "Version: $version"
println "Custom property: $customProperty"
}
}
ORG_GRADLE_PROJECT_
and is followed by the property name.
We use our build file to show the properties:// we set ORG_GRADLE_PROJECT_version and ORG_GRADLE_PROJECT_customProperty environment variables,
// then we run our showProperties task:
task showProperties {
doLast {
println "Version: $version"
println "Custom property: $customProperty"
}
}
// Run by : $ gradle --debug logLevels . $ gradle --info logLevels . $ gradle logLevels
// Simple logging sample.
task logLevels << {
logger.debug 'debug: Most verbose logging level'
logger.log LogLevel.DEBUG, 'debug: Most verbose logging level'
logger.info 'info: Use for information messages'
logger.log LogLevel.INFO, 'info: Use for information messages'
logger.lifecycle 'lifecycle: Progress information messages'
logger.log LogLevel.LIFECYCLE,
'lifecycle: Progress information messages'
logger.warn 'warn: Warning messages like invalid configuration'
logger.log LogLevel.WARN,
'warn: Warning messages like invalid configuration'
logger.quiet 'quiet: This is important but not an error'
logger.log LogLevel.QUIET,
'quiet: This is important but not an error'
logger.error 'error: Use for errors'
logger.log LogLevel.ERROR, 'error: Use for errors'
}
we know that every Gradle project and task has a logger we can use. However, we can also explicitly create a logger instance with the Logging class
class Simple {
// Create new logger using the Gradle
// logging support.
private static final Logger logger = Logging.getLogger('Simple')
int square(int value) {
int square = value * value
logger.lifecycle "Calculate square for ${value} = ${square}"
return square
}
}
logger.lifecycle 'Running sample Gradle build.'
task useSimple {
doFirst {
logger.lifecycle 'Running useSimple'
}
doLast {
new Simple().square(3)
}
}
logging.captureStandardOutput LogLevel.INFO
println 'This message is now logged with log level info instead of quiet'
task redirectLogging {
doFirst {
// Use default redirect log level quiet.
println 'Start task redirectLogging'
}
doLast {
logging.captureStandardOutput LogLevel.INFO
println 'Finished task redirectLogging'
}
}
// Creating wrapper scripts
$ gradle wrapper
$ gradle wrapper --gradle-version=2.12
// Customizing the Gradle Wrapper
task createWrapper(type: Wrapper) {
// Set Gradle version for wrapper files.
gradleVersion = '2.12'
// Rename shell scripts name to
// startGradle instead of default gradlew.
scriptFile = 'startGradle'
// Change location and name of JAR file
// with wrapper bootstrap code and
// accompanying properties files.
jarFile = "${projectDir}/gradle-bin/gradle-bootstrap.jar"
}
// run by $ gradle createWrapper
// To change the URL from where the Gradle version must be downloaded, we can alter the distributionUrl property.
task createWrapper(type: Wrapper) {
// Set URL with custom Gradle distribution.
distributionUrl = 'http://intranet/gradle/dist/gradle-custom-2.12.zip'
}
apply plugin: 'java'
task sourceSetJavaProperties << {
sourceSets {
main {
println "java.srcDirs = ${java.srcDirs}"
println "resources.srcDirs = ${resources.srcDirs}"
println "java.files = ${java.files.name}"
println "allJava.files = ${allJava.files.name}"
println "resources.files = ${resources.files.name}"
println "allSource.files = ${allSource.files.name}"
println "output.classesDir = ${output.classesDir}"
println "output.resourcesDir = ${output.resourcesDir}"
println "output.files = ${output.files}"
}
}
}
apply plugin: 'java'
sourceSets {
api
main {
compileClasspath += files(api.output.classesDir)
}
}
classes.dependsOn apiClasses
apply plugin: 'java'
sourceSets {
main {
java {
srcDir 'src/java'
}
resources {
srcDir 'resources/java'
}
}
test {
java {
srcDir 'test/unit/java'
}
resources {
srcDir 'resources/test'
}
}
'integeration-test' {
java {
srcDir 'test/integration/java'
}
resources {
srcDir 'resources/test'
}
}
}
task showConvention << {
println sourceSets.main.name
println project.sourceSets.main.name
println project.convention.plugins.java.sourceSets.main.name
}
apply plugin: 'java'
archivesBaseName = 'gradle-sample'
version = '1.0'
sourceCompatibility = JavaVersion.VERSION_1_8 // Or '1.8' or 8
jar {
manifest {
attributes(
'Implementation-Version' : version,
'Implementation-Title' : 'Gradle Sample'
)
}
}
apply plugin: 'java'
javadoc {
source sourceSets.api.allJava
}
// to set some of the options for the javadoc task in our project:
javadoc {
source sourceSets.api.allJava
title = 'Gradle Sample Project'
options.links = ['http://docs.oracle.com/javase/6/docs/api/']
options.footer = "Generated on ${new Date().format('dd MMM yyyy')}"
options.header = "Documention for version ${project.version}"
}
apply plugin: 'java'
archivesBaseName = 'gradle-sample'
version = '1.0'
sourceSets {
api
main {
compileClasspath += files(api.output.classesDir)
}
}
classes.dependsOn apiClasses
task apiJar(type: Jar) {
// Define appendix that will be
// appended to the archivesBaseName
// for the JAR.
appendix = 'api'
// Define the input for the JAR file.
from sourceSets.api.output
}
Every Gradle build has a ConfigurationContainer object. This object is accessible via the Project property with the name containers. We can use a closure to configure the container with Configuration objects. Each Configuration object has at least a name, but we can change more properties. We can set a resolution strategy, if a configuration has version conflicts with dependencies, or we can change the visibility of a configuration so that it will not be visible outside of our project. A Configuration object also has a hierarchy. So we can extend from the existing Configuration objects to inherit the settings.
configurations {
commonsLib {
description = 'Common libraries'
}
mainLib {
extendsFrom commonsLib
description = 'Main libraries'
}
}
// Reference mainLib configuration
// using [] syntax for the
// configuration container.
println configurations['mainLib'].name
// Reference commonsLib in another way,
// just use the name directly as property.
println configurations.commonsLib.name
repositories {
jcenter()
}
configurations {
springLibs
}
dependencies {
springLibs('org.springframework:spring-web:4.2.3.RELEASE')
}
task copyCompileDeps(type: Copy) {
from configurations.springLibs
into "$buildDir/compileLibs"
}
dependencies {
compile files('spring-core.jar', 'spring-aop.jar')
compile fileTree(dir: 'deps', include: '*.jar')
}
apply plugin: 'java'
repositories {
jcenter()
}
dependencies {
testCompile('junit:junit:4.12')
}
test {
// Add System property to running tests.
systemProperty 'sysProp', 'value'
// Use the following JVM arguments for each test process.
jvmArgs '-Xms256m', '-Xmx512m'
// Enable debugging mode.
debug = true
// Ignore any test failues and don't fail the build.
ignoreFailures = true
// Enable assertions for test with the assert keyword.
enableAssertions = true
// Run four tests in parallel.
maxParallelForks = 4
// Restart proces after
// 10 executions.
forkEvery = 10
// Disable automatic scanning
// for test classes.
scanForTestClasses = false
// Include test classes.
include('**/*Test.class', '**/*Spec.class')
// Exclude test classes.
exclude('**/Abstract*.class', '**/Run*.class')
// Set exception format to full
// instead of default value 'short'.
testLogging.exceptionFormat 'full'
// We can also use a script block to configure
// the testLogging property.
testLogging {
// No log level specified so the
// property is set on LIFECYCLE log level.
// We can pass arguments to determine
// which test events we want to see in the
// command-line output.
events 'passed'
// Show logging events for test methods.
minGranularity = 3
// All valid values for the stackTrace output.
stackTraceFilters 'groovy', 'entry_point', 'truncate'
// Show System.out and System.err output
// from the tests.
showStandardStreams = true
// Configure options for DEBUG log level.
debug {
events 'started'
}
}
}
apply plugin: 'java'
repositories {
jcenter()
}
dependencies {
testCompile('junit:junit:4.12')
}
task testReport(type: TestReport) {
destinationDir = file("$buildDir/test-reports")
testResultDirs = files("$buildDir/test-results")
reportOn(test)
}
// If the test task is finished,
// we want the testReport to be executed.
test.finalizedBy(testReport)
apply plugin: 'java'
task runJava(dependsOn: classes,
description: 'Run gradle.sample.SampleApp') << {
javaexec {
// Java main class to execute.
main = 'gradle.sample.SampleApp'
// We need to set the classpath.
classpath sourceSets.main.runtimeClasspath
// Extra options can be set.
maxHeapSize = '128m'
systemProperty 'sysProp', 'notUsed'
jvmArgs '-client'
}
}
repositories {
jcenter()
}
apply plugin: 'java'
task runJava(type: JavaExec) {
dependsOn classes
description = 'Run gradle.sample.SampleApp'
// Java main class to execute.
main = 'gradle.sample.SampleApp'
// We need to set the classpath.
classpath sourceSets.main.runtimeClasspath
// Extra options can be set.
systemProperty 'sysProp', 'notUsed'
jvmArgs '-client'
// We can pass arguments to the main() method
// of gradle.sample.SampleApp.
args 'mainMethodArgument', 'notUsed'
}
apply plugin: 'application'
mainClassName = 'gradle.sample.SampleApp'
// Extra configuration for run task if needed.
run {
// Extra options can be set.
systemProperty 'sysProp', 'notUsed'
jvmArgs '-client'
// We can pass arguments to the main() method
// of gradle.sample.SampleApp.
args 'mainMethodArgument', 'notUsed'
}
apply plugin: 'application'
mainClassName = 'gradle.sample.SampleApp'
archivesBaseName = 'gradle-sample'
version = '1.0'
Publishing artifacts Read more about publishing artifacts in https://docs.gradle.org/current/userguide/artifact_management.html
Creating a WAR file
apply plugin: 'java'
version = '1.0'
// Custom archive task with
// specific properties for a WAR archive.
task war(type: War) {
dependsOn classes
from 'src/main/webapp'
// Files copied to WEB-INF.
webInf {
from 'src/main/webInf'
}
// Copied to WEB-INF/classes.
classpath sourceSets.main.runtimeClasspath
// Copied to WEB-INF/lib.
classpath fileTree('libs')
// Custom web.xml.
webXml = file(' ')
baseName = 'gradle-webapp'
}
assemble.dependsOn war
settings.gradle
, in the root directory.
and use the include()
method to set the projects that are part of our multi-project build.project()
method and use the complete name of the project as an argument.
We must use a closure to define the tasks and properties of the project.allprojects{}
script block to apply project tasks and properties to all projects
that are part of the multi-project build.subprojects{}
script block apply only tasks and properties of the subprojects of a multi-project build are configured.build.gradle
file, we must use the configure()
method.org.gradle.api.DefaultTask
.
To indicate that the method is the action of the class, we will use the @TaskAction
annotation.org.gradle.api.Plugin<T>
interface and apply()
method.