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

posted @ 2018-03-01 10:18  wzbin  阅读(442)  评论(0编辑  收藏  举报