gradle基础学习
http://www.cnblogs.com/davenkin/p/gradle-learning-1.html
https://www.jianshu.com/p/001abe1d8e95
http://blog.csdn.net/yanbober/article/details/49314255
和Maven一样,Gradle只是提供了构建项目的一个框架,真正起作用的是Plugin。与Maven不同的是,Gradle不提供内建的项目生命周期管理,
只是java Plugin向Project中添加了许多Task,Task被组织成了一个有向无环图(DAG),这些Task依次执行,为我们营造了一种如同Maven般项目构建周期。
现在我们都在谈领域驱动设计,Gradle本身的领域对象主要有Project和Task。
Project为Task提供了执行上下文,所有的Plugin要么向Project中添加用于配置的Property,要么向Project中添加不同的Task。
Gradle的Project从本质上说只是含有多个Task的容器,所有的Task都存放在Project的TaskContainer中,Project中的tasks属性即表示该TaskContainer。
$ gradle tasks——Project中所有的Task;$ gradle projects——显示所有Project,包括根Project和子Project;$ gradle properties——显示一个Project所包含的所有Property。
GRADLE的生命周期
在配置阶段,Gradle将读取所有build.gradle文件的所有内容来配置Project和Task等,比如设置Project和Task的Property,处理Task之间的依赖关系等。
事实上,对于每一个Task,Gradle都会在Project中创建一个同名的Property,另外,Gradle还会创建一个同名的方法,该方法接受一个闭包。
Groovy动态地为name创建了getter和setter,而在内部,Groovy依然是在调用setter和getter方法。
Task的创建
task helloWorld << { println "Hello World!"} //这里的“<<”表示向helloWorld中加入执行代码(其实就是groovy代码),即doLast
task helloWorld {
doFirst{println 'doFirst'}
doLast{println 'doLast'} }
tasks.create(name: 'helloWorld') << { println 'helloWorld' }
4.times { counter -> task "task$counter" << { println "I'm task number $counter" } } //动态创建task1,tasl2,task3,task4 //4.times { task "task$it" << { println "I'm task number $it" } }
执行命令:
gradle task1 task2 [...]
gradle -x task1 task2 [...] //排除任务命令
可以使用-b参数选择其他目录的构建文件(还可以使用-p参数来指定构建的目录),且当使用此参数时settings.gradle将不会生效;
task hello << {
println "using build file '$buildFile.name' in '$buildFile.parentFile.name'."
}
gradle -b other-project/build.gradle hello;
gradle -p other-project hello;
-q参数会抑制Gradle的日志输出,只有任务输出
task有无action的区别:task noActionTask { println 'I am noActionTask' }
无action的Task在脚本初始化initialization阶段(无论执行啥task)被执行。
在执行一个Task之前,我们通常都需要先设定Property的值,Gradle提供了多种方法设置Task的Property值。
task hello << {
description = "this is hello"
println description }
task hello << { println description }
hello { description = "this is hello" }
通过调用Task的configure()方法完成Property的设置:
task hello << {
println description
}
hello.configure {
description = "this is hello"
}
Gradle允许在脚本中定义一个或多个默认任务:
defaultTasks 'myclean', 'myrun' //override clean 方法可能会有问题
gradle命令会自动执行默认任务,不需要指定task-name
声明Task之间的依赖关系
task taskA(dependsOn: taskB) { //do something
}
task taskA{ //do somthing
}
taskA.dependsOn taskB
可以通过$将一个task作为另一个task的属性,如下:
task hello << {
println 'Hello world!'
}
hello.doLast {
println "Greetings from the $hello.name task."
}
Groovy闭包的delegate机制
闭包的ResolveStrategy在默认情况下是OWNER_FIRST,
即它会先查找闭包的owner(这里即parent),如果owner存在,则在owner上执行闭包中的代码。定义Task的闭包将delegate设置成了当前的Task。
class Child { private String name }
class Parent {
Child child = new Child();
void configChild(Closure c) {
c.delegate = child
//将该方法接受的闭包的delegate设置成了child,然后将该闭包的ResolveStrategy设置成了DELEGATE_FIRST。
c.setResolveStrategy Closure.DELEGATE_FIRST
c()
} }
def parent = new Parent()
parent.configChild { name = "child name" }
configurations()方法会将所跟闭包的delegate设置成ConfigurationContainer,然后在该ConfigurationContainer上执行闭包中的代码。
dependencies()方法会将所跟闭包的delegate设置成DependencyHandler
Project还定义了configure(Object object,Closure configureClosure)方法,该方法是专门用来配置对象的(比如Task),它会将configureClosure的delegate设置成object,之后configureClosure中的执行代码其实是在object上执行的
增量式构建
如果多次执行一个Task时的输入和输出是一样的,那么我们便可以认为这样的Task是没有必要重复执行的,是冗余的、耗时的。
在增量式构建中,我们为每个Task定义输入(inputs)和输入(outputs),在执行一个Task时,如果它的输入和输出与前一次执行时没有发生变化,那么Gradle便会认为该Task是最新的(UP-TO-DATE),因此Gradle将不予执行。
每个Task都拥有inputs和outputs属性,他们的类型分别为TaskInputs和TaskOutputs。
task combineFileContentIncremental {
def sources = fileTree('sourceDir')
def destination = file('destination.txt')
inputs.dir sources
outputs.file destination
doLast {
destination.withPrintWriter { writer ->
sources.each {source ->
writer.println source.text }
}}}
当首次执行combineFileContentIncremental时,Gradle会完整地执行该Task。紧接着再执行一次,命令行显示:
:combineFileContentIncremental UP-TO-DATE
BUILD SUCCESSFUL
Total time: 2.104 secs
combineFileContentIncremental被标记为UP-TO-DATE,表示该Task是最新的,Gradle将不予执行。
如果修改了inputs(即sourceDir文件夹)中的任何一个文件或删除destination.txt,调用“gradle combineFileContentIncremental”时,Gradle又会重新执行。
Project的Property
Gradle在默认情况下已经为Project定义了很多Property,其中比较常用的有:
project:Project本身;name:Project的名字;
path:Project的绝对路径;
description:Project的描述信息(Project和Task都拥有description属性);
buildDir:Project构建结果存放目录;version:Project的版本号;
1.在build.gradle文件中定义Property
在build.gradle文件中向Project添加额外的Property时,应该通过ext来定义。(任何实现了ExtensionAware接口的Gradle对象都可以通过这种方式来添加额外的Property)
ext.property1 = "this is property1"
也可以通过闭包的方式:
ext { property2 = "this is property2" }
在定义了Property后,使用这些Property时我们则不需要ext,而是可以直接访问。
2.通过命令行参数定义Property
在调用gradle命令时,通过-P参数传入该Property:gradle -Pproperty3="this is property3" showCommandLieProperties
3.通过JVM系统参数定义Property
gradle -Dorg.gradle.project.property3="this is another property3" showCommandLieProperties
4.通过环境变量设置Property
export ORG_GRADLE_PROJECT_property3="this is yet another property3"
局部变量使用关键字def声明: def dest = "dest"
apply from: rootProject.getRootDir().getAbsolutePath() + "/common.gradle" //引入gradle文件
Plugin
插件分脚本插件和二进制插件。
分脚本插件是额外的构建脚本,需要配置构建,通常会在构建内部使用。脚本插件可以从本地文件系统或远程获取,如果从文件系统获取则是相对于项目目录,如果是远程获取则是由HTTP URL指定。
二进制插是实现了Plugin接口的类,并且采用编程的方式来操纵构建。
apply plugin: 'java'
apply plugin: JavaPlugin //插件还可以使用插件ID
buildscript {
repositories { jcenter() }
dependencies { classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:0.4.1" }
}
apply plugin: "com.jfrog.bintray"
Java Plugin
Java Plugin并没有什么特别的地方,只是向Project中引入了多个Task和Property并没有什么特别的地方,只是向Project中引入了多个Task和Property。
执行“gradle build”:
:compileJava
:processResources
:classes
:jar
:assemble
:compileTestJava
:processTestResources
:testClasses
:test
:check
:build
BUILD SUCCESSFUL
Total time: 4.813 secs
由Plugin向Project中引入了多个Task构建生命周期。
build task :Gradle编译和测试代码,并且创建一个包含类和资源的JAR文件。
clean task :Gradle删除build生成的目录和所有生成的文件。
assemble task :Gradle编译并打包代码,但是并不运行单元测试。
check task :Gradle编译并测试代码,其他的插件会加入更多的检查步骤。
Gradle默认希望能在src/main/java路径下找到源代码,在 src/test/java路径下找到测试代码。所有src/main/resources路径的文件都会被包含在JAR文件里,所有src/test/resources路径的文件都会被加入到classpath中以运行测试代码,所有的输出文件将会被创建在构建目录里,JAR文件存放在 build/libs文件夹里。
java Plugin还向Project中加入了一些额外的Property。比如,sourceCompatibility用于指定在编译Java源文件时所使用的Java版本,archivesBaseName用于指定打包成Jar文件时的文件名称。
source set
对于上图中的目录结构,Gradle实际上创建了2个source set,一个名为main,一个名为test。
Gradle的每个source set都包含有一个名字,并且包含有一个名为java的Property和一个名为resources的Property:
sourceSets {
main {//名字
java {//名为java的Property
srcDir 'java-sources'
}
resources {//resources的Property
srcDir 'resources'
}
}
}
在默认情况下,source set所对应的Java源文件目录被Gradle设置为${path-to-project}/src/<source_set_name>/java,而资源文件目录则被设置成了${path-to-project}/<source_set_name>/resources。
Gradle会自动地为每一个新创建的source set创建相应的Task:compile<source_set_name>Java、process<source_set_name>Resources和<source_set_name>Classes这3个Task。(由于main是Gradle默认创建的source set,并且又是及其重要的source set,Gradle便直接使用了compileJava作为main的编译Task)
关于source set 的依赖:
sourceSets {api}
1.需要在编译main之前对api进行编译: classes.dependsOn apiClasses
2.将api编译生成的class文件放在main的classpath下
sourceSets {
main {
compileClasspath = compileClasspath + files(api.output.classesDir)
}
test {
runtimeClasspath = runtimeClasspath + files(api.output.classesDir)
}
}
//定制 MANIFEST.MF 文件
sourceCompatibility = 1.5
version = '1.0'
jar {
manifest { attributes 'Implementation-Title': 'Gradle Quickstart', 'Implementation-Version': version }
}
//发布jar到本地文件
uploadArchives { repositories { flatDir { dirs 'repos' } } }
//Gradle会将该Plugin打包成jar文件,然后将其上传到上级目录下的lib目录中
uploadArchives {
repositories.mavenDeployer {
repository(url: 'file:../lib')
}
}
依赖管理:
通常来说,这种依赖的表示形式都是将第三方的Jar文件放在自己项目的classpath下(要么是编译时的classpath,要么是运行时的classpath)。
Repository:告诉Gradle在什么地方去获取这些依赖,比如
mavenCentral()
maven { url "http://repo.mycompany.com/maven2" }
ivy { // URL can refer to a local directory
url "../local-repo" }
Configuration:每一组依赖称为一个Configuration,在声明依赖时,我们实际上是在设置不同的Configuration。
configurations { myDependency }
我们可以通过dependencies()方法向myDependency中加入实际的依赖项
dependencies { myDependency 'org.apache.commons:commons-lang3:3.0' }
task showMyDependency << { println configurations.myDependency.asPath }
java Plugin会自动定义compile和testCompile,分别用于编译Java源文件和编译Java测试源文件。ava Plugin还定义了runtime和testRuntime这两个Configuration,分别用于在程序运行和测试运行时加入所配置的依赖。
如果存在依赖冲突,在默认情况下,Gradle会选择最新版本;Maven会选择离依赖树最近的版本。(可以通过设置Configuration的resolutionStrategy来重新设置依赖冲突的处理规则)
dependencies { compile files('spring-core.jar', 'spring-aap.jar') compile fileTree(dir: 'deps', include: '*.jar') }
基本结构的Android多Module(也就是gradle中的多Project Multi-Projects Build)的基本项目结构
Gradle为每个build.gradle都会创建一个相应的Project领域(Module)对象,在编写Gradle脚本时,我们实际上是在操作诸如Project这样的Gradle领域对象。
要创建多Project的Gradle项目,我们首先需要在根(Root)Project中加入名为settings.gradle的配置文件,该文件应该包含各个子Project的名称。
rootProject.name
include 'XXX'
在最外层的build.gradle,一般干得活是:配置其他子Project的,比如为子Project添加一些属性。
在Gradle中,我们可以通过根Project的allprojects()方法将配置一次性地应用于所有的Project,当然也包括定义Task
allprojects {
apply plugin: 'idea'
task allTask << { println project.name }
}
除了allprojects()之外,Project还提供了subprojects()方法用于配置所有的子Project(不包含根Project)。比如,我们可以定义Task来只输出各个子Project的名字:
subprojects {
task subTask << { println project.name }
}
configure(allprojects.findAll { it.name.startsWith('sub') }) {
subTask << { println 'this is a sub project' }
}
还可以配置单个project(只有当一个Task没有在任何Project中定义时,Gradle才会将其当做异常。否则,Gradle会在所有拥有该Task的Project上执行该Task。)
project(':sub-project1') {
task forProject1 << { println 'for project 1' }
}
一旦有了多个Project,他们之间便会存在着依赖关系。Gradle的Project之间的依赖关系是基于Task的,而不是整个Project的
dependencies {compile project(':ProjectB')}
taskA.dependsOn ':sub-project2:taskC'
taskB.dependsOn ':sub-project2:taskD'
自定义Task
Task类型:DefaultTask、Copy和Jar等;也可以自定义Task类型
task copyFile(type: Copy){
from "src"
into "dst”
}
class HelloWorldTask extends DefaultTask {
@Optional
String message = 'I am davenkin'
//@TaskAction表示该Task要执行的动作,即在调用该Task时,hello()方法将被执行
@TaskAction
def hello(){ println "hello world $message" }
}
task hello(type:HelloWorldTask)
task hello1(type:HelloWorldTask){ message ="I am a programmer" }
在当前工程中定义Task类型
Gradle在执行时,会自动地查找buildSrc目录目录下所定义的Task类型,并首先编译该目录下的groovy代码以供build.gradle文件使用。
在buildSrc//src/main/groovy/davenkin目录下创建HelloWorldTask.groovy文件
package davenkin
import org.gradle.api.*
import org.gradle.api.tasks.*
class HelloWorldTask extends DefaultTask {
@Optional
String message = 'I am davenkin'
@TaskAction
def hello(){ println "hello world $message" }
}
使用:
task hello(type:davenkin.HelloWorldTask)
task hello1(type:davenkin.HelloWorldTask){ message ="I am a programmer" }
在单独的项目中定义Task类型
新建项目,将buildSrc//src/main/groovy/davenkin/HelloWorldTask.groovy拷贝过来,build.gradle如下:
apply plugin: 'groovy'
apply plugin: 'maven'
version = '1.0'
group = 'davenkin'
archivesBaseName = 'hellotask'
repositories.mavenCentral()
dependencies {
compile gradleApi()
groovy localGroovy()
}
在使用该HelloWorldTask时,客户端的build.gradle文件可以做以下配置:
配置repository,声明对HelloWorldTask的依赖,该依赖用于当前build文件。
buildscript {
repositories {
maven { url 'file:../lib' }
}
dependencies {
classpath group: 'davenkin', name: 'hellotask', version: '1.0'
}
}
使用:
task hello(type: davenkin.HelloWorldTask)
自定义Plugin
每一个自定义的Plugin都需要实现Plugin<T>接口,事实上,除了给Project编写Plugin之外,我们还可以为其他Gradle类编写Plugin。
apply plugin: DateAndTimePlugin
class DateAndTimePlugin implements Plugin<Project> {
//该接口定义了一个apply()方法,在该方法中,我们可以操作Project,比如向其中加入Task,定义额外的Property等
void apply(Project project) {
//每个Gradle的Project都维护了一个ExtenionContainer,我们可以通过project.extentions进行访问
project.extensions.create("dateAndTime", DateAndTimePluginExtension)
project.task('showTime') << { println "Current time is " + new Date().format(project.dateAndTime.timeFormat) }
project.tasks.create('showDate') << { println "Current date is " + new Date().format(project.dateAndTime.dateFormat) }
}
}
class DateAndTimePluginExtension {
String timeFormat = "MM/dd/yyyyHH:mm:ss.SSS"
String dateFormat = "yyyy-MM-dd"
}
//通过再定义进行重新配置:
dateAndTime {
timeFormat = 'HH:mm:ss.SSS'
dateFormat = 'MM/dd/yyyy'
}
在当前工程中定义
在buildSrc//src/main/groovy/davenkin目录下创建DateAndTimePlugin.groovy文件
在buildSrc//src/main/groovy/davenkin目录下创建DateAndTimePluginExtension.groovy文件
package davenkin
import org.gradle.api.Project
import org.gradle.api.Plugin
class DateAndTimePlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create("dateAndTime", DateAndTimePluginExtension)
project.task('showTime') << { println "Current time is " + new Date().format(project.dateAndTime.timeFormat) }
project.tasks.create('showDate') << { println "Current date is "
+ new Date().format(project.dateAndTime.dateFormat) }
}
}
package davenkin
class DateAndTimePluginExtension {
String timeFormat = "MM/dd/yyyyHH:mm:ss.SSS"
String dateFormat = "yyyy-MM-dd"
}
使用:
apply plugin: davenkin.DateAndTimePlugin
dateAndTime {
timeFormat = 'HH:mm:ss.SSS'
dateFormat = 'MM/dd/yyyy'
}
执行“gradle showTime”,命令行输出如下:
:buildSrc:compileJava UP-TO-DATE
:buildSrc:compileGroovy UP-TO-DATE
:buildSrc:processResources UP-TO-DATE
:buildSrc:classes UP-TO-DATE
:buildSrc:jar UP-TO-DATE
:buildSrc:assemble UP-TO-DATE
:buildSrc:compileTestJava UP-TO-DATE
:buildSrc:compileTestGroovy UP-TO-DATE
:buildSrc:processTestResources UP-TO-DATE
:buildSrc:testClasses UP-TO-DATE
:buildSrc:test UP-TO-DATE
:buildSrc:check UP-TO-DATE
:buildSrc:build UP-TO-DATE
:showTime
Current time is 19:08:35.489
BUILD SUCCESSFUL
Total time: 2.995 secs
在单独的项目中定义
新建项目,将buildSrc//src/main/groovy/davenkin下DateAndTimePlugin.groovyh和DateAndTimePluginExtension.groovyh拷贝过来,build.gradle如下:
apply plugin: 'groovy'
apply plugin: 'maven'
version = '1.0'
group = 'davenkin'
archivesBaseName = 'datetimeplugin'
repositories.mavenCentral()
dependencies {
compile gradleApi()
groovy localGroovy()
}
此外,我们还可以为该Plugin重新命名为time:在src/main/resources/META-INF/gradle-plugins目录下创建名为time.properties的文件,内容如下: implementation-class = davenkin.DateAndTimePlugin
在客户端的build.gradle文件中:
配置repository,声明对HelloWorldTask的依赖,该依赖用于当前build文件。
buildscript {
repositories {
maven { url 'file:../lib' }
}
dependencies {
classpath group: 'davenkin', name: 'datetimeplugin', version: '1.0'
}
}
使用:
apply plugin: 'time'
dateAndTime {
timeFormat = 'HH:mm:ss.SSS'
dateFormat = 'MM/dd/yyyy'
}
Gradle 文件操作
获取或创建
Project.file()方法来定位一个文件获取File对象
File configFile = file('src/config.xml')//相对路径
File configFile = file(configFile.absolutePath) //绝对路径
File configFile = file(new File('src/config.xml'))//项目路径的文件对象
file()方法还能识别URL,如file:/some/path.xml等
FileCollection collection = files('src/file1.txt', new File('src/file2.txt'), ['src/file3.txt', 'src/file4.txt'])
def union = collection + files('src/file3.txt')
def different = collection - files('src/file3.txt')
文件树可以代表一个目录树结构或一个ZIP压缩文件的内容
FileTree tree = fileTree(dir: 'src/main')
FileTree zip = zipTree('someFile.zip')
FileTree tar = tarTree('someFile.tar')
FileTree someTar = tarTree(resources.gzip('someTar.ext'))
// 使用闭合创建一个数
tree = fileTree('src') { include '**/*.java' }
// 使用map创建一个树
tree = fileTree(dir: 'src', include: '**/*.java')
method:include;exclude ;each;matching;visit等
复制文件
task CopyTask(type: Copy){
from 'src/main/webapp'
from 'src/staging/index.html'
from otherCopyTask
from anotherCopyTask .outputs
from zipTree('src/main/assets.zip')
into { getDestDir() }
include '**/*.html'
include '**/*.jsp'
exclude { details -> details.file.name.endsWith('.html') && details.file.text.contains('staging')
rename { String fileName -> fileName.replace('-staging-', '')
}
文件同步
同步任务(Sync)继承自复制任务(Copy),当执行时会复制源文件到目标目录,然后从目标目录删除所有非复制文件。
task libs(type: Sync) {
from configurations.runtime
into "$buildDir/libs"
}
task zip(type: Zip){
from 'src/dist'
into('libs') { from configurations.runtime }
}
Ant的调用
task loadfile << {
def files = file('../antLoadfileResources').listFiles().sort()
files.each { File file ->
if (file.isFile()) {
ant.loadfile(srcFile: file, property: file.name)
println " *** $file.name ***"
println "${ant.properties[file.name]}"
} }}
gradle.taskGraph.whenReady {taskGraph ->
if (taskGraph.hasTask(release)) {
version = '1.0'
} else {
version = '1.0-SNAPSHOT'
}
}
Gradle Wrapper
Wrapper,就是对Gradle的一层包装,便于在团队开发过程中统一Gradle构建的版本
生成wrapper:$ gradle wrapper