Gradle 详细手册(从入门到入土)

·  阅读 2447

导语

本文在Gradle文档基础上进行了提取整理,篇幅较长,请按需阅读

Gradle概述

Gradle是专注于灵活性和性能的开源构建自动化工具,一般使用Groovy或KotlinDSL编写构建脚本。 本文只使用Groovy

Gradle的特点:

  • 高性能

Gradle通过仅运行需要运行的任务来避免不必要的工作。 可以使用构建缓存来重用以前运行的任务输出,甚至可以使用其他计算机(具有共享的构建缓存)重用任务输出。

  • JVM基础

Gradle在JVM上运行。熟悉Java的用户来可以在构建逻辑中使用标准Java API,例如自定义任务类型和插件。 这使得Gradle跨平台更加简单。(Gradle不仅限于构建JVM项目,它甚至附带对构建本机项目的支持。)

  • 约束

和Maven一样,Gradle通过实现约束使常见类型的项目(例如Java项目)易于构建。 应用适当的插件,您可以轻松地为许多项目使用精简的构建脚本。 但是这些约定并没有限制您:Gradle允许您覆盖它们,添加自己的任务以及对基于约定的构建进行许多其他自定义操作。

  • 可扩展性

您可以轻松扩展Gradle以提供您自己的任务类型甚至构建模型。

  • IDE支持

支持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过程已有太多资料,本文不做详细介绍。可使用命令检测自己电脑是否成功安装 jdk

安装Gradle

用软件包安装Gradle

SDKMAN

sdk install gradle
复制代码

Homebrew

brew install gradle
复制代码

手动安装(推荐方式)

下载

services.gradle.org/distributio… (全部版本目录地址,可以查看最新版本)
services.gradle.org/distributio… (截止至2021.02.24最新)

文件介绍

gradle-6.8.3-docs.zip  //文档
gradle-6.8.3-src.zip  //源码
gradle-6.8.3-bin.zip  //软件包
gradle-6.8.3-all.zip   //全部文件

复制代码
   bin :运行文件
   lib:依赖库
   
   docs:文档
   
   src:源文件
   
   init.d :初始化脚本目录,可自己添加
复制代码

配置环境变量

export GRADLE_HOME=/Users/dxs/temp/gradle-6.8.3
export PATH=$PATH:$GRADLE_HOME/bin
复制代码

运行

输入gradle -v 检测是否配置成功 配置环境变量

HelloWord

编写一个build.gradle文件,输入以下内容

task hello{
    doLast {
        println 'Hello World'
    }
}
复制代码

命令行输入gradle -q hello即可运行

HelloWord

版本管理

项目迁移

Gradle Wrapper

定义

Gradle Wrapper是一个脚本,可调用Gradle的声明版本,并在必要时预先下载。因此,开发人员可以快速启动并运行Gradle项目,而无需遵循手动安装过程
Wrapper

添加wrapper

在build.gradle同级目录下使用命令gradle wrapper可以生成gradle wrapper目录

gradle wrapper
复制代码

Wrapper

  • gradle-wrapper.jar

    WrapJAR文件,其中包含用于下载Gradle发行版的代码。

  • gradle-wrapper.properties

    一个属性文件,负责配置Wrapper运行时行为,例如与该版本兼容的Gradle版本。请注意,更多常规设置(例如,将 Wrap配置为使用代理)需要进入其他文件。

  • gradlew, gradlew.bat

    一个shell脚本和一个Windows批处理脚本,用于使用 Wrap程序执行构建。

可以通过命令控制生成选项

--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分布。
复制代码

例:

gradle wrapper --gradle-version 6.8.3 --distribution-type all

复制代码

Wrapper属性文件

一般生成Wrapper会得到如下属性文件 gradle-wrapper.properties

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:

gradlew.bat build
复制代码

shell:

./gradlew build
复制代码

升级

  • 更改gradle-wrapper.properties文件中的distributionUrl属性
  • 使用gradlew wrap --gradle-version 命令
./gradlew wrap --gradle-version 6.8.3
复制代码

自定义Gradle_Wrap

可以通过自定义wrapper少去一些重复操作或定制功能,如

build.gradle

   tasks.named('wrapper') {
       distributionType = Wrapper.DistributionType.ALL
   }
     
   task wrapper(type: Wrapper) {
       gradleVersion = '6.8.3'
   }

复制代码

Gradle 环境

环境变量

GRADLE_OPTS

指定启动Gradle客户端VM时要使用的JVM参数。客户端VM仅处理命令行输入/输出,因此很少需要更改其VM选项。实际的构建由Gradle守护程序运行,不受此环境变量的影响。
复制代码

GRADLE_USER_HOME

指定Gradle用户的主目录(如果未设置,则默认为$USER_HOME/.gradle)。
复制代码

JAVA_HOME

指定要用于客户端VM的JDK安装目录。除非Gradle属性文件使用org.gradle.java.home指定了另一个虚拟机,否则此虚拟机也用于守护程序。
复制代码

注意:命令行选项和系统属性优先于环境变量。

Gradle属性

你可以通过以下方式自己配置你的项目属性,如果存在多个,则从上到下优先读取 :

  • 系统属性,例如在命令行上设置 -Dgradle.user.home
  • GRADLE_USER_HOME目录中的gradle.properties
  • 项目根目录中的gradle.properties
  • Gradle安装目录中的gradle.properties

gradle.properties

# 当设置为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
复制代码

系统属性

# 指定用户名以使用HTTP基本认证从服务器下载Gradle发行版
systemProp.gradle.wrapperUser = myuser
# 指定使用Gradle Wrapper下载Gradle发行版的密码
systemProp.gradle.wrapperPassword = mypassword
# 指定Gradle用户的主目录
systemProp.gradle.user.home=(path to directory)
复制代码

项目属性

org.gradle.project.foo = bar
复制代码

守护程序

Gradle在Java虚拟机(JVM)上运行,并使用一些支持库,这些库需要很短的初始化时间。但有时启动会比较慢。
解决此问题的方法是Gradle Daemon :这是一个长期存在的后台进程,与以前相比,它可以更快地执行构建。

可通过命令获取运行守护程序状态
图 IDLE为空闲,BUSY为繁忙,STOPPED则已关闭

守护程序默认打开,可通过以下属性关闭
.gradle/gradle.properties

org.gradle.daemon=false
复制代码

也可用命令gradle --stop手动关闭守护程序
图

Gradle命令行

命令行格式

gradle [taskName ...] [--option-name ...]
复制代码

如果指定了多个任务,则应以空格分隔。

选项和参数之间建议使用=来指定。

--console=plain
复制代码

启用行为的选项具有长形式的选项,并带有由指定的反函数--no-。以下是相反的情况。

--build-cache
--no-build-cache
复制代码

许多命令具有缩写。例如以下命令是等效的:

--help
-h
复制代码

使用Wrapper时候应该用./gradlewgradlew.bat取代gradle

#获取帮助
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或将一些存档发布到存储库。

项目

表.项目属性

名称类型默认值
projectProject该Project实例
nameString项目目录的名称。
pathString项目的绝对路径。
descriptionString 项目说明。
projectDirFile包含构建脚本的目录。
buildDirFileprojectDir /build
groupObjectunspecified
versionObjectunspecified
antant build一个AntBuilder实例

任务

定义任务

  1. 使用字符串作为任务名称定义任务
  2. 使用tasks容器定义任务
  3. 使用DSL特定语法定义任务

例:
build.gradle

// 使用字符串作为任务名称定义任务
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特定语法访问任务
  • 通过任务集合访问任务
  • 通过路径访问
  • 按任务类型访问任务
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

task hello
复制代码

build.gradle

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

Copy myCopy = tasks.getByName("myCopy")
myCopy.from 'resources'
myCopy.into 'target'
myCopy.include('**/*.txt', '**/*.xml', '**/*.properties')
复制代码

使用DSL特定语法配置任务

例:
build.gradle

// Configure task using Groovy dynamic task configuration block
myCopy {
   from 'resources'
   into 'target'
}
myCopy.include('**/*.txt', '**/*.xml', '**/*.properties')
复制代码

用配置块定义一个任务

例:
build.gradle

task copy(type: Copy) {
   from 'resources'
   into 'target'
   include('**/*.txt', '**/*.xml', '**/*.properties')
}
复制代码

将参数传递给任务构造函数

与Task在创建后配置可变属性相反,您可以将参数值传递给Task类的构造函数。为了将值传递给Task构造函数,您必须使用@javax.inject.Inject注释相关的构造函数。

首先创建带有@Inject构造函数的任务类

class CustomTask extends DefaultTask {
    final String message
    final int number

    @Inject
    CustomTask(String message, int number) {
        this.message = message
        this.number = number
    }
}
复制代码

然后创建一个任务,并在参数列表的末尾传递构造函数参数。

tasks.create('myTask', CustomTask, 'hello', 42)
复制代码

你也可以使用Map创建带有构造函数参数的任务

task myTask(type: CustomTask, constructorArgs: ['hello', 42])
复制代码

向任务添加依赖项

从另一个项目添加对任务的依赖

project('project-a') {
    task taskX {
        dependsOn ':project-b:taskY'
        doLast {
            println 'taskX'
        }
    }
}

project('project-b') {
    task taskY {
        doLast {
            println 'taskY'
        }
    }
}
复制代码

使用任务对象添加依赖

task taskX {

    doLast {

        println 'taskX'

    }

}



task taskY {

    doLast {

        println 'taskY'

    }

}



taskX.dependsOn taskY
复制代码

使用惰性块添加依赖项

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:应该在之后运行 例
task taskX {
    doLast {
        println 'taskX'
    }
}
task taskY {
    doLast {
        println 'taskY'
    }
}
taskY.mustRunAfter taskX
复制代码

should run after被忽略的情况

  • 引入排序周期。
  • 使用并行执行时,除了 "should run after "任务外,一个任务的所有依赖关系都已被满足,

引入排序周期例子

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

task copy(type: Copy) {
   description 'Copies the resource directory to the target directory.'
   from 'resources'
   into 'target'
   include('**/*.txt', '**/*.xml', '**/*.properties')
}
复制代码

跳过任务

onlyIf跳过

hello.onlyIf { !project.hasProperty('skipHello') }
//StopExecutionException跳过
compile.doFirst {
    if (true) { throw new StopExecutionException() }
}
复制代码

禁用任务

task disableMe {
    doLast {
        println 'This should not be printed if the task is disabled.'
    }
}
disableMe.enabled = false
复制代码

任务超时

task hangingTask() {
    doLast {
        Thread.sleep(100000)
    }
    timeout = Duration.ofMillis(500)
}
复制代码

任务规则

有时您想执行一个任务,该任务的行为取决于较大或无限数量的参数值范围。提供此类任务的一种非常好的表达方式是任务规则:

任务规则

tasks.addRule("Pattern: ping<ID>") { String taskName ->
    if (taskName.startsWith("ping")) {
        task(taskName) {
            doLast {
                println "Pinging: " + (taskName - 'ping')
            }
        }
    }
}

task groupPing {
    dependsOn pingServer1, pingServer2
}
复制代码
> gradle -q groupPing

Ping:Server1
Ping:Server2
复制代码

终结器任务

计划运行终结任务时,终结任务会自动添加到任务图中。即使完成任务失败,也将执行终结器任务。

task taskX {
    doLast {
        println 'taskX'
    }
}
task taskY {
    doLast {
        println 'taskY'
    }
}
taskX.finalizedBy taskY
复制代码
> gradle -q taskX

TaskX
TaskY
复制代码

动态任务

Groovy或Kotlin的功能可用于定义任务以外的其他功能。例如,您还可以使用它来动态创建任务。

build.gradle

4.times { counter ->
    task "task$counter" {
        doLast {
            println "I'm task number $counter"
        }
    }
}
复制代码

gradle -q task1 输出

> gradle -q task1
I'm task number 1
复制代码

Groovy_DSL快捷方式符号

访问任务有一种方便的表示法。每个任务都可以作为构建脚本的属性来使用:

例.作为构建脚本的属性访问任务

build.gradle

task hello {
    doLast {
        println 'Hello world!'
    }
}
hello.doLast {
    println "Greetings from the $hello.name task."
}
复制代码

输出 gradle -q hello

> gradle -q hello
Hello world!
Greetings from the hello task.
复制代码

这将启用非常可读的代码,尤其是在使用插件提供的任务(例如compile任务)时。

额外任务属性

您可以将自己的属性添加到任务。要添加名为的属性myProperty,请设置ext.myProperty为初始值。从那时起,可以像预定义的任务属性一样读取和设置属性。

build.gradle

task myTask {
    ext.myProperty = "myValue"
}

task printTaskProperties {
    doLast {
        println myTask.myProperty
    }
}
复制代码

输出 gradle -q printTaskProperties

> gradle -q printTaskProperties
myValue
复制代码

额外的属性不仅限于任务。您可以在Extra属性中阅读有关它们的更多信息。

默认任务

Gradle允许您定义一个或多个默认任务。

build.gradle

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

> gradle -q
Default Cleaning!
Default Running!
复制代码

这等效于运行gradle clean run。在多项目构建中,每个子项目可以有其自己的特定默认任务。如果子项目未指定默认任务,则使用父项目的默认任务(如果已定义)。

Groovy基础

基本规则

  • 没有分号
  • 方法括号可以省略
  • 方法可以不写return,返回最后一句代码
  • 代码块可以作为参数传递

定义

def param = 'hello world'
def param1 = "hello world"
println "${param1} ,li" 
复制代码

${}里面可以放变量,也可以是表达式,只有双引号里面可以使用

声明变量

可以在构建脚本中声明两种变量:局部变量和额外属性。

局部变量

局部变量用def关键字声明。它们仅在声明它们的范围内可见。局部变量是基础Groovy语言的功能。

def dest = "dest"
task copy(type: Copy) {
    from "source"
    into dest
}
复制代码

ext属性

Gradle的域模型中的所有增强对象都可以容纳额外的用户定义属性。

可以通过拥有对象的ext属性添加,读取和设置其他属性。可以使用一个ext块一次添加多个属性。

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

> gradle -q printProperties
3.1.0.RELEASE
build@master.org
main
plugin
复制代码

变量范围:本地和脚本范围

用类型修饰符声明的变量在闭包中可见,但在方法中不可见。

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

> groovy作用域
localScope1
localScope2
scriptScope
localScope1
localScope2
scriptScope
localScope1NotAvailable
localScope2NotAvailable
scriptScope
复制代码

对象

使用对象

您可以按照以下易读的方式配置任意对象。

build.gradle

import java.text.FieldPosition

task configure {
    doLast {
        def pos = configure(new FieldPosition(10)) {
            beginIndex = 1
            endIndex = 5
        }
        println pos.beginIndex
        println pos.endIndex
    }
}
复制代码
> gradle -q configure
1
5
复制代码

使用外部脚本配置任意对象

您也可以使用外部脚本配置任意对象。

build.gradle

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

// Set properties.
beginIndex = 1
endIndex = 5
复制代码

输出 gradle -q configure

> gradle -q configure
1
5
复制代码

属性访问器

Groovy自动将属性引用转换为对适当的getter或setter方法的调用。

build.gradle

// Using a getter method
println project.buildDir
println getProject().getBuildDir()

// Using a setter method
project.buildDir = 'target'
getProject().setBuildDir('target')
复制代码

闭包

闭包(闭合代码块,可以引用传入的变量)

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使用该对象来查找不是闭包的局部变量或参数的变量和方法引用。

例.闭包委托

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()
        }
    }
}
复制代码

输出

> Task :configClosure
code:cix;id:1

BUILD SUCCESSFUL in 276ms
复制代码

例:使用必包委托设置依赖

    dependencies {
        assert delegate == project.dependencies
        testImplementation('junit:junit:4.13')
        delegate.testImplementation('junit:junit:4.13')
    }
复制代码

方法

方法调用上的可选括号

括号对于方法调用是可选的。

build.gradle

test.systemProperty 'some.prop', 'value'
test.systemProperty('some.prop', 'value')
复制代码

闭包作为方法中的最后一个参数

当方法的最后一个参数是闭包时,可以将闭包放在方法调用之后:

build.gradle

repositories {
    println "in a closure"
}
repositories() { println "in a closure" }
repositories({ println "in a closure" })
复制代码

集合

List

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

def map= ['name':'li', 'age':18]
println map[name] // li
println map.age // 18

list.each { //迭代
    println "${it.key}:${it.value}"
}

复制代码

JavaBean

class A{
    private int a; //可通过A().a 获取修改
    public int getB(){//可通过A().b获取,但不能修改
        1
    }
}
复制代码

build.gradle

// 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脚本添加了一些类。

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.*
复制代码

导入依赖

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--quietQUIET及更高
-w or --warnWARNING及更高
-i or --infoINFO及更高
-d or --debugDEBUG及更高版本(即所有日志消息)

Stacktrace命令行选项

-s or --stacktrace

打印简洁的堆栈跟踪信息,推荐使用
复制代码

-S or --full-stacktrace

打印完整的堆栈跟踪信息。
复制代码

编写日志

使用stdout编写日志消息

println 'A message which is logged at QUIET level'
复制代码

编写自己的日志消息

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写入日志消息

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


allprojects {
    afterEvaluate { project ->
        if (project.hasTests) {
            println "Adding test task to $project"
            project.task('test') {
                doLast {
                    println "Running tests for $project"
                }
            }
        }
    }
}

复制代码

输出 gradle -q test

> 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

repositories {
    mavenCentral()
}

task showRepos {
    doLast {
        println "All repos:"
        println repositories.collect { it.name }
    }
}

复制代码

init.gradle

allprojects {
    repositories {
        mavenLocal()
    }
}
复制代码

运行任务:
图

初始化脚本里面依赖添加依赖

init.gradle

initscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.apache.commons:commons-math:2.0'
    }
}
复制代码

多项目

可在settings.gradle文件中设置多个项目关系,如下项目结构

.
├── app
│   ...
│   └── build.gradle
└── settings.gradle
复制代码

settings.gradle

rootProject.name = 'basic-multiproject' //根项目名
include 'app' //子项目

复制代码

子项目间依赖

dependencies {
    implementation(project(":shared"))
}
复制代码

构建优化

关于构建优化可查阅构建优化

依赖

configurations

设置configurations 配置依赖信息

build.gradle

configurations {
    // 针对需要组件API的消费者的配置
    exposedApi {
        // canBeResolved 为true 则为可解析配置,为消费者
        canBeResolved = false
        // canBeConsumed 为true 则为消费析配置,为生产者
        canBeConsumed = true
    }
    // 为需要实现该组件的消费者提供的配置。
    exposedRuntime {
        canBeResolved = false
        canBeConsumed = true
    }
}
复制代码

依赖方式

模块依赖

build.gradle

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

dependencies {
    runtimeOnly files('libs/a.jar', 'libs/b.jar')
    runtimeOnly fileTree('libs') { include '*.jar' }
}
复制代码

项目依赖

build.gradle

dependencies {
    implementation project(':shared')
}
复制代码

依赖方式

  • compileOnly —用于编译生产代码所必需的依赖关系,但不应该属于运行时类路径的一部分
  • implementation(取代compile)-用于编译和运行时
  • runtimeOnly(取代runtime)-仅在运行时使用,不用于编译
  • testCompileOnly—与compileOnly测试相同
  • testImplementation —测试相当于 implementation
  • testRuntimeOnly —测试相当于 runtimeOnly

repositories

流行的公共存储库包括Maven Central, Bintray JCenter和Google Android存储库。
图

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

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

repositories {
    maven {
        url "https://repo.mycompany.com/releases"
        mavenContent {
            releasesOnly()
        }
    }
    maven {
        url "https://repo.mycompany.com/snapshots"
        mavenContent {
            snapshotsOnly()
        }
    }
}
复制代码

支持的元数据源

受支持的元数据源

元数据源描述排序MavenIvy/flat dir
gradleMetadata()寻找Gradle.module文件1
mavenPom()查找Maven.pom文件2
ivyDescriptor()查找ivy.xml文件2没有
artifact()直接寻找artifact3

从Gradle 5.3开始,解析元数据文件(无论是Ivy还是Maven)时,Gradle将寻找一个标记,指示存在匹配的Gradle Module元数据文件。如果找到它,它将代替Ivy或Maven文件使用。

从Gradle5.6开始,您可以通过添加ignoreGradleMetadataRedirection()到metadataSources声明来禁用此行为。

例.不使用gradle元数据重定向的Maven存储库

build.gradle

repositories {
    maven {
        url "http://repo.mycompany.com/repo"
        metadataSources {
            mavenPom()
            artifact()
            ignoreGradleMetadataRedirection()
        }
    }
}
复制代码

GAV坐标

GAV坐标一般指group,artifact,version

变体

构建变体是针对不同环境的配置,例如android开发中一般有debug和release两种变体
img

声明功能变体

可以通过应用javajava-library插件来声明功能变体。以下代码说明了如何声明名为mongodbSupport的功能:

示例1.声明一个功能变量

Groovy``Kotlin

build.gradle

group = 'org.gradle.demo'
version = '1.0'

java {
    registerFeature('mongodbSupport') {
        usingSourceSet(sourceSets.main)
    }
}
复制代码

元数据

从存储库中提取的每个模块都有与之关联的元数据,例如其组,名称,版本以及它提供的带有工件和依赖项的不同变体

可配置组件元数据规则的示例

build.gradle

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

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

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

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

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

@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

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.31.3.0-beta31.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

dependencies {
    implementation 'org.springframework:spring-web'
}

dependencies {
    constraints {
        implementation 'org.springframework:spring-web:5.0.2.RELEASE'
    }
}
复制代码

依赖方式

strictly

与该版本符号不匹配的任何版本将被排除。这是最强的版本声明。
在声明的依赖项上,strictly可以降级版本。
在传递依赖项上,如果无法选择此子句可接受的版本,将导致依赖项解析失败。
有关详细信息,请参见覆盖依赖项版本。
该术语支持动态版本。
复制代码

定义后,将覆盖先前的require声明并清除之前的 reject。

require

表示所选版本不能低于require可接受的版本,但可以通过冲突解决方案提高,即使更高版本具有排他性更高的界限。
这就是依赖项上的直接版本所转换的内容。该术语支持动态版本。
复制代码

定义后,将覆盖先前的strictly声明并清除之前的 reject。

prefer

这是一个非常软的版本声明。仅当对该模块的版本没有更强的非动态观点时,才适用。
该术语不支持动态版本。
复制代码

定义可以补充strictly或require。

在级别层次结构之外还有一个附加术语:

reject

声明模块不接受特定版本。如果唯一的可选版本也被拒绝,这将导致依赖项解析失败。该术语支持动态版本。
复制代码

动态版本

build.gradle

plugins {
    id 'java-library'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework:spring-web:5.+'
}
复制代码

版本快照

声明一个版本变化的依赖

build.gradle


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

configurations.all {
    resolutionStrategy.cacheDynamicVersionsFor 10, 'minutes'
}

复制代码

默认情况下,Gradle会将更改的模块缓存24小时。 要更改Gradle将为更改的模块缓存元数据和工件的时间,请使用:

例.改变模块缓存控制

build.gradle

configurations.all {
    resolutionStrategy.cacheChangingModulesFor 4, 'hours'
}
复制代码

锁定配置

锁定特定配置

build.gradle

configurations {
    compileClasspath {
        resolutionStrategy.activateDependencyLocking()
    }
}
复制代码

锁定所有配置

build.gradle

dependencyLocking {
    lockAllConfigurations()
}
复制代码

解锁特定配置

build.gradle

configurations {
    compileClasspath {
        resolutionStrategy.deactivateDependencyLocking()
    }
}
复制代码

锁定buildscript类路径配置

如果将插件应用于构建,则可能还需要利用依赖锁定。为了锁定用于脚本插件的classpath配置,请执行以下操作:

build.gradle

buildscript {
    configurations.classpath {
        resolutionStrategy.activateDependencyLocking()
    }
}

复制代码

使用锁定模式微调依赖项锁定行为

虽然默认锁定模式的行为如上所述,但是还有其他两种模式可用:

  • Strict模式 :在该模式下,除了上述验证外,如果被标记为锁定的配置没有与之相关联的锁定状态,则依赖性锁定将失败。

  • Lenient模式:在这种模式下,依存关系锁定仍将固定动态版本,但除此之外,依赖解析的变化不再是错误。

锁定模式可以从dependencyLocking块中进行控制,如下所示:

build.gradle

dependencyLocking {
    lockMode = LockMode.STRICT
}
复制代码

版本冲突

用force强制执行一个依赖版本

build.gradle

dependencies {
    implementation 'org.apache.httpcomponents:httpclient:4.5.4'
    implementation('commons-codec:commons-codec:1.9') {
        force = true
    }
}
复制代码

排除特定依赖声明的传递依赖

build.gradle

dependencies {
    implementation('commons-beanutils:commons-beanutils:1.9.4') {
        exclude group: 'commons-collections', module: 'commons-collections'
    }
}
复制代码

版本冲突时失败

build.gradle

configurations.all {
    resolutionStrategy {
        failOnVersionConflict()
    }
}

复制代码

使用动态版本时失败

build.gradle

configurations.all {
    resolutionStrategy {
        failOnDynamicVersions()
    }
}
复制代码

改变版本时失败

build.gradle

configurations.all {
    resolutionStrategy {
        failOnChangingVersions()
    }
}
复制代码

解析无法再现时失败

build.gradle

configurations.all {
    resolutionStrategy {
        failOnNonReproducibleResolution()
    }
}

复制代码

插件

插件作用:将插件应用于项目可以使插件扩展项目的功能。它可以执行以下操作:

  • 扩展Gradle模型(例如,添加可以配置的新DSL元素)
  • 根据约定配置项目(例如,添加新任务或配置合理的默认值)
  • 应用特定的配置(例如,添加组织存储库或强制执行标准) 简单来说,插件可以拓展项目功能,如任务,依赖,拓展属性,约束

插件类型

  • 二进制插件 :通过实现插件接口以编程方式编写二进制插件,或使用Gradle的一种DSL语言以声明方式编写二进制插件
  • 脚本插件 :脚本插件是其他构建脚本,可以进一步配置构建,并通常采用声明式方法来操纵构建

插件通常起初是脚本插件(因为它们易于编写),然后,随着代码变得更有价值,它被迁移到可以轻松测试并在多个项目或组织之间共享的二进制插件。

应用插件

二进制插件

实现了org.gradle.api.Plugin接口

apply plugin: 'com.android.application'
复制代码

apply plugin

apply plugin: 'java'  //id
==
apply plugin: org.gradle.api.plugins.JavaPlugin //类型
==
apply plugin: JavaPlugin          //org.gradle.api.plugins默认导入
复制代码

plugins DSL

plugins {
    id 'java' //应用核心插件
    id 'com.jfrog.bintray' version '0.4.1' //应用社区插件
    id 'com.example.hello' version '1.0.0' apply false //使用`apply false`语法告诉Gradle不要将插件应用于当前项目
}
复制代码

脚本插件

脚本插件会自动解析,可以从本地文件系统或远程位置的脚本中应用。可以将多个脚本插件(任意一种形式)应用于给定目标。

apply from:'version.gradle'
复制代码

apply可传入内容

void apply(Map<String,? options);
void apply(Closure closure);
void apply(Action<? super ObjectConfigurationAction> action);
复制代码

定义插件

定义一个带有ID的buildSrc插件
buildSrc / build.gradle

plugins {
    id 'java-gradle-plugin'
}
gradlePlugin {
    plugins {
        myPlugins {
            id = 'my-plugin'
            implementationClass = 'my.MyPlugin'
        }
    }
}
复制代码

第三方插件

通过将插件添加到构建脚本classpath中,然后应用该插件,可以将已发布为外部jar文件的二进制插件添加到项目中。可以使用buildscript {}块将外部jar添加到构建脚本classpath中。

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.0.1"
    }
}
复制代码

插件管理

pluginManagement {}块只能出现在settings.gradle文件中,必须是文件中的第一个块,也可以以settings形式出现在初始化脚本中。

settings.gradle

pluginManagement {
    plugins {
    }
    resolutionStrategy {
    }
    repositories {
    }
}
rootProject.name = 'plugin-management'
复制代码

init.gradle

settingsEvaluated { settings ->
    settings.pluginManagement {
        plugins {
        }
        resolutionStrategy {
        }
        repositories {
        }
    }
}
复制代码

例:通过pluginManagement管理插件版本。

settings.gradle

pluginManagement {
  plugins {
        id 'com.example.hello' version "${helloPluginVersion}"
  }
}

复制代码

gradle.properties

helloPluginVersion=1.0.0
复制代码

自定义插件存储库

要指定自定义插件存储库,请使用repositories {}块其中的pluginManagement {}:

settings.gradle

pluginManagement {
    repositories {
        maven {
            url '../maven-repo'
        }
        gradlePluginPortal()
        ivy {
            url '../ivy-repo'
        }
    }
}
复制代码

java库

导入java

apply plugin:'java'
复制代码

自定义路径

sourceSets {
    main {
         java {
            srcDirs = ['src']
         }
    }

    test {
        java {
            srcDirs = ['test']
        }
    }
}
复制代码
sourceSets {
    main {
        java {
            srcDir 'thirdParty/src/main/java'
        }
    }
}
复制代码

导入依赖

 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

include ':app'
rootProject.name = "GradleTest"
复制代码

安卓实用

设置签名

android  {
    signingConfig = {
        release {
            storeFile file("MYKEY.keystore")
            storePassword "storePassword"
            keyAlias "keyAlias"
            keyPassword "keyPassword"
        }
    }
}
复制代码

自定义输出apk文件名称

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

<meta-data android:name="paramName" android:value="${PARAM_NAME}">
android {
    productFlavors{
        google{
            manifestPlaceholders.put("PARAM_NAME",'google')
        }
    }
}
复制代码

多渠道

android {
    productFlavors{
        google{
            
        },
        baidu{
            
        }
    }
    productFlavors.all{ flavor->
        manifestPlaceholders.put("PARAM_NAME",name)
    }
}
复制代码

adb设置

adb工具

android {
    adbOptions{
        timeOutInMs = 5000 //5s超时
        installOptions '-r','-s' //安装指令
    }
}
复制代码

dexOptions

dex工具

android {
    dexOptions{
        incremental true //增量
        javaMaxHeapSize '4G'//dx最大队内存
        jumboMode true //强制开启jumbo跳过65535限制
        preDexLibraries true //提高增量构建速度
            threadCount 1 //dx线程数量
    }
}
复制代码

Ant

例.将嵌套元素传递给Ant任

build.gradle

task zip {
    doLast {
        ant.zip(destfile: 'archive.zip') {
            fileset(dir: 'src') {
                include(name: '**.xml')
                exclude(name: '**.java')
            }
        }
    }
}
复制代码

例.使用Ant类型

build.gradle

task list {
    doLast {
        def path = ant.path {
            fileset(dir: 'libs', includes: '*.jar')
        }
        path.list().each {
            println it
        }
    }
}
复制代码

例.使用定制的Ant任务

build.gradle

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目录下的工具,一个代码扫描工具,能够帮助我们识别资源、代码结构存在的问题

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 //是否显示所有输出,不截断
    }
}
复制代码

参考文献

  • Gradle 文档
  • 《Android Gradle权威指南 》(飞雪无情)
分类:
Android
标签:
收藏成功!
已添加到「」, 点击更改