Gradle学习笔记

Gradle

1、什么是Gradle?

Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建工具,适用于自动化地进行软件构建、测试、发布、部署、软件打包的项目。

2、为什么要用Gradle?

它既能像Maven一样自动下载依赖,也能像Ant一样灵活定制化打包,自定义脚本。

3、Gradle特点

  • 基于 groovy,其 build 脚本使用 groovy dsl 编写(更灵活,表达更丰富)

  • 通用的灵活的构建工具

  • 一种可切换的,约定优于配置的构建框架

  • 强大的依赖管理

4、Projects和Tasks

projects和tasks是Gradle中最重要的两个概念。

任何一个Gradle构建都是由一个或多个projects组成。每个project包括多个可构建组成部分。

每个project都由多个task组成。每个task都代表了构建执行过程中的一个原子性操作。如:编译、打包、发布到某个仓库等等。

5、构建基础

执行构建时,gradle会寻找build.gradle文件来执行构建。build.gradle文件就是构建配置的脚本。

Groovy代码即脚本。

示例:build.gradle

//采用java、eclipse插件
apply plugin: 'java'        
apply plugin: 'eclipse' 	//若要把项目导入eclipase,则需要该插件
    
//自定义MANIFEST.MF
sourceCompatibility = 1.5  	//JDK版本
version = '1.0'				//项目版本
jar {
    manifest {
        attributes 'Implementation-Title': 'Gradle Quickstart', 'Implementation-Version': version
    }
}

//采用maven中央仓库
repositories {
    mavenCentral()    
}

//添加依赖
dependencies {        
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
    testCompile group: 'junit', name: 'junit', version: '4.+'
}
test {
    systemProperties 'property': 'value'
}

//发布jar包,这里是发布到本地,也可以发布到一个或多个远程仓库中
uploadArchives {
    repositories {
       flatDir {
           dirs 'repos'
       }
    }
}

多项目构建:定义一个多项目构建工程需要在根目录创建一个setting配置文件来指明构建包含的项目,这个文件名必须为 setting.gradle。

示例:setting.gradle

include "shared", "api", "services:webservice", "services:shared"

工程依赖:一个工程的jar包可以提供给另外一个工程使用。Gradle在构建需求者之前总是会先构建提供者的工程。

示例:api/build.gradle

dependencies {
    compile project(':shared')
}

多项目构建发布:

示例:api/build.gradle

task dist(type: Zip) {
    dependsOn spiJar
    from 'src/dist'
    into('libs') {
        from spiJar.archivePath
        from configurations.runtime
    }
}
artifacts {
   archives dist
}

web工程构建

/**
  Gradle为web开发提供了两个主要插件,War plugin和jetty plugin。其中War plugin继承自Java plugin,可以用来打jar包。jetty plugin继承自War plugin作为工程部署的容器。
**/
//应用war插件,当执行gradle build时,将会编译、测试、打包工程
apply plugin:'war'
//应用jetty插件,调用gradle jettyRun将会把工程部署到jetty容器中。调用gradle jettyRunWar会打包并启动部署到jetty容器中。
apply plugin:'jetty'

6、依赖管理

依赖管理由两部分构成:找到项目构建需要的依赖 和 构建完成的发布。

apply plugin: 'java'
repositories {
    mavenCentral()
}
dependencies {
    compile group: 'org.hibernate', name: 'hibernate-core', version: '3.6.7.Final'
    testCompile group: 'junit', name: 'junit', version: '4.+'
}
/**
该段脚本意思是Hibernate-core.3.6.7.final.jar是编译期必需的依赖。并且它相关的依赖也会一并被加载进来,同时还声明项目测试阶段需要 4.0 版本以上的 Junit。同时告诉 Gradle 可以去 Maven 中央仓库去找这些依赖。
**/

依赖配置:

Gradle中每个配置只针对一组依赖(依赖以组的形式划分配置)。

compile:编译范围依赖,在所有的classpath中可用,也会被打包。

runtime:在运行和测试时需要,但编译时不需要。

testCompile:测试期编译需要的附加依赖。

testRuntime:测试期运行需要的依赖。

//快速定义外部依赖
dependencies {
    compile 'org.hibernate:hibernate-core:3.6.7.Final'
}

打包发布

//发布到本地maven仓库
apply plugin: 'maven'
uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: "file://localhost/tmp/myRepo/")
        }
    }
}

7、编写构建脚本

1)Project API

在构建的每一个项目中,Gradle都会创建一个Project类型的实例 ,并在构建脚本中关联此Project对象。

在构建脚本中,如果你任何调用的方法、访问的属性没有定义的话它将被委托给Project对象。

名称 类型 描述
project project project实例
name String 项目目录名
path String 项目绝对路径
description String 项目描述
projectDir File 项目包含生成脚本的目录
group Object 组
version Object 版本

2)Script API

当Gradle执行一个脚本时,它将脚本编译为一个实现了Script接口的类,所以在脚本中可以使用由该Script接口声明的属性和方法。

可以在脚本中声明的变量有两类:局部变量和额外属性。

局部变量:用def关键字声明。只在定义范围内可以被访问。

示例:build.gradle

//复制任务,从原路径到目标路径
def dest = "dest"
task copy(type: Copy){
    from "source"
    into dest
}

额外属性:Gradle的域模型中,所有增强的对象都可以包含用户定义的额外的属性。包括但不限于项目(project)、任务(task)和源码集(sourceset)。额外属性可以通过所属对象的ext属性进行添加、读取和设置,可以使用ext块同时添加多个属性。

示例:build.gradle

apply plugin: "java"
//将两个额外属性添加到project对象中
ext {
    springVersion = "3.1.0.RELEASE"
    emailNotification = "build@master.org"
}
//名为purpose的属性添加到源码集并设置为null
sourceSets.all { ext.purpose = null }
sourceSets {
    main {
        purpose = "production"
    }
    test {
        purpose = "test"
    }
    plugin {
        purpose = "production"
    }
}
task printProperties << {
    println springVersion
    println emailNotification
    sourceSets.matching { it.purpose == "production" }.each { println it.name }
} 

//结果如下:
> gradle -q printProperties
3.1.0.RELEASE
build@master.org
main
plugin 

创建目录:有时多个任务都依赖某个目录。可以在这些任务的开始加入mkdir来解决这个问题,但是比较臃肿,下面的解决方案适用于需要在这个目录的任务有dependsOn关系的情况。

示例:buile.gradle

classesDir = new File('build/classes')
task resources << {
    classesDir.mkdirs()
    // do something
}
task compile(dependsOn: 'resources') << {
    if (classesDir.isDirectory()) {
        println 'The class directory exists. I can operate'
    }
    // do something
} 

//结果如下:
> gradle -q compile
The class directory exists. I can operate 

属性文件:通过gradle.properties属性文件,可以设置属性和以systemProp.为前缀设置系统属性。

示例:gradle.properties

gradlePropertiesProp=gradlePropertiesValue
systemProp.system=systemValue

build.gradle

task printProps << {
    print gradlePropertiesProp
    print System.properties['system']
}

//结果如下:
> gradle -q -PcommandLineProjectProp=commandLineProjectPropValue -Dorg.gradle.project.systemProjectProp=systemPropertyValue printProps
gradlePropertiesValue
systemValue  

使用外部脚本配置项目

示例:build.gradle

apply from: 'other.gradle'

缓存:为了提高响应速度,默认情况下gradle会缓存所有已编译的脚本,包括构建脚本、初始化脚本和其他脚本。第一次运行一个项目构建时,Gradle会创建.gradle目录存放已编译的脚本。如果该脚本没有被修改,则Gradle会使用该已编译的脚本,否则会重新编译,把最新版本存在缓存中。可以通过 --recompile-scripts 选项运行Gradle来丢弃缓存的脚本,重新编译并放入缓存中。

8、任务详述

使用替代语法定义任务

示例:build.gradle

//将任务添加到tasks集合
tasks.create(name: 'hello') << {
    print "hello"
}
tasks.create(name: 'copy', type: Copy){
    from(file('srcDir'))
    into(buildDir)
}

定位任务

//1、以属性方式访问任务
task hello
println hello.name

//2、通过tasks集访问任务
task hello
println tasks.hello.name
    
//3、通过路径访问任务
project(':projectA'){
    task hello
}
task hello
println tasks.getByPath('hello').path    

配置任务

task myCopy(type: Copy)
myCopy {
   from 'resources'
   into 'target'
   include('**/*.txt', '**/*.xml', '**/*.properties')
} 

添加依赖

//使用闭包添加依赖
task taskX << {
    println 'taskX'
}
taskX.dependsOn {
    tasks.findAll { task -> task.name.startsWith('lib') }
}
task lib1 << {
    println 'lib1'
}
task lib2 << {
    println 'lib2'
}
task notALib << {
    println 'notALib'
} 

//结果如下:
> gradle -q taskX
lib1
lib2
taskX      

任务排序

task taskX << {
    println 'taskX'
}
task taskY << {
    println 'taskY'
}
//taskY在taskX后执行,任务排序不以为任务执行
taskY.shouldRunAfter taskX 

替换任务(重写任务)

通过Java插件添加的一个任务与不同类型的自定义任务进行交换

task copy(type: Copy)
task copy(overwrite: true) << {
    println('我是重写的新任务。')
} 

跳过任务

使用 onlyIf()方法将断言附加到一项任务中。如果断言结果为 true,才会执行任务的操作。你可以用一个闭包来实现断言。闭包会作为一个参数传给任务,并且任务应该执行时返回 true,或任务应该跳过时返回 false。

task hello << {
    println 'hello world'
}
hello.onlyIf { !project.hasProperty('skipHello') }  

启用禁用任务

每一项任务有默认值为true的enabled标记。如果设置为false,就可以不让这个任务执行。

task task01 << {
    print '任务执行...'
}
//禁用任务
task01.enabled = false

Gradle怎么实现检查确定任务是否为最新状态?

在第一次执行任务之前对数据进行一次快照,包含输入文件集和每个文件内容的Hash值。执行该任务后,如果成功执行,将对输出也进行一份快照,保存这两份快照直到任务的下一次执行。在以后的每一次执行任务之前都会对输入输出进行一次新的快照,如果新的快照与之前相同,Gradle会假定输出是最新状态的并跳过该任务。如果任务有一个指定的输出目录,在它上一次执行之后添加到该目录的所有文件都将被忽略,并不会使这个任务成为过时状态。

9、使用文件

/**
  可以把任何对象传递给file()方法,它将其转换为一个绝对路径的file对象。一般是一个String或File的实例,这个对象的tostring()方法的值会作为文件路径。
**/

//定位文件
File confFile = file('src/config.xml')
//创建文件集合
FileCollection collection = files('src/file1.txt', new File('src/file2.txt'), ['src/file3.txt', 'src/file4.txt'])
//迭代文件集合
collection.each {
    File file -> print file.name
}
//可以用as转换成其他类型的对象集合
String path = collection.asPath
//集合增删元素
def union = collection + files('src/file3.txt')
def different = collection - files('src/file3.txt')  

文件树:按层次排序的文件集合。

//创建文件树
FileTree tree = fileTree(dir: 'src/main')
//在树中添加包含和排除模式 
tree.include '**/*.java'
tree.exclude '**/Abstract*'
//迭代文件树
tree.each {File file ->
    println file
}
//遍历树节点
tree.visit {element ->
    println "$element.relativePath => $element.file"
}   

指定一组输入文件

compile {
    //用文件路径方式
    source 'src/main/java', 'src/main/groovy'
    //用File对象方式
    source file('../shared/java')
    //用闭包方式                                                                                   
    source { file('src/test/').listFiles() }
} 

Sync任务:Sync任务继承了Copy任务,同步任务,将源文件复制到目标目录下,然后从目标目录移除所有不是它复制的文件。

//使用同步任务复制依赖项
task libs(type: Sync) {
    from configurations.runtime
    into "$buildDir/libs"
}  

归档文件

//用java插件,它对归档任务添加了一些默认值
apply plugin: 'java'
task zip(type:Zip){
    from 'src/dist'
    into('libs') {
        from configurations.runtime
    }
    //生成的归档文件默认名称是projectName-version.type,可以自定义归档名称
    baseName = 'customName'
}

Gradle日志

//使用标准输出写日志
print '一个QUIET等级的日志消息'

//编写自己的日志消息
logger.quiet('一个经常被记录的INFO日志')
logger.error('一个错误日志')
logger.warn('一个警告日志')
logger.lifecycle('一个生命周期日志')
logger.info('一个INFO日志')
logger.debug('一个DEBUG日志')
logger.trace('一个跟踪日志') 

//使用SLF4j编写日志消息
import org.slf4j.Logger
import org.slf4j.LoggerFactory
Logger slf4jLogger = LoggerFactory.getLogger('some-logger')
slf4jLogger.info('一个用SLF4j记录的INFO日志') 

学习内容来源:https://www.w3cschool.cn/gradle/

posted @ 2021-04-06 16:45  Leil_blogs  阅读(303)  评论(0)    收藏  举报