gradle学习笔记
优点:
- 支持了各种不同的依赖传递管理, 本地文件系统jar包, 还是maven, ivy的方式
- 是第一个构建集成工具, 可以将ant tasks, maven pom 转化成一个gradle脚本
- 方便移植
- gradle wrapper, 哪怕本地没有gradle, 也可以构建, 且还是用的设定好的版本
- 使用动态语言编写脚本很强大, groovy, 而不是xml, 且还有无其他好处, 等用到再体会吧
- 申明式的(个人感觉)
安装
jvm 参数配置, gradle 运行时的jvm参数可以通过: GRADLE_OPTS
或者是 JAVA_OPTS
来设置, 且同时生效, 但是注意, JAVA_OPTS
的修改对其他java应用是共享的
基础
概念差别
Projects 和 tasks的差异
gradle的构建是由单个或者多个projects组成的, 每个projects包括许多可构建组成部分, 可以是一个jar包, 或者一个web app, 或者其他项目产生的压缩包, jar包等. 而project内部由各个tasks组成, 每个task代表了执行过程中的一个原子操作, 像编译, 打包, 生产javadoc, 发布到仓库等.
入门
定义task
task hello {
doLast{
println 'hello world'
}
}
task hello {
println 'hello' //这是采用闭包的方式定义的task
}
groovy
采用groovy作为脚本语言, 它的语法十分简单, 在我看来又像java , 又像python , 还像scala.
class Example {
static void main(String[] args) {
int x = 4;
int X = 6;
println("sum of x and X is: " + (x+X));
}
}
变量定义, 基本数据类型, 基本运算符都与java类似, 循环 不过多了个for-in, 方法的定义也差不多, 不过它对闭包的支持比java好些.
使用groovy定义gradle任务
task count << {
4.times {println '$it'} // 相当于 4.times { it -> println(it) }
}
任务(task)构建
任务依赖关系
dependsOn: xxx
, 且task的申明定义顺序与依赖顺序无关
task hello << {
println 'hello'
}
task intro(dependsOn: hello) {
println 'i am gradle'
}
延迟依赖
延迟依赖的作用是什么?
在多项目构建中, 可以让taskX在taskY之前的定义, 且taskX依赖taskY.
动态任务
什么是动态任务: 任务的行为定义是推迟的, 推迟到被选择的时候, 依据传入的参数变化
4.times { count ->
task "task$counter" << {
println "i am task number $counter"
}
}
执行
> gradle -q task1
i am task number 1 //执行task1, task1的任务在被选择的时候被定义, 且行为也确定下来, 这是语言的特性还是?
任务创建后, 需要通过api进行相互之间的访问, 于是, 可以通过api , 增加任务之间的依赖, 这是否可以称之为动态依赖.
task0.dependsOn task2, task3 // 任务0,1,2,3都是上面的动态任务创建的
> gradle -q task0
i am task number 2
................ 3
................ 0
通过api给任务增加行为, 类似于python的动态添加方法以及切面行为, 比如给任务添加doFirst, doLast,分别会被添加到任务执行的开头和结尾, 当任务执行时候, 会按照特定顺序执行, 另外 << 是doLast的简写方式.
hello << {xxx}等同于 hello.doLast {xxx}
短标记法
通过 someTask.name
访问到任务的属性, 还可以通过 ext 为task增加自定义属性
task myTask {
ext.myPro = "myPro"
}
task printSomeProp << {
println myTask.myPro
}
调用Ant 任务
许多老的项目都是用ant构建, 或者从ant转化而来. Ant任务是gradle中的一等公民, gradle用grovvy对ant任务进行了整合, 自带AntBuilder, 这样Gradle中调用Ant任务比在 build.xml 中更方便和强大.
利用AntBuilder 创建任务, 执行任务
task loadfile << {
def files = file('../ddddir').listFiles().sort()
files.each { File file ->
if (file.isFile()) {
ant.loadFile(srcFile: file, property: file.name)
println " ** $file.name **"
println "${ant.properties[file.name]}"
}
}
}
gradle -q loadfile
定义默认任务
defaultTasks ..., ....
这个语法可以在build.gradle
文件的开头定义默认任务, 并且在执行的时候gradle -q
就可以, 而不用指定任务名.
DAG配置
task distribution << {
println "We build the zip with version=$version"
}
task release(dependsOn: 'distribution') << {
println 'We release now'
}
gradle.taskGraph.whenReady {taskGraph ->
if (taskGraph.hasTask(release)) {
version = '1.0'
} else {
version = '1.0-SNAPSHOT'
}
}
whenReady会在已发布的任务之前影响到已发布任务的执行, 在这里发布的命令如果是distribution, 那么回事1.0-snapshot, 而如果是release, 则是1.0
实战
gradle是通用工具,通过脚本构建可以实现任意的构建
java构建
gradle已经提供了java插件来适应java程序相似的流程: 编译源码, 单元测试, 创建jar包. 且插件还为工程定义了许多默认值, 比如源码的位置等等, 而工程遵循插件的默认值会让脚本文件更加简单, 当插件不满足时, 也可以自定义插件.
使用在build.gradle
中apply plugin: 'java'
这样就为工程添加了java的插件, 和内置的任务. 默认值的工程结构:
project
+ build
+ src/main/java
+ src/main/resources
+ src/test/java
+ src/test/resources
Gradle 默认会从 src/main/java
搜寻打包源码,在 src/test/java
下搜寻测试源码。并且 src/main/resources
下的所有文件按都会被打包,所有 src/test/resources
下的文件 都会被添加到类路径用以执行测试。所有文件都输出到 build 下,打包的文件输出到 build/libs 下.
构建项目的任务
> gradle build
:compileJava
:processResources
:classes
:jar
:assemble
:compileTestJava
:processTestResources
:testClasses
:test
:check
:build
BUILD SUCCESSFUL
JDK在编译的时候,会或许系统的编码, 如果用的是windows系统, 可能是GBK编码, 需要在gradle中设置下:
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
参考: https://blog.csdn.net/weixin_42356453/article/details/119828358
clean
删除build目录以及所有构建完成的文件.
assemble
编译并且打包jar/war文件, 但不会执行单测.
check
编译并且测试代码, 有些插件还会增强这个任务的功能, 比如Code-quality
插件执行的CheckStyle
外部依赖
gradle集成了各种依赖传递管理的方式
maven
repositories {
mavenCentral()
}
其他添加依赖的方式如:
dependencies {
compile group: xxx, name: yyyy, version: zzz
testCompile group: xx, name: yy, version: zz
}
自定义项目
sourceCompatibility=11 // 项目源码版本, 还可以设置目标字节码版本
version= '1.0'
jar {
manifest {
attributes 'Implementation-Title': 'Gradle Quickstart', 'Implementation-Version': version
}
}
为test添加属性
test {
systemProperties 'proterty': 'value'
}
定义属性,添加属性, 以控制脚本行为
发布项目
uploadArchives {
repositories {
flatDir {
dirs 'repos'
}
}
}
执行gradle uploadArchives
将jar包发布
多项目构建
需要在根目录中创建一个setting.gradle
来指明构建需要包含的项目
include "shard", "api", "services:webservice", "services:shared"
看lucene的
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
rootProject.name = "lucene-root"
includeBuild("dev-tools/missing-doclet")
include "lucene:analysis:common"
include "lucene:analysis:icu"
include "lucene:analysis:kuromoji"
include "lucene:analysis:morfologik"
include "lucene:analysis:morfologik.tests"
include "lucene:analysis:nori"
公共配置
多项目构建, 有些公共配置, 可以写在根目录的配置里, 子项目会迭代访问它并将其注入到自己的配置中
subprojects {
apply plugin: 'java'
apply plugin: 'xxxx'
repositories {
mavenCentral()
}
version = '1.0'
dependencies {
testComple 'junit:junit:4.11'
}
version = '1.0'
jar {
manifest.attribute provider: 'gradle'
}
}
多项目的工程依赖
项目之间的依赖是在子项目的构建文件build.gradle
中的定义的, 比如需要先build shared, 再build api, 即api 依赖shared, 那么在api的构建文件里:
dependencies {
compile project(':shared')
}
多项目工程的构建发布
api/build.gradle
task dist(type: zip) {
dependsOn spiJar
from 'src/dist'
into('libs') {
from spiJar.archivePath
from configuration.runtime
}
}
artifacts {
archives dist
}
依赖管理基础
什么是依赖管理?
答: 一: gradle 需要知道项目构建或者运行所需要的一些文件, 以及如何找到文件, 这成为项目的依赖. 二: 项目构建完成后, 需要自动上传到某些地方, 称之为发布.
工程的依赖, 需要开发人员告诉gradle, 工程依赖什么, 他们在哪里, gradle 会帮助开发人员将它们加入到构建中, 如果在远程, 比如maven或者ivy库, 就会下载, 这个过程称之为: 依赖解决.
而依赖的自身也可能会依赖其他依赖, 这叫做: 依赖传递, gradle在构建的时候也会深入寻找它们.
最后构建完成, 需要发布到某个地方被使用, 什么需要被发布, 发布到哪里, 以什么样的形式发布出去, 这个过程叫做: 发行.
依赖配置
比如java, java的依赖, 在不同工程的不同时期可以不同, 有些只需要在编译的时候需要,有些只需要运行时有就可.
compile
编译范围依赖在所有的 classpath 中可用,同时它们也会被打包
runtime
runtime 依赖在运行和测试系统的时候需要,但在编译的时候不需要。比如,你可能在编译的时候只需要 JDBC API JAR,而只有在运行的时候才需要 JDBC 驱动实现
testCompile
测试期编译需要的附加依赖
testRuntime
测试运行期需要
不同的插件提供了不同的标准配置,你甚至也可以定义属于自己的配置项。
外部依赖
和maven类似, 需要三元组 group:name:version来定位一个依赖. 三元组前是依赖的不同阶段.
仓库
如果不使用中央仓库: mavenCentral()
repositories {
maven {
url "http://repo.mycompany.com/maven2"
或者
url = uri("http://xxx")
}
}
repositories {
ivy {
url "http://repo.mycompany.com/repo"
}
}
repositories { // 本地
ivy {
// URL can refer to a local directory
url "../local-repo"
}
}
发布
uploadArchives {
repositories {
ivy {
credential {
username: xx
password: uu
}
url "http:/......"
}
}
}
当发布到maven仓库时候, 语法换一下, 且需要maven插件的支持以生成对应的pom.xml
apply plugin: 'maven'
uploadArchives {
repositories {
mavenDeployer {
repository(url: "file://localhost/tmp/myRepo/")
}
}
}
todos
- 编写脚本
- 任务详细
- 使用文件
- 调用ant
- 构建环境
- java插件
- scala插件
- osgi插件
- checkstyle插件