Gradle DSL

Posted Jeanboy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gradle DSL相关的知识,希望对你有一定的参考价值。

Gradle DSL

Gradle 是一个编译打包工具,但实际上它也是一个编程框架。Gradle 有自己的 API 文档,对应链接如下:

  • Gradle User Manual - 官方介绍文档

  • DSL Reference - API 文档

由此可以看出,编写 Gradle 脚本时,我们实际上就是调用 Gradle 的 API 编程。

基本组件

  • Project

    Gradle 中,每一个待编译的工程都是一个 Project。


例如:用 androidStudio 构建项目时,每一个 module 都是 Gradle 定义的 Project。

  • Task

    每一个 Project 在构建的时候都包含一系列的 Task。


比如,一个 Android APK 的编译可能包含:Java 源码编译 Task、资源编译 Task、JNI 编译 Task、lint 检查 Task、打包生成 APK 的 Task、签名 Task 等。

对于 Gradle 的编译打包流程而言,Task 就是最小的执行单元,其中将调用具体的函数来完成实际的工作。

  • Plugin

    一个 Project 到底包含多少个 Task,在很大程度上依赖于编译脚本指定的插件。插件定义了一系列基本的 Task,并指定了这些 Task 的执行顺序。

    整体来看,Gradle 作为一个框架,负责定义通用的流程和规则;根据不同的需求,实际的编译工作则通过具体的插件来完成。 


例如:编译 Java 项目时使用 Java 插件、编译 Groovy 项目时使用 Groovy 插件、编译 Android App 有 Android App 插件,编译 Android Library 有 Android Library 插件。

举个例子来说,我们在 Android APK 对应的 build.gradle 中经常可以看到如下代码:

groovy apply plugin:'com.android.application'

这就是使用编译 APK 的插件。

同样,在编译 Android Library 时可以看到如下代码,用于指定使用编译 Library 的插件:

groovy apply plugin:'com.android.library'

到现在为止,我们知道每一个 Library 和每一个 App 都是单独的 Project。根据 Gradle 的要求,每一个 Project 在其根目录下都需要有一个 build.gradlebuild.gradle 文件就是该 Project 的编译脚本,类似于 Makefile。

构建过程

新建一个 Android 项目,目录结构如下:

 
   
   
 
  1. ProjectName

  2.    |-app

  3.        |-build

  4.        |-lib

  5.        |-src

  6.        |-build.gradle  //后面讨论

  7.    |-library-test

  8.        |-build.gradle  //后面讨论

  9.    |-gradle

  10.        |-wrapper

  11.    |-build.gradle  //*

  12.    |-settings.gradle   //*

通过目录结构可以看出来,每一个 Project 中都有一个 build.gradle 文件,里面的内容后面再介绍。

在上面项目中有 applibrary-test 两个 Project,如果编译某个 Project 则需要 cd 到某个 Project 目录中。比如, cd xxx/app 然后执行 gradle xxx xxx 是 task 的名字。

这很麻烦啊,有 10 个独立 Project,就得重复执行 10 次这样的命令。更有甚者,所谓的独立 Project 其实有依赖关系的。那么,我想在项目目录下,直接执行 gradle xxx 是否能够把所有的 Project 都编译出来呢?

答案是可以的。 在Gradle中,这叫Multi-Projects Build。需要在根目录下放一个 build.gradle 和一个 settings.gradle

可以看到 project/build.gradle 文件中的内容类似如下:

 
   
   
 
  1. buildscript {

  2.    //为当前项目配置仓库

  3.    repositories {

  4.        //jcenter 是一个函数,表示编译过程中依赖的库,

  5.        //所需的插件可以在 jcenter 仓库中下载

  6.        jcenter()

  7.    }

  8.    //定义编译脚本依赖的库

  9.    dependencies {

  10.         //表示我们编译的时候,依赖 Android 开发的 gradle 插件

  11.        classpath 'com.android.tools.build:gradle:3.0.1'

  12.    }

  13. }

  14. //为所有的子项目配置

  15. allprojects {

  16.    repositories {

  17.        jcenter()

  18.    }

  19. }

  20. // 项目 clean task

  21. task clean(type: Delete) {

  22.    delete rootProject.buildDir

  23. }

这个 build.gradle 主要作用是配置其他子 Project 。比如,为子 Project 添加一些属性。这个 build.gradle 有没有都无所属。

project/settings.gradle 则主要定义了根目录下具体有多少个 Gradle Project ,其内容类似于:

 
   
   
 
  1. include ':app', ':library-test'

这个文件很重要,名字必须是 settings.gradle。它里边用来告诉 Gradle,这个 Multi-Projects 包含多少个子 Project。

生命周期

当我们执行 Gradle 的时候,Gradle 首先是按顺序解析各个 Gradle 文件。这里边就有所所谓的生命周期的问题,即先解析谁,后解析谁。

  • Build Lifecycle - 官方文档

Gradle 构建系统有自己的生命周期,初始化、配置和运行三个阶段。


初始化阶段

  • 读取项目根目录中 setting.gradle 中的 include 信息,决定有哪几个工程加入构建,并为每个项目创建一个 Project 对象实例,比如下面有两个工程:

    所以 Gradle 将会为它们两个分别创建一个 Project 对象实例。


    1. include ':app', ':library-test'

  • 配置阶段

    执行所有 Project 中的 build.gradle 脚本,配置 Project 对象,一个对象由多个任务组成,此阶段也会去创建、配置 task 及相关信息。


  • 运行阶段

    根据 Gradle 命令传递过来的 task 名称,执行相关依赖任务。task 的执行阶段。首先执行 doFirst{} 闭包中的内容,最后执行 doLast{} 闭包中的内容。


Gradle 基于 Groovy,Groovy 又基于 Java。所以,Gradle 执行的时候和 Groovy 一样,会把脚本转换成 Java 对象。Gradle 主要有三种对象,这三种对象和三种不同的脚本文件对应,在 Gradle 执行的时候,会将脚本转换成对应的对端:

  • Gradle 对象

    当我们执行 gradle xxx 或者什么的时候,gradle 会从默认的配置脚本中构造出一个 Gradle 对象。在整个执行过程中,只有这么一个对象。Gradle 对象的数据类型就是 Gradle。我们一般很少去定制这个默认的配置脚本。


  • Project 对象

    每一个 build.gradle 会转换成一个 Project 对象。


  • Settings 对象

    显然,每一个 settings.gradle 都会转换成一个 Settings 对象。


对于其他 gradle 文件,除非定义了 class,否则会转换成一个实现了 Script 接口的对象。

Task

Task 是 Gradle 中的一种数据类型,它代表了一些要执行或者要干的工作。不同的插件可以添加不同的 Task。每一个 Task 都需要和一个 Project 关联。

Task 的 API 文档位于:https://docs.gradle.org/current/dsl/org.gradle.api.Task.html

  • 任务创建

    task 中有一个 action list,task 运行时会顺序执行 action list 中的 action,doLast 或者 doFirst 后面跟的闭包就是一个 action,doLast 是把 action 插入到 list 的最后面,而 doFirst 是把 action 插入到 list 的最前面。


    1. task hello {

    2.  doLast {//doLast 可用 << 代替,不推荐此写法

    3.      println "hello"//在 gradle 的运行阶段打印出来

    4.  }

    5. }

    6. task hello {

    7.  println "hello"//在 gradle 的配置阶段打印出来

    8. }

  • 任务依赖

    当我们在 Android 工程中执行 ./gradlew build 的时候,会有很多任务运行,因为 build 任务依赖了很多任务,要先执行依赖任务才能运行当前任务。

    任务依赖主要使用 dependsOn 方法,如下所示:

    另外,你也可以在 Task 的配置区中来声明它的依赖:

    mustRunAfter:

    例如下面的场景,A 依赖 B,A 又同时依赖 C。但执行的结果可能是 B -> C -> A,我们想 C 在 B 之前执行,可以使用 mustRunAfter。

    finalizedBy:在 Task 执行完之后要执行的 Task。


    1. task A << {println 'Hello from A'}

    2. task B << {println 'Hello from B'}

    3. task C << {println 'Hello from C'}

    4. A.dependsOn B

    5. A.dependsOn C

    6. B.mustRunAfter C    //B 必须在 C 之后执行

    7. task A << {println 'Hello from A'}

    8. task B {

    9.  dependsOn A

    10.  doLast {

    11.      println 'Hello from B'  

    12.  }

    13. }

    14. task A << {println 'Hello from A'}

    15. task B << {println 'Hello from B'}

    16. task C << {println 'Hello from C'}

    17. B.dependsOn A    //执行 B 之前会先执行 A

    18. C.dependsOn B    //执行 C 之前会先执行 B

  • 增量构建

    你在执行 Gradle 命令的时候,是不是经常看到有些任务后面跟着 [UP-TO-DATE],这是怎么回事?

    在 Gradle 中,每一个 Task 都有 inputs 和 outputs,如果在执行一个 Task时,如果它的输入和输出与前一次执行时没有发生变化,那么 Gradle 便会认为该 Task 是最新的,因此 Gradle 将不予执行,这就是增量构建的概念。

    一个 Task 的 inputs 和 outputs 可以是一个或多个文件,可以是文件夹,还可以是 project 的某个 property,甚至可以是某个闭包所定义的条件。自定义 Task 默认每次执行,但通过指定 inputs 和 outputs,可以达到增量构建的效果。


  • 依赖传递

    Gradle 默认支持传递性依赖,比如当前工程依赖包A,包 A 依赖包 B,那么当前工程会自动依赖包 B。同时,Gradle 支持排除和关闭依赖性传递。

    之前引入远程 AAR,一般会这样写:

    上面的写法会关闭依赖性传递,所以有时候可能就会出问题,为什么呢?

    本来以为 @aar 是指定下载的格式,但其实不然,远程仓库文件下载格式应该是由 pom 文件中 packaging 属性决定的,@ 符号的真正作用是 Artifact only notation,也就是只下载文件本身,不下载依赖,相当于变相的关闭了依赖传递。


    1. compile 'com.somepackage:LIBRARY_NAME:1.0.0@aar'

常用命令

$ gradle tasks // 查看根目录包含的 task

$ gradle tasks -all // 查看根目录包含的所有 task

$ gradle app:tasks //查看具体 Project 中的 task

$ gradle projects //查看项目下所有的子 Project

$ gradle build //构建项目

Android Studio 的 Terminal 中:

$ ./gradlew tasks // 查看根目录包含的 task

$ ./gradlew tasks -all // 查看根目录包含的所有 task

$ ./gradlew app:tasks //查看具体 Project 中的 task

$ ./gradlew projects //查看项目下所有的子 Project

$ ./gradlew build //构建项目

环境变量

Mac 中使用 Gradle 命令会出现 bash gradle commandnotfound 原因是没有配置环境变量。

  • 找到 Gradle 所在的路径

    在 Finder 应用程序中 -> Android Studio 右键 -> 显示包内容,打开之后按照 Contents -> gradle -> gradle-xxx -> bin -> gradle。

    找到 gradle 文件后,右键 -> 显示简介,复制路径,类似:


/Applications/Android\ Studio.app/Contents/gradle/gradle-4.4

  • 设置环境变量

$ cd ~ //返回 Home 目录

$ touch .baseprofile //创建 baseprofile 文件

$ open -e .baseprofile //使用文本编辑器打开 baseprofile 文件

输入以下内容:

jsexportGRADLE_HOME=/Applications/Android\Studio.app/Contents/gradle/gradle-4.4exportPATH=${PATH}:${GRADLE_HOME}/bin

$ source .bash_profile //使修改生效

$ gradle -v //显示 gradle 版本

```js WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/Applications/Android%20Studio.app/Contents/gradle/gradle-4.4/lib/groovy-all-2.4.12.jar) to method java.lang.Object.finalize() WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClass WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release


Gradle 4.4


Build time: 2017-12-06 09:05:06 UTC Revision: cf7821a6f79f8e2a598df21780e3ff7ce8db2b82

Groovy: 2.4.12 Ant: Apache Ant(TM) version 1.9.9 compiled on February 2 2017 JVM: 9.0.1 (Oracle Corporation 9.0.1+11) OS: Mac OS X 10.13.3 x86_64 ```

如果显示如下问题,需要修改权限:

js-bash:/Applications/AndroidStudio.app/Contents/gradle/gradle-4.4/bin/gradle:Permissiondenied

  • 修改权限

//进入 gradle 中 bin 目录

$ cd /Applications/Android\ Studio.app/Contents/gradle/gradle-4.4/bin

$ ls -l //查看权限

js total24-rw-r--r--1jeanboy admin528641617:49gradle-rw-r--r--1jeanboy admin225041617:49gradle.bat

$ chmod +x gradle //增加权限 $ chmod +x gradle.bat //增加权限

js total24-rwxr-xr-x1jeanboy admin528641617:49gradle-rwxr-xr-x1jeanboy admin225041617:49gradle.bat

推荐阅读

1. 

2. 

3. 

4. 

5. 

6. 




欢迎 长按下图 -> 识别图中二维码



以上是关于Gradle DSL的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 kotlinscript DSL (build.gradle.kts) 通过 url 添加 maven 存储库

在 buildSrc 中定义的 Gradle Kotlin DSL 未解析的引用

使用 Gradle Kotlin DSL 发布 Kotlin MPP 元数据

kotlin gradle dsl问题:类型不匹配:推断类型是字符串但URI!预计

Android Gradle开发指南

Gradle DSL