Intro
Gradle is a build and automation tool and it is JVM based, implemented in java.
Creating build scans (Gradle Cloud services)
Build scan is used to see
what's happen in the build process;
what's the performance of this build.
Run the build with -Dscan
, will generate the build scan and will publish to gradle server. A build scan contains data about the build (project names, file paths, host names).
An example snippet of build scan block in the gradle script is the following:
// Adds build scan functionality
plugins {
id 'com.gradle.build-scan' version '1.2'
}
buildScan {
licenseAgreementUrl = 'https://gradle.com/terms-of-service'
licenseAgree = 'yes'
tag 'TRAINING' // Not mandantory
}
// End build scan functionality
Also, if you want always generate a build scan, following block can be provided in the gradle script
buildScan {
publishAlways()
}
An option to create a build scan for all projects, ~/.gralde/init.d/buildScan.gradle
can provided to satisfy this requirement.
initscript {
repositories {
maven { url 'https://plugins.gradle.org/m2' }
}
dependencies {
classpath 'com.gradle:build-scan-plugin:1.2'
}
}
rootProject {
apply plugin: com.gradle.scan.plugin.BuildScanPlugin
buildScan {
licenseAgreementUrl = 'https://gradle.com/terms-of-service'
licenseAgree = 'yes'
}
}
Gradle wrapper
It is used to solve that projects are built by different gradle version.
run gradle wrapper --gradle-version=<version>
to build gradle wrapper for this project
Under ./gradle/wrapper/
in the project:
gradle-wrapper.jar: Micro-library for downloading distribution
gradle-wrapper.properties: Defines distribution download URL and options
Under the project:
gradlew: Gradle excutable for *nix systems
gradlew.bat: Gradle excutable for Windows systems
Run ./gradlew <task>
to use this wrapper to build
For version control system, usually gradle
folder and the gradle scripts in the project dir to share.
NOTE1: Config environment variable to this directory, run
gradlew <task>
all the time.NOTE2: If do not specify gradle version to use, it will default by using the current gradle version.
NOTE3: There is nightly build here.
Gradle build Daemon
For gradle 1.x/2.x, three options to enable daemon:
Running
--daemon
in the command to enable daemon.Set
org.gradle.daemon=true
ingradle.properties
.Set
-Dorg.gradle.daemon=true
inGRADLE_OPTS
.
The daemon is enabled by default in Gradle 3.x.
Gradle idea plugin
idea
plugin provides two tasks: idea
and cleanIdea
By running gradle idea
, it will generate *.iml
, *.ipr
and *.iws
meta data for idea IDE. Click ipr would directly open project in IntelliJ without import project.
Tasks
Tasks are the basic unit of work in gradle. Tasks have a list of actions, doLast
and doFirst
can be used to decorate the action. They are closures.
If the actions are provided in the task block directly line by line, it will implemented in the configuration phase. On the other hand, if these actions are provided in doLast
and doFirst
closures, the closure is a mean to making the build script look like a DSL essentially. It will only be executed by call the task in the execution phase, not in the configuration phase.
Three build phases in build lifecycle: Initialization, Configuration, and Execution. Detail
-
Initialization Phase
Configure environment(init.gradle, gradle.properties)
Find projecs and build scripts(settings.gradle)
-
Configuration Phase
Evaluate all build scripts
Build object model (Gradle -> Project -> Task, etc.)
Build task exection graph (task dependency)
-
Execution Phase
Execute (subset of) tasks
For example:
// Configuration Phase execution
task helloWorld {
println("hello world")
}
// Execution Phase execution
task helloWorld {
doLast {
println("hello world")
}
}
run gradle helloWorld
to see the difference. (hello world
will appear before or after the task, in two tasks.) Or run gradle tasks
to see whether hello world
is appear or not in two cases.
More info about the task api, provided here, i.e. description, group, and etc.
Task types
task type is more like a interface to define the behavior of the task type. In default, task get DefaultTask
.
For example
task helloWorld {
onlyIf { System.getProperty("be.nice") == "true" }
doLast { println "Hello" }
}
onlyIf()
and doLast()
methods are available for all tasks, an api in DefaultTask
task copyFiles(type: Copy) {
from "sourceDir"
into "targetDir"
}
form()
and into()
mehtods are only avaiable for Copy
tasks. Then the underlying copy file action is handled by Copy
task.
Task dependencies
Tasks can depend on each other. Gradle executes tasks form a directed acyclic graph.
For example:
task foo
task bar {
dependsOn foo
}
The --dry-run
(or -m
) command line option is used to execute the build but disable all actions. and gradle tasks --all
will list all the tasks including the depended tasks.
task bar {
doLast {
dependsOn foo
}
}
This is a wrong example, dependsOn
block would not execute in the Congifuration Phase, since it's in the doLast()
closure. If run this task, an exception would throw out.
Note: dependsOn
need be executed in Configuration phase.
Working with the filesystem
All respect to Incremental Build rule.
Important properties:
projectDir (read-only): the base directory of the proejct
buildDir: the build output directory of the project
rootDir (read-only): the base directory of the root project(multi-project)
The buildDir
is $projectDirc/build
by default. But in plugins, don't assume this. Use $buildDirc
to find the exact path.
Relative files
Don't do this new File("src/main/java/Thing.java")
. The working directory of the JVM is not know by default. Instead, by using project.file("src/main/java/Thing.java")
. Project.file(Object)
always resolves relative to the projectDir
.
Copy task
Powerful API, including filtering and transforming.
An exmaple snippet:
-
multiple sources/sub directories
task copyStuff(type: Copy) { exclude "**/.svn" // default into "targetDir" into("targetSubDir") { from "sourceDir" } into("targetSubDir2") { from "sourceDir2", "someFile.txt" } into("targetSubDir3") { from "sourceDir3" include "**/*.jpeg" exclude "**/obsoleteImages/*" } }
-
Transforming:
task copyStuff(type: Copy) { into "targetDir" from("someDir") { // Use Ant's HeadFilter filter(HeadFilter, lines: 25, skip: 2) } from("otherDir") { // Line by line transform filter { line -> line.substring(5) } } from("anotherDir") { // Groovy's SimpleTemplateEngine expand foo: "bar" } }
-
Renaming:
task copyStuff(type: Copy) { into "targetDir" from("someDir") { rename "(.*)_OEM_BLUE_(.*)", '$1$2' } from("otherDir") { eachFile { FileCopyDetails copyDetails -> if (copyDetails.name.length() > 10) { copyDetails.path = "longFileNames/$copyDetails.name" } } } }
More info. Also, sync task has the similar function.
Archives
Task type for each archive type(Zip, Jar, War, Tar, and etc.). similar to copy task: archiving(copying to), unarchiving(copying from), transforming/renaming.
Archiving
Base plugin adds conventional naming defaults. Example:
apply plugin: "base"
version = "1.0"
task zipLibs(type: Zip) {
baseName = "services"
appendix = "api"
// ...
}
baseName -> project.name
appendix -> empty string
version -> project.version
classifier -> empty string
extension -> type extension
Pattern: <baseName>-<appendix>-<version>-<classfier>.<exetension>
Default destination dir for Zip/Tar(by base plugin)
build/distributions
Default destination dir for Jar/War(by java-base plugin)
build/libs
custom destination dir by set
desinationDir
field.
Unarchiving
Use zipTree()
and tarTree()
to specify archive content.
task unpackArchives(type: Copy) {
from zipTree("zip1.zip"), zipTree("jar1.jar")
from(tarTree("tar1.tar")) {
exclude "**/*.properties"
}
from "zip2.zip"
into "unpackDir"
}
Merging
zipTree()
and tarTree()
can be used to merge archives.
task mergedZip(type: Zip) {
from zipTree("someZip.zip")
from zipTree("otherZip.zip")
}
Typical use case: fat jars. (We can use zipTree()
to do with jar file, since jar file is essentially a zip file)
Java Plugin
Source sets
Java source files
Non complied source files(e.g. properties files)
Classpath(compile & runtime)
Output class files
Compilation tasks
sourceSets {
main {
java {
srcDir "src/main/java" // default
}
resources {
srcDir "src/main/resources" // default
}
}
}
Lifecycle Tasks
clean - delete all build output
classes - compile code, provess resources
test - run tests
assemble - make all archives(e.g. zips jars, wars etc.)
check - run all quality checks(e.g. tests + static code analysis)
build - combination of assemble & check
More in here
Native language supoort
Depenedency Management
Supports managed and unmanaged dependecies.
-
"Managed" dependencies have identity and possibly metadata
dependencies { compile "org.springframework:spring-core:4.0.5.RELEASE" compile group: "org.springframework", name: "spring-web", version: "4.0.5.RELEASE" }
Group/Module/Version
-
"Unmanaged" dependecies are just anonymous file.
dependencies { compile fileTree(dir: "lib", include: "*.jar") }
Configure it to the classpath when compile.
Dependencies are assigned to configurations
configurations {
//default with "java" plugin
compile
runtime
testCompile
testRuntime
}
dependencies {
compile "org.springframework:spring-core:4.0.5.RELEASE"
}
More about Configuration
Transitive Dependecies
Gradle (by default) fetches dependencies of your dependencies. This can introduce version conflicts.
Only one version of a given dependency can be part of a configuration.
Options:
Use default strategy (highest version number)
Disable transitive dependency management
Excludes
Force a version
Fail on version conflict
Dependency resolution rules
Dependency configurations
It is a configuration name to identify where it is in the project?
By default, java plugin create
configuration {
main
}
sourceSets {
main {
java.srcDir = 'src/main/java'
}
}
It usually <configuration_name> <dependency_name>
. But if use default format of gradle, where you put source file under src/main/java
, you can simply do compile <denpendency_name>
.
Another example:
Create configuration A
which has dependency x
y
z
, use that to compile files in src/main/a
.
And create configuration B
which has dependency m
n
o
, use that to compile files in src/main/b
.
It is essentially to group dependency together
Repositories
Any Maven/Ivy repository can be used, and very flexible layouts are possible for lvy repositories.
repositories {
jcenter()
mavenCentral()
maven {
name "codehaus"
url "http://repository.codehaus.org"
}
ivy {
url "http://repo.mycompany.com"
layout "gradle" // default
}
flatDir(dirs: ["dir1", "dir2"])
}
Others
Dependency cache's default locaiton ~/.gradle/caches/...
. More features: changin dependencies, dynamic dependencies etc.
dependency basic and dependency management
Use gradle dependencies [--configuration <name>]
to view the dependency graph.
Use gradle dependencyInsight --dependency <name> [--configuration <name>]
to view a dependency in graph.
Multi-Project builds
Flexible directory layout (flat or hierachy)
Configuration injection
Project dependencies & partial builds
Separate configuration/execution hierarchy
Configuration injection
The app hierachy:
-
ultimateApp
api
-
services
webservice
shared
subprojects {
apply plugin: "java"
dependencies {
testCompile "junit:junit:4.7"
}
test {
jvmArgs "-Xmx512M"
}
}
Inject the configuration from the root gradle script.
Project dependecies
-
ultimateApp
api
-
services
webservice
shared
dependencies {
compile "commons-lang:commons-lang:2.4"
compile project(":shared")
}
Project builds
-
ultimateApp
api
-
services
webservice
shared
gradle build
is only to build the artifact in api.
gradle buildDependents
is to build the artifacts in api and the artifacts in other projects that associated or dependended on api.
gradle buildNeeded
is larger than build, and will tests all the artifacts in other projects, it dependended on.
Name Matching Execution
-
ultimateApp
api
-
services
webservice
shared
gradle build
will run all the build in ultimateApp and teh subprojects under ultimateApp.
Task/Project Paths
For projects and tasks there is a fully qualified path notation, :
(root project), :clean
(the clean task of the root project), :api:clasees
(the classes task in api project).
gradle :api:clean
Defining a multi-project build
In settings.gradle
(unique in all and located defines the root).
//declare projects:
include "api", "shared", "services:webservice"
//Everything is configurable:
//default: root dir name
rootProject.name = "main"
//default: "api" dir
project(":api").projectDir = file("/myLocation")
//default: "build.gradle"
project(":shared").buildFileName = "shared.gradle"
Other features
Continue after failure
gradle build --continue
Parallel Builds
gradle build --parallel
(Run independent tasks from different projects in parallel.)Exec
task