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  阅读(195)  评论(0编辑  收藏  举报