bingmous

欢迎交流,不吝赐教~

导航

Gradle User Manual 8.5

官网:https://gradle.org/

Overview

Gradle是开源的自动构建工具,聚焦于灵活性和性能。Gradle脚本是使用Groovy或者Kotlin DSL写的。Gradle features:https://gradle.org/features/

  • 高可定制化:Gradle的建模方式以最基本的方式进行自定义和扩展
  • 快:完成任务快,通过重复利用前一个执行的输出、只处理变化的输入、并行执行
  • 强大:Gradle是Android的官方构建工具,并且支持许多流行的语言和技术

与maven的比较:https://gradle.org/maven-vs-gradle/

使用现有的Gradle builds:开发工具、Gradle命令行接口、Gradle Build Scan https://scans.gradle.com/

Gradle是什么

一个开源的自动化构建工具,足够灵活来构建任何类型的软件,Gradle只做少许的假设,关于你试图构建或如何构建,这让Gradle非常的灵活。

Gradle基于以下基本原则进行设计:

  • 高性能:避免不必要的工作,仅仅执行由于输出输出变化而必要的一些任务,使用各种缓存
  • JVM基础:Gradle运行与JVM之上,对于熟悉java的是一件好事,因为构建逻辑可以使用标准的java api,也利于在不同平台上运行Gradle
  • Conventions:常见的项目类型很容易通过常规构建,插件集默认让构建脚本最小化,但是这些常规并不会限制你:你可以通过配置、添加自己的task、及做其他的定制化
  • 扩展性:大多数构建都需要特殊的构建逻辑,你可以轻而易举的扩展Gradle来提供自己的构建逻辑
  • IDE支持:idea、vscode等
  • Insight:Build Scan提供构建过程中更详细的信息

用户手册

用户手册分为以下几个部分:

Running Gradle Builds
Learn Gradle basics and how to use Gradle to build your project.

Authoring Gradle Builds
Develop tasks and plugins to customize your build.

Authoring JVM Builds
Use Gradle with your Java project.

Working with Dependencies
Add dependencies to your build.

Optimizing Builds
Use caches to optimize your build and understand the Gradle daemon, incremental builds and file system watching.

Gradle on CI
Gradle integration with popular continuous integration (CI) servers.

Release

兼容性:https://docs.gradle.org/8.5/userguide/compatibility.html

安装

安装过程:https://docs.gradle.org/8.5/userguide/installation.html

如果只是想运行已经存在的Gradle build且里面有Gradle wrapper,不需要安装(有gradlew或者gradlew.bat文件),只需要保证前置条件满足即可。

前置条件:jdk环境,jdk8以上,java -version验证可用。

安装完整版,包含doc和sources,下载安装包:https://gradle.org/releases/ ,解压,将bin目录配置到系统环境变量path中,gradle -v验证安装成功。

注意:提前配置好原来Maven的安装目录环境变量M2_HOME,后续可以使用mavenLocal()从maven仓库获取依赖。及Gradle仓库环境变量GRADLE_USER_HOME,gradle的依赖及缓存会放在这个环境变量配置的目录下,而不是用户的.gradle目录下

Running Gradle Builds

了解核心概念、基本使用

核心概念

Gradle根据构建脚本(settings.gradlebuild.gradle)自动构建、测试、部署软件.
image

Gradle Basic

Projects
一个Gradle project是需要构建的软件的一部分,比如一个应用或者一个库。单项目只有一个root project,多项目包含一个root project和多个任意数量的子项目。一个子项目可以包含自己的子项目。

根项目的构建脚本是settings.gradle,子项目的构建脚本是build.gradle

Build Scripts
构建脚本,告诉Gradle如何构建整个项目。一个项目有一个或多个构建脚本。

Dependency Management
依赖管理,是项目声明和解析外部资源的自动化技术,每个项目一般都包含一些外部依赖,Gradle会在构建期间解析。

Tasks
task是一个基本的工作单元,比如编译代码、运行测试。每个项目的构建脚本或者插件里都有一个或多个任务

大多数情况下,一般使用已有的task,Gradle提供了大多数构建系统需要的task,比如java的单元测试,plugins提供了更多类型的任务。

  • task由以下组成:
  • actions:一部分动作,比如拷贝文件、编译代码
  • input:action需要的值、文件、目录
  • outputs:action修改或生成的文件或目录

Plugins
plugin用来扩展Gradle的能力,一般向一个项目提供task。

通过插件,你可以写一次在多个构建中使用。或者你可以在一个地方存储普通的配置,比如日志、依赖、版本管理。这减少了构建脚本中的重复。通过插件合适的建模构建过程可以很大的提高易用性和效率。


项目结构:大多数开发者第一次接触Gradle是通过现有的项目。项目根目录下有gradlew或者gradle.bat表明这是一个Gradle管理的项目。
image

调用Gradle:IDE会自动调用,初学者建议使用命令行进行学习,大多数项目不使用安装的Gradle而是使用项目根目录下的gradle wrapper脚本。

Gradle Wrapper Basics

推荐执行Gradle build的方式是使用Gradle Wrapper,它首先会下载指定版本的Gradle存储到Gradle User Home中,然后解压使用。

如果本地没有指定版本的Gradle,它就会去下载,它只做这件事。项目里有这个wrapper,即使没有安装Gradle的机器也可以使用Gradle

它有以下好处:

  • 标准化一个项目,使用指定版本的Gradle
  • 对不同的用户提供相同的Gradle版本
  • 对不同的执行环境提供相同的Gradle版本

使用Gradle Wrapper:推荐一直使用Gradle Wrapper来保证可靠的、可控的、标准的构建执行。针对不同的操作系统,可以执行gradlew或者gradlew.bat

其他参考:https://docs.gradle.org/8.5/userguide/gradle_wrapper.html#gradle_wrapper

命令行接口

命令行接口是IDE之外与gradle交互的主要方法:gradle [taskName...] [--option-name...]

在项目根目录执行指定task及它的所有依赖:gradle :taskName,task依赖:https://docs.gradle.org/8.5/userguide/tutorial_using_tasks.html#sec:task_dependencies

其他参考:https://docs.gradle.org/8.5/userguide/command_line_interface.html

settings.gradle文件

这个文件是每个gradle项目的入口,它的主要目的就是添加子项目到build中。对于单一项目,settings文件可以没有,如果是多项目,settings必须有并且声明所有的子项目。

settings文件是一个脚本,是settings.gradle(Groovy DSL)或者是settings.gradle.kts(Kotlin DSL),一般在项目的根目录下。

rootProject.name = 'root-project' //定义项目名称,每个build构建只有一个根项目

include('sub-project-a') //添加子项目 定义了项目的结构
include('sub-project-b')
include('sub-project-c')

具体如何写settings文件:https://docs.gradle.org/8.5/userguide/writing_settings_files.html#writing_settings_files

build.gradle文件

构建脚本详细的说明构建的配置、任务和插件。

每个Gradle build都至少有一个构建脚本,可以向其中添加两种类型的依赖:
1、Gradle和构建脚本依赖的库或插件
2、项目源码依赖的库

构建脚本是build.gradle或者build.gradle.kts

plugins {
    id 'application' //添加插件 
	//id("application") 或者这种写法 application插件创建一个可执行jvm应用。这个插件隐含的使用了java插件
}

application {
    mainClass = 'com.example.Main' //使用约定的属性
}

添加插件
插件扩展了Gradle的功能 可以向项目提供task 也叫做应用一个插件并且启动额外的功能。

java插件:https://docs.gradle.org/8.5/userguide/java_plugin.html#java_plugin
其他jvm插件:https://docs.gradle.org/8.5/userguide/java_library_plugin.html

使用约定属性
插件向项目添加了一些task,也添加了一些属性和方法。application插件定义了打包和分发应用的任务,比如run。也提供了声明java应用主类的方式

具体如何写build脚本:https://docs.gradle.org/8.5/userguide/writing_build_scripts.html#writing_build_scripts

依赖管理

声明、解析外部依赖的自动化技术。Gradle构建脚本中定义了项目需要外部依赖的过程,jar、插件、库或者源码等项目需要。

Version Catalog
提供了一种统一管理版本声明的方式在libs.versions.toml文件中,使得在子项目之间共享依赖更简单,也让团队在大项目中强制库和插件的版本。

版本目录有四个部分:

[versions] to declare the version numbers that plugins and libraries will reference.

[libraries] to define the libraries used in the build files.

[bundles] to define a set of dependencies.

[plugins] to define plugins.

这个文件在gradle/libs.versions.toml下,IDE和Gradle会自动引用

[versions]
androidGradlePlugin = "7.4.1"
mockito = "2.16.0"

[libraries]
google-material = { group = "com.google.android.material", name = "material", version = "1.1.0-alpha05" }
mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" }

[plugins]
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }

然后再构建脚本build.gradle里就可以引用插件和依赖了:

plugins {
   alias(libs.plugins.android.application)  
}

dependencies {
    // Dependency on a remote binary to compile and run the code
    implementation(libs.google.material)    

    // Dependency on a remote binary to compile and run the test code
    testImplementation(libs.mockito.core)   
}

依赖管理参考:https://docs.gradle.org/8.5/userguide/dependency_management_terminology.html#dependency_management_terminology

Task

构建中一些独立的工作单元,比如编译代码、创建jar、生成javadoc、发布归档到仓库等。

查看所有的task:gradlew tasks
运行task:gradlew run

task依赖,比如build task依赖compileJava。构建脚本可以指定任务依赖,Gradle会自动决定执行顺序。

task开发参考:https://docs.gradle.org/8.5/userguide/more_about_tasks.html#more_about_tasks

插件

插件用来扩展构建的能力并且定制Gradle。大多数特性,比如编译java代码,都是通过插件完成的。使用插件也是组织构建逻辑的主要机制。插件可以提供有用的任务,比如运行代码、创建文档、设置源文件、发布归档等。使用插件可以扩展项目和Gradle的能力。

比如springboot gradle插件提供springboot支持。

插件distribution
核心插件,由Gradle开发和维护:https://docs.gradle.org/8.5/userguide/plugin_reference.html#plugin_reference
社区插件,有社区共享:https://plugins.gradle.org/
本地插件,使用Gradle api自定义插件

在构建脚本里使用插件id、全局唯一标识符或者名称

plugins {
    id «plugin id» version «plugin version» [apply «false»]
}

Core plugins
核心插件是唯一的,可以使用短名称,比如java插件

plugins {
    id("java")
}

社区插件
Gradle活跃的社区有很多插件开发者提供各种各样能力的插件,比如springboot gradle插件

plugins {
    id("org.springframework.boot") version "3.1.5"
}

插件tasks
jib插件是用来构建java应用的docker和OCI镜像的,可以通过gradlew tasks查看通过插件添加的task

plugins {
  id("com.google.cloud.tools.jib") version "3.4.0"
}

插件开发参考:https://docs.gradle.org/8.5/userguide/custom_plugins.html#custom_plugins

Gradle增量构建和构建缓存

Gradle通过增量构建和构建缓存来减少构建时间。增量构建就是避免执行哪些输入没有变的task,重新执行没有意义,因为他们的输出是一样的。增量构建的执行需要任务定义输入和输出,Gradle确定输入和输出是否改变,如果变了则重新执行。

增量构建
增量构建总是开启的,可以通过verbose模式查看他们。如gradlew compileJava --console=verbose

显示UP-TO-DATE的那些task就是之前已经执行过且没有变化的。

如果要永久开启verbose模式,可以在gradle.properties添加org.gradle.console=verbose

构建缓存
构建缓存存储之前构建的结果,当需要的时候恢复他们,比如切换了分支,之前构建的东西可以缓存起来。当缓存被使用的时候,任务后面会显示FROM-CACHE,只要当前目录重新调整了,就会从FROM-CACHE标记为UP-TO-DATE

构建缓存参考:https://docs.gradle.org/8.5/userguide/build_cache.html#build_cache

持续构建

当文件变化时自动重新执行构建,使用-t--continuous,如gradle test --continuous

不相关的变化不会触发重新构建,持续构建依赖文件系统监听,如果文件系统不支持那么因为不会生效。使用--no-daemon是不会生效。

文件系统监听参考:https://docs.gradle.org/8.5/userguide/file_system_watching.html#sec:daemon_watch_fs

当Gradle监听到输入变化时,不会立即触发构建,会在一段时间内没有其他的变化时进行构建。可以通过配置org.gradle.continuous.quietperiod控制,单位ms

终止持续构建
如果使用交互式中断,使用Ctrl + D或使用kill,使用工具api则使用对应的取消机制。

局限性
由于文件系统监听工作的方式,创建之前不存在的目录可能不会触发重新构建,比如创建src/main/java。同样,如果输入是一个过滤的文件树,创建的新的文件不匹配这个文件树时,也不会触发构建。

改变没有追踪的输入task或者task没有输出时可能不会触发构建

Gradle只监听项目内的文件,项目目录外的文件变化不会被检测,也不会触发构建。

如果一个构建正在执行,改变了它的输入,那么这次构建就不会被认为是最新的。可以通过使用--info增加日志找到由哪些文件变化导致的。

Authoring Gradle Builds

定制构建逻辑、写自己的插件

Gradle的目录

gradle使用两个目录,GRADLE_USER_HOME和项目root目录
image

多项目构建

多项目构建有两个标准:

多项目路径
使用:分割项目名称,开头的:是可选的,表示root项目,可以使用gradle projects查看项目结构,项目路径反映了文件系统目录布局,但是在组合构建中也有例外。gradle -q projects查看项目结构,可以显示项目标识的全路径名称

多项目build是task的集合,区别就是可以控制执行哪个项目。通过名称执行,会执行当前项目及子项目下所有该名称的task,如gradle test,也可以通过全路径名称指定,如gradle :services:webservice:tasks

多项目构建和测试
在多项目构建中,经常要执行跨各种项目的所有任务,这时候可以使用buildNeededbuildDependents

如::services:person-service依赖:api:shared:api又依赖:shared,如果此时调整了api相关的代码,需要构建api及其相关它的依赖,可以使用gradle :api:build

如果没有调整api的代码,但是别人修改了api依赖模块的代码,想要看是否有问题,可以使用gradle :api:buildNeeded,会构建api及api所依赖的模块

如果调整了api的代码,想知道是否对依赖api模块的上层模块有影响,可以使用gradle :api:buildDependents

其他参考:https://docs.gradle.org/8.5/userguide/multi_project_builds.html#multi_project_builds

Gradle构建的生命周期

所有task在执行前gradle会构建好task graph(有向无环图)
image

Build Phases
Gradle计算和执行构建脚本在三个阶段

  • Initialization:检测settings.gradle文件,创建Settings实例,根据脚本判断哪些项目被添加进来,创建Project实例
  • Configuration:执行每个项目构建脚本,创建task graphe
  • Execution:执行Configuration阶段被选中的任务
    image

写settings.gradle文件

它对应一个Settings对象,脚本的内容就是设置Settings对象的过程。脚本里许多顶层属性和块都是Settings api的一部分,Settings对象暴露了一个标准的属性集合,如:

rootProject.name = "root" //实际上是 settings.rootProject.name = "root"

include("app") //实际上是settings.include("app")

//使用groovy脚本迭代目录 自动添加子项目
rootDir.listFiles().filter { it.isDirectory && (new File(it, "build.gradle.kts").exists()) }.forEach {
    include(it.name)
}

其中rootProject是Settings实例的一个属性,其他参考DSL:https://docs.gradle.org/8.5/dsl/org.gradle.api.initialization.Settings.html

Settings的api:https://docs.gradle.org/8.5/javadoc/org/gradle/api/initialization/Settings.html

image

Settings脚本的调用本质上是Settings对象的api调用

写build.gradle文件

在初始化阶段,gradle会为每个添加到settings.gradle文件中的项目创建一个Project对象实例,并查找相应的build.gradle文件。

在配置阶段执行脚本时,对Project实例进行配置。参考DSL:https://docs.gradle.org/8.5/dsl/org.gradle.api.Project.html

与settings.gradle类似,构建脚本中也有许多顶层的属性和块是Project api的一部分,如:

//打印当前项目的名称
println name //使用顶级属性
println project.name //使用当前项目的project对象实例

//它的参数类型是 PluginDependenciesSpec
plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.9.0'
    id 'application'
}

//它的返回值是 RepositoryHandler 由project.getRepositories()返回
repositories {
    mavenCentral()
}

//它的返回值是 DependencyHandler 由project.getDependencies()返回
dependencies {
    implementation("com.google.guava:guava:32.1.1-jre")
}

//application插件 由project对象相关的ExtensionContainer对象包含了应用到这个project的这个插件所有的设置和属性
application {
    mainClass = "com.example.Main" //使用插件可以添加属性(插件里面定义的)
}

//注册和配置task 可以使用 TaskContainer.register(java.lang.String) tasks是project的顶层属性 类型是TaskContainer
//register()会开启 task configuration avoidance,比create()更好
tasks.named('test') { //使用named()可以定位到一个task
    useJUnitPlatform()
}

image

task configuration avoidance: https://docs.gradle.org/8.5/userguide/task_configuration_avoidance.html#task_configuration_avoidance

构建脚本由statement和脚本块组成:statement包括方法调用、属性赋值、局部变量定义

println(project.layout.projectDirectory);
version = '1.0.0.GA'

repositories { //脚本块其实就是方法调用,参数是闭包
    google()
}

//局部变量定义
def dest = 'dest'
tasks.register('copy', Copy) {
    from 'source'
    into dest
}

//设置额外的属性
ext {
    springVersion = "3.1.0.RELEASE"
    emailNotification = "build@master.org"
}

//闭包委托 每个闭包都有一个delegate属性 它表示这个闭包要配置的对象 使用这个delegate查找属性和方法
//比如下面这个闭包 delegate就是依赖对象 类型是DependencyHandler 闭包里面的方法调用 都是针对这个依赖对象的
dependencies {
    assert delegate == project.dependencies
    testImplementation('junit:junit:4.13')
    delegate.testImplementation('junit:junit:4.13')
}

Using Tasks

task可以分组,在使用gradle tasks时会进行分组显示,gradle help --task <task-name>可以查看task的详细信息

Writing tasks

继承DefaultTask

Using Plugins


后续整理

  • Build Phases:Gradle计算和执行构建脚本在三个阶段
    • Initialization:为构建建立环境,并且确定哪个项目参与
    • Configuration:为build创建和配置任务图,基于用户想要执行的任务,确定哪些任务需要执行,以及执行顺序
    • Execution:执行Configuration阶段被选中的任务
  • Builds:build是Gradle项目中一系列任务的一次执行,通过命令行接口执行或者通过ide指定任务选择器执行,Gradle配置这次build并且选择任务进行执行。Gradle基于请求的任务和他们的依赖执行最小的任务集合

快速开始

如果你只是想运行一个存在的Gradle项目,并且项目里有gradle warpper,那么不需要安装Gradle,也即是在根目录下有gradlew或者gradlew.bat,只需要满足前置需求即可(有jdk)。

要创建一个新的build或者添加wrapper到存在的build去,需要安装Gradle

尝试使用Gradle:

  • buidling java application:
    • 创建一个目录,名称为项目名
    • 进入目录,执行执行gradle init
    • 选择application、java、groovy,其他默认即可
  • buidling java library

案例 构建java应用

image

gradle项目目录说明:

gradle相关:

  • gradle目录:里面有个wrapper目录,里面放了gradle-wrapper.jar和gradle-wrapper.properties,里面配置了这个项目使用的gradle版本
  • gradlew和gradlew.bat:是Gradle wrapper的启动脚本,用于在本地直接使用gradle

项目相关:

  • settings.gradle:这个build的名称和子项目配置文件,只在顶层有一个
  • app/build.gradle:是子项目app的构建脚本,每个project有一个
  • app/src:项目源码,app是子项目名称,这里只有一个子项目

settings.gradle:

rootProject.name = 'gradle-helloworld-java-app' //定义这个build的名字 建议使用固定的名字 因为目录名称可能会变
include('app') //定义这个build包含的子项目app 其中app里包含实际的代码和构建逻辑 如果有多个子项目可以再添加include('subproject2')

app/build.gradle:

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application' //配置插件application
}

repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral() //配置中央仓库
}

dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1' //配置测试依赖

    // This dependency is used by the application.
    implementation 'com.google.guava:guava:31.1-jre' //配置运行时依赖
}

application {
    // Define the main class for the application.
    mainClass = 'org.example.App' //定义主类
}

tasks.named('test') {
    // Use JUnit Platform for unit tests.
    useJUnitPlatform()
}

运行这个应用:gradlew run,第一次执行脚本会下载这个版本的gradle到~/.gradle/wrapper/dists目录下,
打包应用:gradlew build,application插件会打包应用及其依赖

使用idea,可以配置使用本地安装的gradle。

  • 默认gradle下载依赖到C:\Users\用户名\.gradle\caches\modules-2\files-2.1下,可以配置环境变量GRADULE_USER_HOME修改这个目录
  • 在gradle安装目录下init.d下添加脚本,指定下载源,其中mavenLocal()需要配置maven安装目录的环境变量M2_HOME
allprojects {
    repositories {
        mavenLocal()
		maven{name "Alibaba"; url "https://maven.aliyun.com/repository/public"}
		maven{name "Bstek"; url "https://nexus.bsdn.org/content/groups/public"}
		mavenCentral()
    }
 
	buildscript {
		repositories {
			maven{name "Alibaba"; url "https://maven.aliyun.com/repository/public"}
			maven{name "Bstek"; url "https://nexus.bsdn.org/content/groups/public"}
			maven{name "M2"; url "https://plugins.gradle.org/m2"}
		}
	}
}

案例 构建java库

与构建java应用不同的是,在项目的build.gradle文件里,使用的插件不同:

plugins {
    id 'java-library'
}

在gradle目录下,多了一个文件libs.versions.toml,记录了build.gradle里指定的依赖,build.gradle里引用了libs

posted on 2024-04-12 17:27  Bingmous  阅读(15)  评论(0编辑  收藏  举报