导语
本文在Gradle文档 基础上进行了提取整理,篇幅较长,请按需阅读
Gradle概述
Gradle是专注于灵活性和性能的开源构建自动化工具,一般使用Groovy或KotlinDSL编写构建脚本。 本文只使用Groovy
Gradle的特点:
Gradle通过仅运行需要运行的任务来避免不必要的工作。 可以使用构建缓存来重用以前运行的任务输出,甚至可以使用其他计算机(具有共享的构建缓存)重用任务输出。
Gradle在JVM上运行。熟悉Java的用户来可以在构建逻辑中使用标准Java API,例如自定义任务类型和插件。 这使得Gradle跨平台更加简单。(Gradle不仅限于构建JVM项目,它甚至附带对构建本机项目的支持。)
和Maven一样,Gradle通过实现约束使常见类型的项目(例如Java项目)易于构建。 应用适当的插件,您可以轻松地为许多项目使用精简的构建脚本。 但是这些约定并没有限制您:Gradle允许您覆盖它们,添加自己的任务以及对基于约定的构建进行许多其他自定义操作。
您可以轻松扩展Gradle以提供您自己的任务类型甚至构建模型。
支持IDE:Android Studio,IntelliJ IDEA,Eclipse和NetBeans。 Gradle还支持生成将项目加载到Visual Studio所需的解决方案文件。
构建扫描提供了有关构建运行的广泛信息,可用于识别构建问题。他们特别擅长帮助您确定构建性能的问题。 您还可以与其他人共享构建扫描,如果您需要咨询以解决构建问题,这将特别有用。
您需要了解有关Gradle的五件事
本节在官方文档里面反复提及,我大概概括了下,具体可见Gradle文档
1. Gradle是通用的构建工具
Gradle允许您构建任何软件,因为它不关心你具体的工作。
2. 核心模型基于任务
Gradle将其构建模型建模为任务(工作单元)的有向无环图(DAG)。这意味着构建实质上配置了一组任务,并根据它们的依赖关系将它们连接在一起以创建该DAG。创建任务图后,Gradle将确定需要按顺序运行的任务,然后继续执行它们。
任务本身包括以下部分,它们通过依赖链接在一起:
动作-做某事的工作,例如复制文件或编译源代码
输入-操作使用或对其进行操作的值,文件和目录
输出-操作修改或生成的文件和目录
3. Gradle有几个固定的构建阶段
重要的是要了解Gradle分三个阶段评估和执行构建脚本:
初始化
设置构建环境,并确定哪些项目将参与其中。
配置
构造和配置构建的任务图,然后根据用户要运行的任务确定需要运行的任务和运行顺序。
执行
运行在配置阶段结束时选择的任务。
这些阶段构成了Gradle的构建生命周期。
4. Gradle的扩展方式不止一种
Gradle捆绑的构建逻辑不可能满足所有构建情况,大多数构建都有一些特殊要求,你需要添加自定义构建逻辑。Gradle提供了多种机制来扩展它,例如:
自定义任务类型。
自定义任务动作。
项目和任务的额外属性。
自定义约束。
自定义module。
5. 构建脚本针对API运行
可以将Gradle的构建脚本视为可执行代码,但设计良好的构建脚本描述了构建软件需要哪些步骤,而不关心这些步骤应该如何完成工作。
由于Gradle在JVM上运行,因此构建脚本也可以使用标准Java API。Groovy构建脚本可以另外使用Groovy API,而Kotlin构建脚本可以使用Kotlin。
功能的生命周期
功能可以处于以下四种状态之一:
Internal:内部功能,不提供接口
Incubating: 孵化功能。在成为公共功能之前会继续更改
Public:公共功能,可放心使用
Deprecated:废弃功能,将在未来删除
Gradle安装
安装JDK
安装JDK过程已有太多资料,本文不做详细介绍。可使用命令检测自己电脑是否成功安装
安装Gradle
用软件包安装Gradle
SDKMAN
Homebrew
手动安装(推荐方式)
下载
services.gradle.org/distributio… (全部版本目录地址,可以查看最新版本)
services.gradle.org/distributio… (截止至2021.02.24最新)
文件介绍
1 2 3 4 gradle-6.8.3-docs.zip //文档 gradle-6.8.3-src.zip //源码 gradle-6.8.3-bin.zip //软件包 gradle-6.8.3-all.zip //全部文件
1 2 3 4 5 6 7 8 bin :运行文件 lib:依赖库 docs:文档 src:源文件 init.d :初始化脚本目录,可自己添加
配置环境变量
1 2 export GRADLE_HOME=/Users/dxs/temp/gradle-6.8.3 export PATH=$PATH:$GRADLE_HOME/bin
运行
输入gradle -v 检测是否配置成功
HelloWord
编写一个build.gradle文件,输入以下内容
1 2 3 4 5 task hello{ doLast { println 'Hello World' } }
命令行输入gradle -q hello即可运行
版本管理
项目迁移
Gradle Wrapper
定义
Gradle Wrapper是一个脚本,可调用Gradle的声明版本,并在必要时预先下载。因此,开发人员可以快速启动并运行Gradle项目,而无需遵循手动安装过程
添加wrapper
在build.gradle同级目录下使用命令gradle wrapper
可以生成gradle wrapper目录
WrapJAR文件,其中包含用于下载Gradle发行版的代码。
gradle-wrapper.properties
一个属性文件,负责配置Wrapper运行时行为,例如与该版本兼容的Gradle版本。请注意,更多常规设置(例如,将 Wrap配置为使用代理)需要进入其他文件。
一个shell脚本和一个Windows批处理脚本,用于使用 Wrap程序执行构建。
可以通过命令控制生成选项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 --gradle-version 用于下载和执行 Wrap程序的Gradle版本。 --distribution-type Wrap使用的Gradle分布类型。可用的选项是bin和all。默认值为bin。 --gradle-distribution-url 指向Gradle分发ZIP文件的完整URL。使用此选项,--gradle-version并且--distribution- type过时的网址已经包含此信息。如果您想在公司网络内部托管Gradle发行版,则此选项非常有价值。 --gradle-distribution-sha256-sum SHA256哈希和用于验证下载的Gradle分布。
例:
1 gradle wrapper --gradle-version 6.8.3 --distribution-type all
Wrapper属性文件
一般生成Wrapper会得到如下属性文件 gradle-wrapper.properties
1 2 3 4 5 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists
GRADLE_USER_HOME是你的环境变量,如果没配置,则默认是用户目录下的.gradle文件夹
distributionBase 下载的 Gradle压缩包解压后存储的主目录
distributionPath 相对于 distributionBase的解压后的 Gradle压缩包的路径
zipStoreBase 同 distributionBase,只不过是存放 zip压缩包的
zipStorePath 同 distributionPath,只不过是存放 zip压缩包的
distributionUrl Gradle发行版压缩包的下载地址
使用wrapper构建
在 gradlew目录下执行命令:
windows:
shell:
升级
更改gradle-wrapper.properties文件中的distributionUrl属性
使用gradlew wrap --gradle-version 命令
1 ./gradlew wrap --gradle-version 6.8.3
自定义Gradle_Wrap
可以通过自定义wrapper少去一些重复操作或定制功能,如
build.gradle
1 2 3 4 5 6 7 tasks.named('wrapper') { distributionType = Wrapper.DistributionType.ALL } task wrapper(type: Wrapper) { gradleVersion = '6.8.3' }
Gradle 环境
环境变量
GRADLE_OPTS
1 指定启动Gradle客户端VM时要使用的JVM参数。客户端VM仅处理命令行输入/输出,因此很少需要更改其VM选项。实际的构建由Gradle守护程序运行,不受此环境变量的影响。
GRADLE_USER_HOME
1 指定Gradle用户的主目录(如果未设置,则默认为$USER_HOME/.gradle)。
JAVA_HOME
1 指定要用于客户端VM的JDK安装目录。除非Gradle属性文件使用org.gradle.java.home指定了另一个虚拟机,否则此虚拟机也用于守护程序。
注意:命令行选项和系统属性优先于环境变量。
Gradle属性
你可以通过以下方式自己配置你的项目属性,如果存在多个,则从上到下优先读取 :
系统属性,例如在命令行上设置 -Dgradle.user.home
GRADLE_USER_HOME目录中的gradle.properties
项目根目录中的gradle.properties
Gradle安装目录中的gradle.properties
gradle.properties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 # 当设置为true时,Gradle将在可能的情况下重用任何先前构建的任务输出,从而使构建速度更快 org.gradle.caching=true # 设置为true时,单个输入属性哈希值和每个任务的构建缓存键都记录在控制台上。 org.gradle.caching.debug=true # 启用按需孵化配置,Gradle将尝试仅配置必要的项目。 org.gradle.configureondemand=true # 自定义控制台输出的颜色或详细程度。默认值取决于Gradle的调用方式。可选(auto,plain,rich,verbose) org.gradle.console=auto # 当设置true的Gradle守护进程来运行构建。默认值为true。 org.gradle.daemon=true # 在指定的空闲毫秒数后,Gradle守护程序将自行终止。默认值为10800000(3小时)。 org.gradle.daemon.idletimeout=10800000 # 设置true为时,Gradle将在启用远程调试的情况下运行构建,侦听端口5005。 # 请注意,这等同于添加-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005到JVM命令行,并且将挂起虚拟机,直到连接了调试器。 # 默认值为false。 org.gradle.debug=true # 指定用于Gradle构建过程的Java主页。可以将值设置为jdk或jre位置,但是,根据您的构建方式,使用JDK更安全。 # 如果未指定设置,则从您的环境(JAVA_HOME或的路径java)派生合理的默认值。这不会影响用于启动Gradle客户端VM的Java版本(请参阅环境变量)。 org.gradle.java.home=/usr/bin/java # 指定用于Gradle守护程序的JVM参数。该设置对于配置JVM内存设置以提高构建性能特别有用。这不会影响Gradle客户端VM的JVM设置。 org.gradle.jvmargs=-Xmx2048m # 当设置为quiet,warn,lifecycle,info,debug时,Gradle将使用此日志级别。这些值不区分大小写。该lifecycle级别是默认级别。 # 可选(quiet,warn,lifecycle,info,debug) org.gradle.logging.level=debug # 配置后,Gradle将分叉到org.gradle.workers.maxJVM以并行执行项目 org.gradle.parallel=true # 指定Gradle守护程序及其启动的所有进程的调度优先级。默认值为normal。(low,normal) org.gradle.priority=normal # 在监视文件系统时配置详细日志记录。 默认为关闭 。 org.gradle.vfs.verbose=true # 切换观看文件系统。允许Gradle在下一个版本中重用有关文件系统的信息。 默认为关闭 。 org.gradle.vfs.watch=true # 当设置为all,summary或者none,Gradle会使用不同的预警类型的显示器。(all,fail,summary,none) org.gradle.warning.mode=all # 配置后,Gradle将最多使用给定数量的工人。默认值为CPU处理器数。 org.gradle.workers.max=5
系统属性
1 2 3 4 5 6 # 指定用户名以使用HTTP基本认证从服务器下载Gradle发行版 systemProp.gradle.wrapperUser = myuser # 指定使用Gradle Wrapper下载Gradle发行版的密码 systemProp.gradle.wrapperPassword = mypassword # 指定Gradle用户的主目录 systemProp.gradle.user.home=(path to directory)
项目属性
1 org.gradle.project.foo = bar
守护程序
Gradle在Java虚拟机(JVM)上运行,并使用一些支持库,这些库需要很短的初始化时间。但有时启动会比较慢。
解决此问题的方法是Gradle Daemon :这是一个长期存在的后台进程,与以前相比,它可以更快地执行构建。
可通过命令获取运行守护程序状态
IDLE为空闲,BUSY为繁忙,STOPPED则已关闭
守护程序默认打开,可通过以下属性关闭
.gradle/gradle.properties
也可用命令gradle --stop手动关闭守护程序
Gradle命令行
命令行格式
1 gradle [taskName ...] [--option-name ...]
如果指定了多个任务,则应以空格分隔。
选项和参数之间建议使用=
来指定。
启用行为的选项具有长形式的选项,并带有由指定的反函数--no-
。以下是相反的情况。
1 2 --build-cache --no-build-cache
许多命令具有缩写。例如以下命令是等效的:
使用Wrapper时候应该用./gradlew
或gradlew.bat
取代gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #获取帮助 gradle -? gradle -h gradle -help # 显示所选项目的子项目列表,以层次结构显示。 gradle projects #查看可执行task gradle task #查看可执行task帮助 gradle help -task # 在Gradle构建中,通常的`build`任务是指定组装所有输出并运行所有检查。 gradle build # 执行所有验证任务(包括test和linting)。 gradle check # 清理项目 gradle clean #强制刷新依赖 gradle --refresh-dependencies assemble #缩写调用 gradle startCmd == gradle sc # 执行任务 gradle myTask # 执行多个任务 gradle myTask test # 执行 dist任务但排除test任务 gradle dist --exclude-task test # 强制执行任务 gradle test --rerun-tasks # 持续构建 # gradle test --continue # 生成扫描会提供完整的可视化报告,说明哪些依赖项存在于哪些配置,可传递依赖项和依赖项版本选择中。 $ gradle myTask --scan # 所选项目的依赖项列表 $ gradle dependencies
更多命令行可参阅命令行选项
项目与任务
Gradle中的所有内容都位于两个基本概念之上:
projects :每个Gradle构建都由一个或多个 projects组成 ,一个projects代表什么取决于您使用Gradle做的事情。例如,一个projects可能代表一个JAR库或一个Web应用程序。
tasks :每个projects由一个或多个 tasks组成 。tasks代表构建执行的一些原子工作。这可能是编译某些类,创建JAR,生成Javadoc或将一些存档发布到存储库。
项目
表.项目属性
名称
类型
默认值
project
Project
该Project实例
name
String
项目目录的名称。
path
String
项目的绝对路径。
description
String 项目说明。
projectDir
File
包含构建脚本的目录。
buildDir
File
projectDir /build
group
Object
unspecified
version
Object
unspecified
ant
ant build
一个AntBuilder实例
任务
定义任务
使用字符串作为任务名称定义任务
使用tasks容器定义任务
使用DSL特定语法定义任务
例:
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // 使用字符串作为任务名称定义任务 task('hello') { doLast { println "hello" } } // 使用tasks容器定义任务 tasks.create('hello') { doLast { println "hello" } } // 使用DSL特定语法定义任务 task(hello) { doLast { println "hello" } } task('copy', type: Copy) { from(file('srcDir')) into(buildDir) }
定位任务
使用DSL特定语法访问任务
通过任务集合访问任务
通过路径访问
按任务类型访问任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 task hello task copy(type: Copy) // 使用DSL特定语法访问任务 println hello.name println project.hello.name println copy.destinationDir println project.copy.destinationDir // 通过任务集合访问任务 println tasks.hello.name println tasks.named('hello').get().name println tasks.copy.destinationDir println tasks.named('copy').get().destinationDir //按任务类型访问任务 tasks.withType(Tar).configureEach { enabled = false } task test { dependsOn tasks.withType(Copy) }
通过路径访问
project-a / build.gradle
build.gradle
1 2 3 4 5 6 task hello println tasks.getByPath('hello').path println tasks.getByPath(':hello').path println tasks.getByPath('project-a:hello').path println tasks.getByPath(':project-a:hello').path
配置任务
使用API配置任务
例:
build.gradle
1 2 3 4 Copy myCopy = tasks.getByName("myCopy") myCopy.from 'resources' myCopy.into 'target' myCopy.include('**/*.txt', '**/*.xml', '**/*.properties')
使用DSL特定语法配置任务
例:
build.gradle
1 2 3 4 5 6 // Configure task using Groovy dynamic task configuration block myCopy { from 'resources' into 'target' } myCopy.include('**/*.txt', '**/*.xml', '**/*.properties')
用配置块定义一个任务
例:
build.gradle
1 2 3 4 5 task copy(type: Copy) { from 'resources' into 'target' include('**/*.txt', '**/*.xml', '**/*.properties') }
将参数传递给任务构造函数
与Task在创建后配置可变属性相反,您可以将参数值传递给Task类的构造函数。为了将值传递给Task构造函数,您必须使用@javax.inject.Inject注释相关的构造函数。
首先创建带有@Inject构造函数的任务类
1 2 3 4 5 6 7 8 9 10 class CustomTask extends DefaultTask { final String message final int number @Inject CustomTask(String message, int number) { this.message = message this.number = number } }
然后创建一个任务,并在参数列表的末尾传递构造函数参数。
1 tasks.create('myTask', CustomTask, 'hello', 42)
你也可以使用Map创建带有构造函数参数的任务
1 task myTask(type: CustomTask, constructorArgs: ['hello', 42])
向任务添加依赖项
从另一个项目添加对任务的依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 project('project-a') { task taskX { dependsOn ':project-b:taskY' doLast { println 'taskX' } } } project('project-b') { task taskY { doLast { println 'taskY' } } }
使用任务对象添加依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 task taskX { doLast { println 'taskX' } } task taskY { doLast { println 'taskY' } } taskX.dependsOn taskY
使用惰性块添加依赖项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 task taskX { doLast { println 'taskX' } } // Using a Groovy Closure taskX.dependsOn { tasks.findAll { task -> task.name.startsWith('lib') } } task lib1 { doLast { println 'lib1' } } task lib2 { doLast { println 'lib2' } } task notALib { doLast { println 'notALib' } }
任务排序
控制任务排序的两种方式:
must run after :必须在之后运行
should run after:应该在之后运行 例
1 2 3 4 5 6 7 8 9 10 11 task taskX { doLast { println 'taskX' } } task taskY { doLast { println 'taskY' } } taskY.mustRunAfter taskX
should run after被忽略的情况
引入排序周期。
使用并行执行时,除了 "should run after "任务外,一个任务的所有依赖关系都已被满足,
引入排序周期例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 task taskX { doLast { println 'taskX' } } task taskY { doLast { println 'taskY' } } task taskZ { doLast { println 'taskZ' } } taskX.dependsOn taskY taskY.dependsOn taskZ taskZ.shouldRunAfter taskX
为任务添加描述
您可以在任务中添加描述。执行gradle tasks时将显示此描述。
build.gradle
1 2 3 4 5 6 task copy(type: Copy) { description 'Copies the resource directory to the target directory.' from 'resources' into 'target' include('**/*.txt', '**/*.xml', '**/*.properties') }
跳过任务
onlyIf跳过
1 2 3 4 5 hello.onlyIf { !project.hasProperty('skipHello') } //StopExecutionException跳过 compile.doFirst { if (true) { throw new StopExecutionException() } }
禁用任务
1 2 3 4 5 6 task disableMe { doLast { println 'This should not be printed if the task is disabled.' } } disableMe.enabled = false
任务超时
1 2 3 4 5 6 task hangingTask() { doLast { Thread.sleep(100000) } timeout = Duration.ofMillis(500) }
任务规则
有时您想执行一个任务,该任务的行为取决于较大或无限数量的参数值范围。提供此类任务的一种非常好的表达方式是任务规则:
任务规则
1 2 3 4 5 6 7 8 9 10 11 12 13 tasks.addRule("Pattern: ping<ID>") { String taskName -> if (taskName.startsWith("ping")) { task(taskName) { doLast { println "Pinging: " + (taskName - 'ping') } } } } task groupPing { dependsOn pingServer1, pingServer2 }
1 2 3 4 > gradle -q groupPing Ping:Server1 Ping:Server2
终结器任务
计划运行终结任务时,终结任务会自动添加到任务图中。即使完成任务失败,也将执行终结器任务。
1 2 3 4 5 6 7 8 9 10 11 task taskX { doLast { println 'taskX' } } task taskY { doLast { println 'taskY' } } taskX.finalizedBy taskY
1 2 3 4 > gradle -q taskX TaskX TaskY
动态任务
Groovy或Kotlin的功能可用于定义任务以外的其他功能。例如,您还可以使用它来动态创建任务。
build.gradle
1 2 3 4 5 6 7 4.times { counter -> task "task$counter" { doLast { println "I'm task number $counter" } } }
gradle -q task1 输出
1 2 > gradle -q task1 I'm task number 1
Groovy_DSL快捷方式符号
访问任务有一种方便的表示法。每个任务都可以作为构建脚本的属性来使用:
例.作为构建脚本的属性访问任务
build.gradle
1 2 3 4 5 6 7 8 task hello { doLast { println 'Hello world!' } } hello.doLast { println "Greetings from the $hello.name task." }
输出 gradle -q hello
1 2 3 > gradle -q hello Hello world! Greetings from the hello task.
这将启用非常可读的代码,尤其是在使用插件提供的任务(例如compile任务)时。
额外任务属性
您可以将自己的属性添加到任务。要添加名为的属性myProperty,请设置ext.myProperty为初始值。从那时起,可以像预定义的任务属性一样读取和设置属性。
build.gradle
1 2 3 4 5 6 7 8 9 task myTask { ext.myProperty = "myValue" } task printTaskProperties { doLast { println myTask.myProperty } }
输出 gradle -q printTaskProperties
1 2 > gradle -q printTaskProperties myValue
额外的属性不仅限于任务。您可以在Extra属性中阅读有关它们的更多信息。
默认任务
Gradle允许您定义一个或多个默认任务。
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 defaultTasks 'clean', 'run' task clean { doLast { println 'Default Cleaning!' } } task run { doLast { println 'Default Running!' } } task other { doLast { println "I'm not a default task!" } }
输出 gradle -q
1 2 3 > gradle -q Default Cleaning! Default Running!
这等效于运行gradle clean run。在多项目构建中,每个子项目可以有其自己的特定默认任务。如果子项目未指定默认任务,则使用父项目的默认任务(如果已定义)。
Groovy基础
基本规则
没有分号
方法括号可以省略
方法可以不写return,返回最后一句代码
代码块可以作为参数传递
定义
1 2 3 def param = 'hello world' def param1 = "hello world" println "${param1} ,li"
${}里面可以放变量,也可以是表达式,只有双引号里面可以使用
声明变量
可以在构建脚本中声明两种变量:局部变量和额外属性。
局部变量
局部变量用def关键字声明。它们仅在声明它们的范围内可见。局部变量是基础Groovy语言的功能。
1 2 3 4 5 def dest = "dest" task copy(type: Copy) { from "source" into dest }
ext属性
Gradle的域模型中的所有增强对象都可以容纳额外的用户定义属性。
可以通过拥有对象的ext属性添加,读取和设置其他属性。可以使用一个ext块一次添加多个属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 plugins { id 'java' } ext { springVersion = "3.1.0.RELEASE" emailNotification = "build@master.org" } sourceSets.all { ext.purpose = null } sourceSets { main { purpose = "production" } test { purpose = "test" } plugin { purpose = "production" } } task printProperties { doLast { println springVersion println emailNotification sourceSets.matching { it.purpose == "production" }.each { println it.name } } }
输出 gradle -q printProperties
1 2 3 4 5 > gradle -q printProperties 3.1.0.RELEASE build@master.org main plugin
变量范围:本地和脚本范围
用类型修饰符声明的变量在闭包中可见,但在方法中不可见。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 String localScope1 = 'localScope1' def localScope2 = 'localScope2' scriptScope = 'scriptScope' println localScope1 println localScope2 println scriptScope closure = { println localScope1 println localScope2 println scriptScope } def method() { try { localScope1 } catch (MissingPropertyException e) { println 'localScope1NotAvailable' } try { localScope2 } catch(MissingPropertyException e) { println 'localScope2NotAvailable' } println scriptScope } closure.call() method()
输出 groovy scope.groovy
1 2 3 4 5 6 7 8 9 10 > groovy作用域 localScope1 localScope2 scriptScope localScope1 localScope2 scriptScope localScope1NotAvailable localScope2NotAvailable scriptScope
对象
使用对象
您可以按照以下易读的方式配置任意对象。
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 import java.text.FieldPosition task configure { doLast { def pos = configure(new FieldPosition(10)) { beginIndex = 1 endIndex = 5 } println pos.beginIndex println pos.endIndex } }
1 2 3 > gradle -q configure 1 5
使用外部脚本配置任意对象
您也可以使用外部脚本配置任意对象。
build.gradle
1 2 3 4 5 6 7 8 9 task configure { doLast { def pos = new java.text.FieldPosition(10) // Apply the script apply from: 'other.gradle', to: pos println pos.beginIndex println pos.endIndex } }
other.gradle
1 2 3 // Set properties. beginIndex = 1 endIndex = 5
输出 gradle -q configure
1 2 3 > gradle -q configure 1 5
属性访问器
Groovy自动将属性引用转换为对适当的getter或setter方法的调用。
build.gradle
1 2 3 4 5 6 7 // Using a getter method println project.buildDir println getProject().getBuildDir() // Using a setter method project.buildDir = 'target' getProject().setBuildDir('target')
闭包
闭包(闭合代码块,可以引用传入的变量)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 task testClosure { doLast { func { println it } funa { a, b -> println a + b } } } def funa(closure) { closure(10, 3) } def func(closure) { closure(10) }
闭包委托
每个闭包都有一个delegate
对象,Groovy使用该对象来查找不是闭包的局部变量或参数的变量和方法引用。
例.闭包委托
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Info { int id; String code; def log() { println("code:${code};id:${id}") } } def info(Closure<Info> closure) { Info p = new Info() closure.delegate = p // 委托模式优先 closure.setResolveStrategy(Closure.DELEGATE_FIRST) closure(p) } task configClosure { doLast { info { code = "cix" id = 1 log() } } }
输出
1 2 3 4 > Task :configClosure code:cix;id:1 BUILD SUCCESSFUL in 276ms
例:使用必包委托设置依赖
1 2 3 4 5 dependencies { assert delegate == project.dependencies testImplementation('junit:junit:4.13') delegate.testImplementation('junit:junit:4.13') }
方法
方法调用上的可选括号
括号对于方法调用是可选的。
build.gradle
1 2 test.systemProperty 'some.prop', 'value' test.systemProperty('some.prop', 'value')
闭包作为方法中的最后一个参数
当方法的最后一个参数是闭包时,可以将闭包放在方法调用之后:
build.gradle
1 2 3 4 5 repositories { println "in a closure" } repositories() { println "in a closure" } repositories({ println "in a closure" })
集合
List
1 2 3 4 5 6 7 8 9 def list = [1,2,3,4] println list[0] // 1 println list[-1] // 4 最后一个 println list[-2] // 3 倒数第二个 println list[0..2] // 第1-3个 list.each { //迭代 println it }
Map
1 2 3 4 5 6 7 def map= ['name':'li', 'age':18] println map[name] // li println map.age // 18 list.each { //迭代 println "${it.key}:${it.value}" }
JavaBean
1 2 3 4 5 6 class A{ private int a; //可通过A().a 获取修改 public int getB(){//可通过A().b获取,但不能修改 1 } }
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 // List literal test.includes = ['org/gradle/api/**', 'org/gradle/internal/**'] List<String> list = new ArrayList<String>() list.add('org/gradle/api/**') list.add('org/gradle/internal/**') test.includes = list // Map literal. Map<String, String> map = [key1:'value1', key2: 'value2'] // Groovy will coerce named arguments // into a single map argument apply plugin: 'java'
默认导入
为了使构建脚本更简洁,Gradle自动向Gradle脚本添加了一些类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 import org.gradle.* import org.gradle.api.* import org.gradle.api.artifacts.* import org.gradle.api.artifacts.component.* import org.gradle.api.artifacts.dsl.* import org.gradle.api.artifacts.ivy.* import org.gradle.api.artifacts.maven.* import org.gradle.api.artifacts.query.* import org.gradle.api.artifacts.repositories.* import org.gradle.api.artifacts.result.* import org.gradle.api.artifacts.transform.* import org.gradle.api.artifacts.type.* import org.gradle.api.artifacts.verification.* import org.gradle.api.attributes.* import org.gradle.api.attributes.java.* import org.gradle.api.capabilities.* import org.gradle.api.component.* import org.gradle.api.credentials.* import org.gradle.api.distribution.* import org.gradle.api.distribution.plugins.* import org.gradle.api.execution.* import org.gradle.api.file.* import org.gradle.api.initialization.* import org.gradle.api.initialization.definition.* import org.gradle.api.initialization.dsl.* import org.gradle.api.invocation.* import org.gradle.api.java.archives.* import org.gradle.api.jvm.* import org.gradle.api.logging.* import org.gradle.api.logging.configuration.* import org.gradle.api.model.* import org.gradle.api.plugins.* import org.gradle.api.plugins.antlr.* import org.gradle.api.plugins.quality.* import org.gradle.api.plugins.scala.* import org.gradle.api.provider.* import org.gradle.api.publish.* import org.gradle.api.publish.ivy.* import org.gradle.api.publish.ivy.plugins.* import org.gradle.api.publish.ivy.tasks.* import org.gradle.api.publish.maven.* import org.gradle.api.publish.maven.plugins.* import org.gradle.api.publish.maven.tasks.* import org.gradle.api.publish.plugins.* import org.gradle.api.publish.tasks.* import org.gradle.api.reflect.* import org.gradle.api.reporting.* import org.gradle.api.reporting.components.* import org.gradle.api.reporting.dependencies.* import org.gradle.api.reporting.dependents.* import org.gradle.api.reporting.model.* import org.gradle.api.reporting.plugins.* import org.gradle.api.resources.* import org.gradle.api.services.* import org.gradle.api.specs.* import org.gradle.api.tasks.* import org.gradle.api.tasks.ant.* import org.gradle.api.tasks.application.* import org.gradle.api.tasks.bundling.* import org.gradle.api.tasks.compile.* import org.gradle.api.tasks.diagnostics.* import org.gradle.api.tasks.incremental.* import org.gradle.api.tasks.javadoc.* import org.gradle.api.tasks.options.* import org.gradle.api.tasks.scala.* import org.gradle.api.tasks.testing.* import org.gradle.api.tasks.testing.junit.* import org.gradle.api.tasks.testing.junitplatform.* import org.gradle.api.tasks.testing.testng.* import org.gradle.api.tasks.util.* import org.gradle.api.tasks.wrapper.* import org.gradle.authentication.* import org.gradle.authentication.aws.* import org.gradle.authentication.http.* import org.gradle.build.event.* import org.gradle.buildinit.plugins.* import org.gradle.buildinit.tasks.* import org.gradle.caching.* import org.gradle.caching.configuration.* import org.gradle.caching.http.* import org.gradle.caching.local.* import org.gradle.concurrent.* import org.gradle.external.javadoc.* import org.gradle.ide.visualstudio.* import org.gradle.ide.visualstudio.plugins.* import org.gradle.ide.visualstudio.tasks.* import org.gradle.ide.xcode.* import org.gradle.ide.xcode.plugins.* import org.gradle.ide.xcode.tasks.* import org.gradle.ivy.* import org.gradle.jvm.* import org.gradle.jvm.application.scripts.* import org.gradle.jvm.application.tasks.* import org.gradle.jvm.platform.* import org.gradle.jvm.plugins.* import org.gradle.jvm.tasks.* import org.gradle.jvm.tasks.api.* import org.gradle.jvm.test.* import org.gradle.jvm.toolchain.* import org.gradle.language.* import org.gradle.language.assembler.* import org.gradle.language.assembler.plugins.* import org.gradle.language.assembler.tasks.* import org.gradle.language.base.* import org.gradle.language.base.artifact.* import org.gradle.language.base.compile.* import org.gradle.language.base.plugins.* import org.gradle.language.base.sources.* import org.gradle.language.c.* import org.gradle.language.c.plugins.* import org.gradle.language.c.tasks.* import org.gradle.language.coffeescript.* import org.gradle.language.cpp.* import org.gradle.language.cpp.plugins.* import org.gradle.language.cpp.tasks.* import org.gradle.language.java.* import org.gradle.language.java.artifact.* import org.gradle.language.java.plugins.* import org.gradle.language.java.tasks.* import org.gradle.language.javascript.* import org.gradle.language.jvm.* import org.gradle.language.jvm.plugins.* import org.gradle.language.jvm.tasks.* import org.gradle.language.nativeplatform.* import org.gradle.language.nativeplatform.tasks.* import org.gradle.language.objectivec.* import org.gradle.language.objectivec.plugins.* import org.gradle.language.objectivec.tasks.* import org.gradle.language.objectivecpp.* import org.gradle.language.objectivecpp.plugins.* import org.gradle.language.objectivecpp.tasks.* import org.gradle.language.plugins.* import org.gradle.language.rc.* import org.gradle.language.rc.plugins.* import org.gradle.language.rc.tasks.* import org.gradle.language.routes.* import org.gradle.language.scala.* import org.gradle.language.scala.plugins.* import org.gradle.language.scala.tasks.* import org.gradle.language.scala.toolchain.* import org.gradle.language.swift.* import org.gradle.language.swift.plugins.* import org.gradle.language.swift.tasks.* import org.gradle.language.twirl.* import org.gradle.maven.* import org.gradle.model.* import org.gradle.nativeplatform.* import org.gradle.nativeplatform.platform.* import org.gradle.nativeplatform.plugins.* import org.gradle.nativeplatform.tasks.* import org.gradle.nativeplatform.test.* import org.gradle.nativeplatform.test.cpp.* import org.gradle.nativeplatform.test.cpp.plugins.* import org.gradle.nativeplatform.test.cunit.* import org.gradle.nativeplatform.test.cunit.plugins.* import org.gradle.nativeplatform.test.cunit.tasks.* import org.gradle.nativeplatform.test.googletest.* import org.gradle.nativeplatform.test.googletest.plugins.* import org.gradle.nativeplatform.test.plugins.* import org.gradle.nativeplatform.test.tasks.* import org.gradle.nativeplatform.test.xctest.* import org.gradle.nativeplatform.test.xctest.plugins.* import org.gradle.nativeplatform.test.xctest.tasks.* import org.gradle.nativeplatform.toolchain.* import org.gradle.nativeplatform.toolchain.plugins.* import org.gradle.normalization.* import org.gradle.platform.base.* import org.gradle.platform.base.binary.* import org.gradle.platform.base.component.* import org.gradle.platform.base.plugins.* import org.gradle.play.* import org.gradle.play.distribution.* import org.gradle.play.platform.* import org.gradle.play.plugins.* import org.gradle.play.plugins.ide.* import org.gradle.play.tasks.* import org.gradle.play.toolchain.* import org.gradle.plugin.devel.* import org.gradle.plugin.devel.plugins.* import org.gradle.plugin.devel.tasks.* import org.gradle.plugin.management.* import org.gradle.plugin.use.* import org.gradle.plugins.ear.* import org.gradle.plugins.ear.descriptor.* import org.gradle.plugins.ide.* import org.gradle.plugins.ide.api.* import org.gradle.plugins.ide.eclipse.* import org.gradle.plugins.ide.idea.* import org.gradle.plugins.javascript.base.* import org.gradle.plugins.javascript.coffeescript.* import org.gradle.plugins.javascript.envjs.* import org.gradle.plugins.javascript.envjs.browser.* import org.gradle.plugins.javascript.envjs.http.* import org.gradle.plugins.javascript.envjs.http.simple.* import org.gradle.plugins.javascript.jshint.* import org.gradle.plugins.javascript.rhino.* import org.gradle.plugins.signing.* import org.gradle.plugins.signing.signatory.* import org.gradle.plugins.signing.signatory.pgp.* import org.gradle.plugins.signing.type.* import org.gradle.plugins.signing.type.pgp.* import org.gradle.process.* import org.gradle.swiftpm.* import org.gradle.swiftpm.plugins.* import org.gradle.swiftpm.tasks.* import org.gradle.testing.base.* import org.gradle.testing.base.plugins.* import org.gradle.testing.jacoco.plugins.* import org.gradle.testing.jacoco.tasks.* import org.gradle.testing.jacoco.tasks.rules.* import org.gradle.testkit.runner.* import org.gradle.vcs.* import org.gradle.vcs.git.* import org.gradle.work.* import org.gradle.workers.*
导入依赖
1 2 3 4 5 6 7 8 buildscript { repositories { mavenCentral() } dependencies { classpath group: 'commons-codec', name: 'commons-codec', version: '1.2' } }
Logging
日志级别
日志级别
说明
ERROR
错误讯息
QUIET
重要信息消息
WARNING
警告讯息
LIFECYCLE
进度信息消息
INFO
信息讯息
DEBUG
调试信息
选择日志级别
可以通过命令行选项或者gradle.properties文件配置
选项
输出日志级别
没有记录选项
LIFECYCLE及更高
-q or–quiet
QUIET及更高
-w or --warn
WARNING及更高
-i or --info
INFO及更高
-d or --debug
DEBUG及更高版本(即所有日志消息)
Stacktrace命令行选项
-s or --stacktrace
-S or --full-stacktrace
编写日志
使用stdout编写日志消息
1 println 'A message which is logged at QUIET level'
编写自己的日志消息
1 2 3 4 5 6 7 8 9 10 logger.quiet('An info log message which is always logged.') logger.error('An error log message.') logger.warn('A warning log message.') logger.lifecycle('A lifecycle info log message.') logger.info('An info log message.') logger.debug('A debug log message.') logger.trace('A trace log message.') // Gradle never logs TRACE level logs // 用占位符写一条日志消息 logger.info('A {} log message', 'info')
logger构建脚本提供了一个属性,该脚本是Logger的实例。该接口扩展了SLF4JLogger接口,并向其中添加了一些Gradle特定的方法
使用SLF4J写入日志消息
1 2 3 4 import org.slf4j.LoggerFactory def slf4jLogger = LoggerFactory.getLogger('some-logger') slf4jLogger.info('An info log message logged using SLF4j')
构建
构建生命周期
Gradle的核心是一种基于依赖性的编程语言,这意味着你可以定义任务和任务之间的依赖关系。 Gradle保证这些任务按照其依赖关系的顺序执行,并且每个任务只执行一次。 这些任务形成一个定向无环图。
构建阶段
Gradle构建具有三个不同的阶段。
Gradle支持单项目和多项目构建。在初始化阶段,Gradle决定要参与构建的项目,并为每个项目创建一个Project实例。
在此阶段,将配置项目对象。执行作为构建一部分的 所有 项目的构建脚本。
Gradle确定要在配置阶段创建和配置的任务子集。子集由传递给gradle命令的任务名称参数和当前目录确定。然后Gradle执行每个选定的任务。
设置文件
默认名称是settings.gradle
项目构建在多项目层次结构的根项目中必须具有一个settings.gradle文件。
对于单项目构建,设置文件是可选的
初始化
查找settings.gradle文件判断是否多项目
没有settings.gradle或settings.gradle没有多项目配置则为单项目
例:将test任务添加到每个具有特定属性集的项目
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 allprojects { afterEvaluate { project -> if (project.hasTests) { println "Adding test task to $project" project.task('test') { doLast { println "Running tests for $project" } } } } }
输出 gradle -q test
1 2 3 > gradle -q test Adding test task to project ':project-a' Running tests for project ':project-a'
初始化脚本
初始化脚本与Gradle中的其他脚本相似。但是,这些脚本在构建开始之前运行。初始化脚本不能访问buildSrc项目中的类。
使用初始化脚本
有几种使用初始化脚本的方法:
在命令行中指定一个文件。命令行选项是-I或-init-script,后面是脚本的路径。 命令行选项可以出现一次以上,每次都会添加另一个 init 脚本。 如果命令行上指定的文件不存在,编译将失败。
在 USER_HOME /.gradle/目录中放置一个名为init.gradle(或init.gradle.ktsKotlin)的文件。
在 USER_HOME /.gradle/init.d/目录中放置一个以.gradle(或.init.gradle.ktsKotlin)结尾的文件。
在Gradle发行版的 GRADLE_HOME /init.d/目录中放置一个以.gradle(或.init.gradle.ktsKotlin)结尾的文件。这使您可以打包包含一些自定义构建逻辑和插件的自定义Gradle发行版。您可以将其与Gradle Wrapper结合使用,以使自定义逻辑可用于企业中的所有内部版本。
如果发现一个以上的初始化脚本,它们将按照上面指定的顺序依次执行。
示例
build.gradle
1 2 3 4 5 6 7 8 9 10 repositories { mavenCentral() } task showRepos { doLast { println "All repos:" println repositories.collect { it.name } } }
init.gradle
1 2 3 4 5 allprojects { repositories { mavenLocal() } }
运行任务:
初始化脚本里面依赖添加依赖
init.gradle
1 2 3 4 5 6 7 8 initscript { repositories { mavenCentral() } dependencies { classpath 'org.apache.commons:commons-math:2.0' } }
多项目
可在settings.gradle文件中设置多个项目关系,如下项目结构
1 2 3 4 5 . ├── app │ ... │ └── build.gradle └── settings.gradle
settings.gradle
1 2 rootProject.name = 'basic-multiproject' //根项目名 include 'app' //子项目
子项目间依赖
1 2 3 dependencies { implementation(project(":shared")) }
构建优化
关于构建优化可查阅构建优化
依赖
configurations
设置configurations 配置依赖信息
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 configurations { // 针对需要组件API的消费者的配置 exposedApi { // canBeResolved 为true 则为可解析配置,为消费者 canBeResolved = false // canBeConsumed 为true 则为消费析配置,为生产者 canBeConsumed = true } // 为需要实现该组件的消费者提供的配置。 exposedRuntime { canBeResolved = false canBeConsumed = true } }
依赖方式
模块依赖
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 dependencies { runtimeOnly group: 'org.springframework', name: 'spring-core', version: '2.5' runtimeOnly 'org.springframework:spring-core:2.5', 'org.springframework:spring-aop:2.5' runtimeOnly( [group: 'org.springframework', name: 'spring-core', version: '2.5'], [group: 'org.springframework', name: 'spring-aop', version: '2.5'] ) runtimeOnly('org.hibernate:hibernate:3.0.5') { transitive = true } runtimeOnly group: 'org.hibernate', name: 'hibernate', version: '3.0.5', transitive: true runtimeOnly(group: 'org.hibernate', name: 'hibernate', version: '3.0.5') { transitive = true } }
文件依赖
build.gradle
1 2 3 4 dependencies { runtimeOnly files('libs/a.jar', 'libs/b.jar') runtimeOnly fileTree('libs') { include '*.jar' } }
项目依赖
build.gradle
1 2 3 dependencies { implementation project(':shared') }
依赖方式
compileOnly —用于编译生产代码所必需的依赖关系,但不应该属于运行时类路径的一部分
implementation(取代compile)-用于编译和运行时
runtimeOnly(取代runtime)-仅在运行时使用,不用于编译
testCompileOnly—与compileOnly测试相同
testImplementation —测试相当于 implementation
testRuntimeOnly —测试相当于 runtimeOnly
repositories
流行的公共存储库包括Maven Central, Bintray JCenter和Google Android存储库。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 repositories { mavenCentral() // Maven Central存储库 jcenter() // JCenter Maven存储库 google() // Google Maven存储库 mavenLocal() // 将本地Maven缓存添加为存储库(不推荐) //flat存储库解析器 flatDir { dirs 'lib' } flatDir { dirs 'lib1', 'lib2' } //添加定制的Maven仓库 maven { url "http://repo.mycompany.com/maven2" // 为JAR文件添加附加的Maven存储库 artifactUrls "http://repo.mycompany.com/jars" } //Ivy ivy { url "http://repo.mycompany.com/repo" layout "maven" // 有效的命名布局值是'gradle'(默认值)'maven'和'ivy'。 } }
声明存储库过滤器
声明存储库内容
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 repositories { maven { url "https://repo.mycompany.com/maven2" content { // this repository *only* contains artifacts with group "my.company" includeGroup "my.company" } } jcenter { content { // this repository contains everything BUT artifacts with group starting with "my.company" excludeGroupByRegex "my\\.company.*" } } }
默认情况下,存储库包含所有内容,不包含任何内容:
如果声明include,那么它排除了一切 include 以外的内容。
如果声明exclude,则它将包括除exclude之外的所有内容。
如果声明include和exclude,则它仅包括显式包括但不排除的内容。
分割快照和发行版
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 repositories { maven { url "https://repo.mycompany.com/releases" mavenContent { releasesOnly() } } maven { url "https://repo.mycompany.com/snapshots" mavenContent { snapshotsOnly() } } }
支持的元数据源
受支持的元数据源
元数据源
描述
排序
Maven
Ivy/flat dir
gradleMetadata()
寻找Gradle.module文件
1
是
是
mavenPom()
查找Maven.pom文件
2
是
是
ivyDescriptor()
查找ivy.xml文件
2
没有
是
artifact()
直接寻找artifact
3
是
是
从Gradle 5.3开始,解析元数据文件(无论是Ivy还是Maven)时,Gradle将寻找一个标记,指示存在匹配的Gradle Module元数据文件。如果找到它,它将代替Ivy或Maven文件使用。
从Gradle5.6开始,您可以通过添加ignoreGradleMetadataRedirection()到metadataSources声明来禁用此行为。
例.不使用gradle元数据重定向的Maven存储库
build.gradle
1 2 3 4 5 6 7 8 9 10 repositories { maven { url "http://repo.mycompany.com/repo" metadataSources { mavenPom() artifact() ignoreGradleMetadataRedirection() } } }
GAV坐标
GAV坐标一般指group,artifact,version
变体
构建变体是针对不同环境的配置,例如android开发中一般有debug和release两种变体
声明功能变体
可以通过应用java
或java-library
插件来声明功能变体。以下代码说明了如何声明名为mongodbSupport
的功能:
示例1.声明一个功能变量
Groovy``Kotlin
build.gradle
1 2 3 4 5 6 7 8 group = 'org.gradle.demo' version = '1.0' java { registerFeature('mongodbSupport') { usingSourceSet(sourceSets.main) } }
元数据
从存储库中提取的每个模块都有与之关联的元数据,例如其组,名称,版本以及它提供的带有工件和依赖项的不同变体
可配置组件元数据规则的示例
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class TargetJvmVersionRule implements ComponentMetadataRule { final Integer jvmVersion @Inject TargetJvmVersionRule(Integer jvmVersion) { this.jvmVersion = jvmVersion } @Inject ObjectFactory getObjects() { } void execute(ComponentMetadataContext context) { context.details.withVariant("compile") { attributes { attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, jvmVersion) attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_API)) } } } } dependencies { components { withModule("commons-io:commons-io", TargetJvmVersionRule) { params(7) } withModule("commons-collections:commons-collections", TargetJvmVersionRule) { params(8) } } implementation("commons-io:commons-io:2.6") implementation("commons-collections:commons-collections:3.2.2") }
可以通过以下方法进行修改变体:
allVariants:修改组件的所有变体
withVariant(name):修改由名称标识的单个变体
addVariant(name)或addVariant(name, base):从头开始 或通过 复制 现有变体的详细信息(基础)向组件添加新变体
可以调整每个变体的以下详细信息:
标识变体的属性-attributes {}块
该变体提供的功能-withCapabilities { }块
变体的依赖项,包括丰富的版本-withDependencies {}块
变体的依赖关系约束,包括丰富版本-withDependencyConstraints {}块
构成变体实际内容的已发布文件的位置-withFiles { }块
平台
使用平台
获取平台中声明的版本
build.gradle
1 2 3 4 5 6 dependencies { // get recommended versions from the platform project api platform(project(':platform')) // no version required api 'commons-httpclient:commons-httpclient' }
platform表示法是一种简写表示法,实际上在后台执行了一些操作:
它将org.gradle.category属性设置为platform,这意味着Gradle将选择依赖项的 平台 组件。
它默认设置endorseStrictVersions行为, 这意味着如果平台声明了严格的依赖关系,则将强制执行它们。
这意味着默认情况下,对平台的依赖项会触发该平台中定义的所有严格版本的继承, 这对于平台作者确保所有使用者在依赖项的版本方面都遵循自己的决定很有用。 可以通过显式调用doNotEndorseStrictVersions方法来将其关闭。
例.依靠一个BOM导入其依赖约束
build.gradle
1 2 3 4 5 6 7 8 dependencies { // import a BOM implementation platform('org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE') // define dependencies without versions implementation 'com.google.code.gson:gson' implementation 'dom4j:dom4j' }
导入BOM,确保其定义的版本覆盖找到的任何其他版本
build.gradle
1 2 3 4 5 6 7 8 9 10 11 dependencies { // import a BOM. The versions used in this file will override any other version found in the graph implementation enforcedPlatform('org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE') // define dependencies without versions implementation 'com.google.code.gson:gson' implementation 'dom4j:dom4j' // this version will be overridden by the one found in the BOM implementation 'org.codehaus.groovy:groovy:1.8.6' }
Capability
声明组件的capability
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 configurations { apiElements { outgoing { capability("com.acme:my-library:1.0") capability("com.other:module:1.1") } } runtimeElements { outgoing { capability("com.acme:my-library:1.0") capability("com.other:module:1.1") } } }
解决冲突
按Capability(能力)解决冲突,(若存在相同能力的依赖性会失败)
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @CompileStatic class AsmCapability implements ComponentMetadataRule { void execute(ComponentMetadataContext context) { context.details.with { if (id.group == "asm" && id.name == "asm") { allVariants { it.withCapabilities { // Declare that ASM provides the org.ow2.asm:asm capability, but with an older version it.addCapability("org.ow2.asm", "asm", id.version) } } } } } }
一个带有日志框架隐式冲突的构建文件
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 dependencies { // Activate the "LoggingCapability" rule components.all(LoggingCapability) } @CompileStatic class LoggingCapability implements ComponentMetadataRule { final static Set<String> LOGGING_MODULES = ["log4j", "log4j-over-slf4j"] as Set<String> void execute(ComponentMetadataContext context) { context.details.with { if (LOGGING_MODULES.contains(id.name)) { allVariants { it.withCapabilities { // Declare that both log4j and log4j-over-slf4j provide the same capability it.addCapability("log4j", "log4j", id.version) } } } } } }
版本
版本规则
Gradle支持不同的版本字符串声明方式:
一个确切的版本:比如1.3
,1.3.0-beta3
,1.0-20150201.131010-1
一个Maven风格的版本范围:例如[1.0,)
,[1.1, 2.0)
,(1.2, 1.5]
[
和]
的符号表示包含性约束; (
和)
表示排他性约束。
当上界或下界缺失时,该范围没有上界或下界。
符号]
可以被用来代替(
用于排他性下界,[
代替)
用于排他性上界。例如]1.0, 2.0[
前缀版本范围:例如1.+
,1.3.+
仅包含与+
之前部分完全匹配的版本。
+
本身的范围将包括任何版本。
一个latest-status版本:例如latest.integration,latest.release
Maven的SNAPSHOT版本标识符:例如1.0-SNAPSHOT,1.4.9-beta1-SNAPSHOT
版本排序
每个版本均分为其组成的“部分”:
字符[. - _ +]用于分隔版本的不同“部分”。
同时包含数字和字母的任何部分都将分为以下各个部分: 1a1 == 1.a.1
仅比较版本的各个部分。实际的分隔符并不重要:1.a.1 == 1-a+1 == 1.a-1 == 1a1
使用以下规则比较2个版本的等效部分:
如果两个部分都是数字,则最高数字值 较高 :1.1<1.2
如果一个部分是数值,则认为它 高于 非数字部分:1.a<1.1
如果两个部分都不是数字,则按字母顺序比较,区分大小写:1.A< 1.B< 1.a<1.b
有额外数字部分的版本被认为比没有数字部分的版本高:1.1<1.1.0
带有额外的非数字部分的版本被认为比没有数字部分的版本低:1.1.a<1.1
某些字符串值出于排序目的具有特殊含义:
字符串dev被认为比任何其他字符串部分低:1.0-dev< 1.0-alpha< 1.0-rc
。
字符串rc、release和final被认为比任何其他字符串部分都高(按顺序排列:1.0-zeta< 1.0-rc< 1.0-release< 1.0-final< 1.0
。
字符串SNAPSHOT没有特殊意义,和其他字符串部分一样按字母顺序排序:1.0-alpha< 1.0-SNAPSHOT< 1.0-zeta< 1.0-rc< 1.0
。
数值快照版本没有特殊意义,和其他数值部分一样进行排序:1.0< 1.0-20150201.121010-123< 1.1
。
简单来说:数字>final>release>rc>字母>dev
声明没有版本的依赖
对于较大的项目,建议的做法是声明没有版本的依赖项, 并将依赖项约束 用于版本声明。 优势在于,依赖关系约束使您可以在一处管理所有依赖关系的版本,包括可传递的依赖关系。
build.gradle
1 2 3 4 5 6 7 8 9 dependencies { implementation 'org.springframework:spring-web' } dependencies { constraints { implementation 'org.springframework:spring-web:5.0.2.RELEASE' } }
依赖方式
strictly
1 2 3 4 5 与该版本符号不匹配的任何版本将被排除。这是最强的版本声明。 在声明的依赖项上,strictly可以降级版本。 在传递依赖项上,如果无法选择此子句可接受的版本,将导致依赖项解析失败。 有关详细信息,请参见覆盖依赖项版本。 该术语支持动态版本。
定义后,将覆盖先前的require声明并清除之前的 reject。
require
1 2 表示所选版本不能低于require可接受的版本,但可以通过冲突解决方案提高,即使更高版本具有排他性更高的界限。 这就是依赖项上的直接版本所转换的内容。该术语支持动态版本。
定义后,将覆盖先前的strictly声明并清除之前的 reject。
prefer
1 2 这是一个非常软的版本声明。仅当对该模块的版本没有更强的非动态观点时,才适用。 该术语不支持动态版本。
定义可以补充strictly或require。
在级别层次结构之外还有一个附加术语:
reject
1 声明模块不接受特定版本。如果唯一的可选版本也被拒绝,这将导致依赖项解析失败。该术语支持动态版本。
动态版本
build.gradle
1 2 3 4 5 6 7 8 9 10 11 plugins { id 'java-library' } repositories { mavenCentral() } dependencies { implementation 'org.springframework:spring-web:5.+' }
版本快照
声明一个版本变化的依赖
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 plugins { id 'java-library' } repositories { mavenCentral() maven { url 'https://repo.spring.io/snapshot/' } } dependencies { implementation 'org.springframework:spring-web:5.0.3.BUILD-SNAPSHOT' }
以编程方式控制依赖项缓存
您可以使用ResolutionStrategy 对配置进行编程来微调缓存的某些方面。 如果您想永久更改设置,则编程方式非常有用。
默认情况下,Gradle将动态版本缓存24小时。 要更改Gradle将解析后的版本缓存为动态版本的时间,请使用:
例.动态版本缓存控制
build.gradle
1 2 3 configurations.all { resolutionStrategy.cacheDynamicVersionsFor 10, 'minutes' }
默认情况下,Gradle会将更改的模块缓存24小时。 要更改Gradle将为更改的模块缓存元数据和工件的时间,请使用:
例.改变模块缓存控制
build.gradle
1 2 3 configurations.all { resolutionStrategy.cacheChangingModulesFor 4, 'hours' }
锁定配置
锁定特定配置
build.gradle
1 2 3 4 5 configurations { compileClasspath { resolutionStrategy.activateDependencyLocking() } }
锁定所有配置
build.gradle
1 2 3 dependencyLocking { lockAllConfigurations() }
解锁特定配置
build.gradle
1 2 3 4 5 configurations { compileClasspath { resolutionStrategy.deactivateDependencyLocking() } }
锁定buildscript类路径配置
如果将插件应用于构建,则可能还需要利用依赖锁定。为了锁定用于脚本插件的classpath配置,请执行以下操作:
build.gradle
1 2 3 4 5 buildscript { configurations.classpath { resolutionStrategy.activateDependencyLocking() } }
使用锁定模式微调依赖项锁定行为
虽然默认锁定模式的行为如上所述,但是还有其他两种模式可用:
Strict模式 :在该模式下,除了上述验证外,如果被标记为锁定的配置没有与之相关联的锁定状态,则依赖性锁定将失败。
Lenient模式:在这种模式下,依存关系锁定仍将固定动态版本,但除此之外,依赖解析的变化不再是错误。
锁定模式可以从dependencyLocking块中进行控制,如下所示:
build.gradle
1 2 3 dependencyLocking { lockMode = LockMode.STRICT }
版本冲突
用force强制执行一个依赖版本
build.gradle
1 2 3 4 5 6 dependencies { implementation 'org.apache.httpcomponents:httpclient:4.5.4' implementation('commons-codec:commons-codec:1.9') { force = true } }
排除特定依赖声明的传递依赖
build.gradle
1 2 3 4 5 dependencies { implementation('commons-beanutils:commons-beanutils:1.9.4') { exclude group: 'commons-collections', module: 'commons-collections' } }
版本冲突时失败
build.gradle
1 2 3 4 5 configurations.all { resolutionStrategy { failOnVersionConflict() } }
使用动态版本时失败
build.gradle
1 2 3 4 5 configurations.all { resolutionStrategy { failOnDynamicVersions() } }
改变版本时失败
build.gradle
1 2 3 4 5 configurations.all { resolutionStrategy { failOnChangingVersions() } }
解析无法再现时失败
build.gradle
1 2 3 4 5 configurations.all { resolutionStrategy { failOnNonReproducibleResolution() } }
插件
插件作用:将插件应用于项目可以使插件扩展项目的功能。它可以执行以下操作:
扩展Gradle模型(例如,添加可以配置的新DSL元素)
根据约定配置项目(例如,添加新任务或配置合理的默认值)
应用特定的配置(例如,添加组织存储库或强制执行标准) 简单来说,插件可以拓展项目功能,如任务,依赖,拓展属性,约束
插件类型
二进制插件 :通过实现插件接口以编程方式编写二进制插件,或使用Gradle的一种DSL语言以声明方式编写二进制插件
脚本插件 :脚本插件是其他构建脚本,可以进一步配置构建,并通常采用声明式方法来操纵构建
插件通常起初是脚本插件(因为它们易于编写),然后,随着代码变得更有价值,它被迁移到可以轻松测试并在多个项目或组织之间共享的二进制插件。
应用插件
二进制插件
实现了org.gradle.api.Plugin接口
1 apply plugin: 'com.android.application'
apply plugin
1 2 3 4 5 apply plugin: 'java' //id == apply plugin: org.gradle.api.plugins.JavaPlugin //类型 == apply plugin: JavaPlugin //org.gradle.api.plugins默认导入
plugins DSL
1 2 3 4 5 plugins { id 'java' //应用核心插件 id 'com.jfrog.bintray' version '0.4.1' //应用社区插件 id 'com.example.hello' version '1.0.0' apply false //使用`apply false`语法告诉Gradle不要将插件应用于当前项目 }
脚本插件
脚本插件会自动解析,可以从本地文件系统或远程位置的脚本中应用。可以将多个脚本插件(任意一种形式)应用于给定目标。
1 apply from:'version.gradle'
apply可传入内容
1 2 3 void apply(Map<String,? options); void apply(Closure closure); void apply(Action<? super ObjectConfigurationAction> action);
定义插件
定义一个带有ID的buildSrc插件
buildSrc / build.gradle
1 2 3 4 5 6 7 8 9 10 11 plugins { id 'java-gradle-plugin' } gradlePlugin { plugins { myPlugins { id = 'my-plugin' implementationClass = 'my.MyPlugin' } } }
第三方插件
通过将插件添加到构建脚本classpath中,然后应用该插件,可以将已发布为外部jar文件的二进制插件添加到项目中。可以使用buildscript {}块将外部jar添加到构建脚本classpath中。
1 2 3 4 5 6 7 8 9 buildscript { repositories { google() jcenter() } dependencies { classpath "com.android.tools.build:gradle:4.0.1" } }
插件管理
pluginManagement {}块只能出现在settings.gradle文件中,必须是文件中的第一个块,也可以以settings形式出现在初始化脚本中。
settings.gradle
1 2 3 4 5 6 7 8 9 pluginManagement { plugins { } resolutionStrategy { } repositories { } } rootProject.name = 'plugin-management'
init.gradle
1 2 3 4 5 6 7 8 9 10 settingsEvaluated { settings -> settings.pluginManagement { plugins { } resolutionStrategy { } repositories { } } }
例:通过pluginManagement管理插件版本。
settings.gradle
1 2 3 4 5 pluginManagement { plugins { id 'com.example.hello' version "${helloPluginVersion}" } }
gradle.properties
1 helloPluginVersion=1.0.0
自定义插件存储库
要指定自定义插件存储库,请使用repositories {}块其中的pluginManagement {}:
settings.gradle
1 2 3 4 5 6 7 8 9 10 11 pluginManagement { repositories { maven { url '../maven-repo' } gradlePluginPortal() ivy { url '../ivy-repo' } } }
java库
导入java
自定义路径
1 2 3 4 5 6 7 8 9 10 11 12 13 sourceSets { main { java { srcDirs = ['src'] } } test { java { srcDirs = ['test'] } } }
1 2 3 4 5 6 7 sourceSets { main { java { srcDir 'thirdParty/src/main/java' } } }
导入依赖
1 2 3 4 5 6 7 8 9 10 11 repositories { jcenter() } dependencies { implementation group:'com.android.support',name:'appcompat-v7',version:'28.0.0' implementation 'com.android.support:appcompat-v7:28.0.0' implementation protect(':p') implementation file('libs/ss.jar','libs/ss2.jar') implementation fileTree(dir: "libs", include: ["*.jar"]) }
多项目 设置 settings.gradle
1 2 include ':app' rootProject.name = "GradleTest"
安卓实用
设置签名
1 2 3 4 5 6 7 8 9 10 android { signingConfig = { release { storeFile file("MYKEY.keystore") storePassword "storePassword" keyAlias "keyAlias" keyPassword "keyPassword" } } }
自定义输出apk文件名称
1 2 3 4 5 6 7 8 9 applicationVariants.all { variant -> variant.outputs.all { output -> def fileName = "自定义名称_${variant.versionName}_release.apk" def outFile = output.outputFile if (outFile != null && outFile.name.endsWith('.apk')) { outputFileName = fileName } } }
动态AndroidManifest
1 2 3 4 5 6 7 8 <meta-data android:name="paramName" android:value="${PARAM_NAME}"> android { productFlavors{ google{ manifestPlaceholders.put("PARAM_NAME",'google') } } }
多渠道
1 2 3 4 5 6 7 8 9 10 11 12 13 android { productFlavors{ google{ }, baidu{ } } productFlavors.all{ flavor-> manifestPlaceholders.put("PARAM_NAME",name) } }
adb设置
adb工具
1 2 3 4 5 6 android { adbOptions{ timeOutInMs = 5000 //5s超时 installOptions '-r','-s' //安装指令 } }
dexOptions
dex工具
1 2 3 4 5 6 7 8 9 android { dexOptions{ incremental true //增量 javaMaxHeapSize '4G'//dx最大队内存 jumboMode true //强制开启jumbo跳过65535限制 preDexLibraries true //提高增量构建速度 threadCount 1 //dx线程数量 } }
Ant
例.将嵌套元素传递给Ant任
build.gradle
1 2 3 4 5 6 7 8 9 10 task zip { doLast { ant.zip(destfile: 'archive.zip') { fileset(dir: 'src') { include(name: '**.xml') exclude(name: '**.java') } } } }
例.使用Ant类型
build.gradle
1 2 3 4 5 6 7 8 9 10 task list { doLast { def path = ant.path { fileset(dir: 'libs', includes: '*.jar') } path.list().each { println it } } }
例.使用定制的Ant任务
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 task check { doLast { ant.taskdef(resource: 'checkstyletask.properties') { classpath { fileset(dir: 'libs', includes: '*.jar') } } ant.checkstyle(config: 'checkstyle.xml') { fileset(dir: 'src') } } }
Lint
Lint:android tool目录下的工具,一个代码扫描工具,能够帮助我们识别资源、代码结构存在的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 lintOptions android { lintOptions{ abortOnError true //发生错误时推出Gradle absolutePaths true //配置错误输出是否显示绝对路径 check 'NewApi','InlinedApi' // 检查lint check的issue id enable 'NewApi','InlinedApi' //启动 lint check的issue id disable 'NewApi','InlinedApi' //关闭 lint check的issue id checkAllWarnings true //检查所有警告issue ignoreWarning true //忽略警告检查,默认false checkReleaseBuilds true //检查致命错误,默认true explainIssues true //错误报告是否包含解释说明,默认true htmlOutput new File("/xx.html") //html报告输出路径 htmlReport true // 是否生成html报告,默认true lintConfig new File("/xx.xml") //lint配置 noLines true // 输出不带行号 默认true quite true // 安静模式 showAll true //是否显示所有输出,不截断 } }
参考文献