Gradle是一个基于Apache Ant和Apache Maven概念的JVM项目自动化建构工具。 有别于传统的不忍卒读的XML项目设置语言,它使用基于Groovy或者Kotlin的DSL(领域专用语言)来配置项目构建流程,大大提升了可读性和易用性。Spring Framework、Hibernate等著名的开源框架都在使用Gradle,固然还包括Gradle自己。java
固然Gradle自己也是有学习曲线的,不少开发者对其的理解和使用可能只停留在gradle test
和gradle build
上。随着项目代码的增长,项目依赖也愈来愈多,自定义的构建步骤也开始出现,整个项目的构建速度渐渐变慢,进而使得持续集成的速度也相继变慢。编程
本文将基于Gradle 5.5介绍几个很是实用但不为人知的Gradle使用技巧,帮助读者优雅地使用Gradle。api
Gradle Wrapper是官方推荐的使用Gradle的方式,由于它能够简单地声明要使用的Gradle的版本,而后在项目构建中使用那个指定的版原本跑各个构建任务。这样,无论你用的是命令行仍是IDE,无论你用的是Windows仍是Linux, 你用的都是同一个命令和版本,好比./gradlew build
,此处./gradlew
便是所说的Gradle Wrapper。缓存
生成Gradle Wrapper,你只须要在项目根目录下运行gradle wrapper --gradle-version 5.4.1
, 你能够根据须要选择适合你的gradle-version。网络
随着项目的发展,产品代码和测试代码与日俱增,然而项目的总体构建速度一般都会不断变慢。变慢的缘由有不少种,好比有不可避免的编译时间增加,也有能够改善的测试运行方式。Gradle 5.x 版本中已经作了不少优化,好比默认的incremental build(增量编译)和build cache(构建缓存)。此外,如今大部分电脑都配置了多核CPU,Gradle提供了--parallel
功能来帮助你基于多核CPU并行运行Gradle的任务。好比你的项目里有三个无交叉依赖Gradle子模块,当你在一台六核的机器上跑时,三个子模块能够并行编译和运行测试,理论上你最多能够节省2/3的构建时间。架构
若是你想把这个选项做为人和人跑任何Gradle命令时的默认选项,你能够在你项目根目录的gradle.properties 文件里添加下面一行app
org.gradle.parallel=true
复制代码
在CI(持续集成)构建项目时,必不可少的一个环节是跑自动化测试,好比单元测试和集成测试。大型的项目每每有成百上千个自动化测试,若是CI系统没有很好的并行化机制的话, 所有跑完至少要个五到十分钟,甚至更久。万一你的分支里有测试挂了,默认状况下你必需要等到全部测试跑完才能获得反馈(好比收到提醒)。假设那个测试是在第二十秒时就挂了,意味着咱们浪费了以后五到十分钟的机器资源。框架
--fail-fast
正是为了解决这个问题而设计的,它的做用就是一旦有测试挂掉就直接终止,还没跑的测试就被跳过了。这样咱们的CI环境资源利用率就能有所提升,对于那种有数十个甚至上百个工程师在同时工做的代码库,--fail-fast
的优化效果就很明显了。ide
--fail-fast
只适用于Gradle Test类别的构建任务,好比 ./gradlew test --fail-fast
。用于其余任务则会报错,好比./gradlew build --fail-fast
,由于build不是Test类别的任务。除了用于命令行,你还能够在Gradle脚本里设置:函数
test {
failFast = true
}
复制代码
--continuous
帮助开发者在本地开发时更高效地验证他们的changes。好比你在写一个类的单元测试,你已经有如下的两个文件
# Foo.java
public class Foo {
public int sum(int a, int b) {
return a + b;
}
}
# FooTest.java
public class FooTest {
@Test
public void testSum() {
// TODO: implement me
}
}
复制代码
你能够在命令行运行./gradlew test --tests FooTest --continuous
或者缩减版./gradlew test --tests FooTest -t
,当你改变Foo.java
的实现或者FooTest.java
的测试代码时,Gradle会检测到文件内容改变,从而从新编译运行你的测试,无需你人工执行一样的命令。
这个功能很是适合那些须要快速反馈、迭代、验证修复的任务,好比编译、测试、重构、代码风格错误修复等。
当你想执行一系列Gradle构建任务但又想跳过某些很慢的或者是不相关的任务时,-x
就派上用场了。好比Gradle有个任务叫check
, 它每每是开发者push本地commits前跑的一个任务,能够当作一个快速代码正确性校验的综合任务,包含编译、代码静态分析、自动化测试等。若是此时你不想跑集成测试,由于集成测试很慢,你就能够在命令行跑./gradlew check -x integrationTest
, 这样它就会跑除了集成测试以外的相关校验任务。
当你修改了一些Gradle脚本,或者想快速验证你的Gradle配置是正确的,合理地应用--dry-run
能帮你节省很多时间。好比你想看看check
任务到底会跑哪些相关任务时,你能够用./gradlew check -m
, 不出几秒Gradle就会在命令行打印出须要执行的任务名(但不执行)。
设想你在火车或者飞机上很无聊,忽然灵光乍现想到一个优化你如今系统的方法,你火烧眉毛地打开笔记本啪啪啪啪敲完了代码,结果在命令行一跑Gradle报错说没有网络链接,没法下载一些项目依赖。再有没有比这个更让一个工程师痛苦的事了吧。其实Gradle大部分时间都在本地缓存了全部的项目依赖,只是它习惯性地会去网上从新更新校对下依赖版本等信息。此时--offline
就能帮助你强制Gradle开启离线工做模式!
不少构建系统都会碰到一个棘手的问题:如何合理解决Transitive Dependency(依赖传递)? 比方说如今有以下依赖关系App -> Lib A -> Lib B
, 若是_Lib B_ 只在_Lib A_的函数内部使用到,理想的状况下_App_的代码是不能直接调用_Lib B_的,由于_Lib B_只是_Lib A_的内部实现方式(internal implementation)。 然而大部分状况下你会发现_App_的代码居然能调用_Lib B_ 😱🤔
以上就是在Gradle脚本里使用compile
关键词的反作用。不要觉得这仅仅是一个内部实现暴露的问题,它还会形成依赖版本解决出错致使应用层出错,还会减慢项目代码的总体编译速度。
Gradle从3.4版本起其实就提供了一个解决方案。它把compile
语义拆分红两类,api
和implementation
。api
等价于被_deprecated_的compile
。至于implementation
, 以上面的例子为基础
# build.gradle of App
dependencies {
api project('LibA')
}
# build.gradle of Lib A
dependencies {
api project('models')
implementation 'com.google.guava:guava:18.0'
}
复制代码
这样_Guava:18_这个内部依赖就不能渗透到_App_那里。若是_App_恰好也须要使用_Guava_, 它就须要显示定义_Guava_为它的依赖,并且它能够自由选择_Guava_的版本而不用担忧版本误用或者冲突。
参考文章
本文原载于: 【技术博客】如何优雅地使用Gradle
艺与术公众号(various__artists): 分享关于编程、软件工程、系统架构、前沿技术的艺与术的思考。