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/