Gradle 堪称Java(JVM)世界构建技术的一个量子跃迁。本文介绍了Gradle的常用概念,并通过对Gradle项目(使用Gradle)自身构建过程的分析进一步了解Gradle。
2017-08-25 更新
剔除Jar包中包含的文件。结果可使用 vim jarname.jar
来查看。
jar {
exclude ('application.properties')
}
2017-07-05 更新
显示项目的依赖树:
# 只显示某个子项目的某一个scope的依赖
→ ~/work/test-proj ( master) $ ./gradlew :subproject:dependencies --configuration compile
2017-05-22 更新
The –refresh-dependencies option tells Gradle to ignore all cached entries for resolved modules and artifacts. A fresh resolve will be performed against all configured repositories, with dynamic versions recalculated, modules refreshed, and artifacts downloaded.
当开发团队更新了API包却没有变更 artifact version,使用 --refresh-dependencies
参数来重新下载依赖包。
→ ~/work/test-proj ( master) $ ./gradlew clean -Penv = testing distz --refresh-dependencies
–end–
Gradle提供:
类似Ant、灵活、通用的构建工具
可切换,build-by-convention(个人理解为可通过不同的命令进行不同的构建)
多项目构建(multi-project)的支持
强大的依赖管理(基于Apache Ivy)
支持已存在的Maven或Ivy仓库
支持传递依赖关系管理,而不需要远程存储库或pom.xml和ivy.xml文件
Ant任务和构建是一等公民
Groovy构建脚本
丰富的用于描述构建的域模型(Domain Specific Language)
Gradle wrapper允许用户在没有安装Gradle的机器上执行Gradle builds, 这对于CI非常有用
安装Gradle
Gradle依赖于Java JDK或JRE,版本>=7。Gradle本身包含了Groovy库,已安装的Groovy会被Gradle忽略。下面使用SDKMAN 进行安装:
# 安装sdkman
$ curl -s "https://get.sdkman.io" | bash
$ source " $HOME /.sdkman/bin/sdkman-init.sh"
# 安装groovy,这一步不需要
$ sdk install groovy
# 安装gradle,版本3.1
$ sdk install gradle 3.1
Gradle Command-Line
任务之间有依赖关系,如下图所示:
build.gradle文件内容如下:
task comile << {
println 'compiling source'
}
task compileTest ( dependsOn: compile ) << {
println 'compiling unit tests'
}
task test ( dependsOn: [ compile , compileTest ]) << {
println 'running unit tests'
}
task dist ( dependsOn: [ compile , test ]) << {
println 'building the distribution'
}
当执行gradle dist test
的时候,compile
只执行一次,执行gradle test test
,test
也只执行一次。
# Excluding tasks, 任务test不会执行
$ gradle dist -x test
# Continue the build when a failure occurs
# 如果一个任务的依赖都没有出错,那么--continue会保证该任务一定会被执行,如果它的依赖失败了,则该任务不会执行
$ gradle dist --continue
# 任务名简写,可以使用驼峰
# same as `gradle dist`
$ gradle di
# same as `gradle comTest`
$ gradle cT
# gradle默认会查找在当前文件夹下的构建文件,可以使用-b选项来选择另一个build file,这样setting.gradle不会被使用
# -q, --quite Log errors only
$ gradle -q -b subdir/myproject.gradle hello
# -p 选择项目目录
$ gradle -q -p subdir hello
# Forcing tasks to execute, 会让所有dist依赖的task强制执行
$ gradle --rerun-tasks dist
# Listing projects
$ gradle -q projects
# Listing tasks
$ gradle -q tasks
$ gradle -q tasks --all
# Show task usage detail
$ gradle -q help --task libs
# Listing project dependencies
$ gradle -q dependencies api:dependencies webapp:dependencies
# Listing project properties
$ gradle -q api:properties
Gradle建议所有的Gradle项目设置Wrapper
,它可以省去Gradle的安装并且保证使用正确的工具版本。每个Wrapper
都与一个特定的Gradle版本绑定,第一次执行./gradlew <task>
时,Wrapper会下载对应版本的Gradle(保存在~/.gradle/wrapper/dists/目录下)并使用它来进行构建。所以,使用./gradlew
替换上述命令的gradle
,效果是一样的。
Adding the Wrapper
to a Project
Wrapper应该使用Version Control来管理,这就意味着任何人都可以clone你的repo,进行构建却不用安装Gradle。
# 在项目根目录运行
$ gradle wrapper --gradle-version 3.1
:wrapper
BUILD SUCCESSFUL
Total time : 4.507 secs
该命令会生成以下文件:
.gradle
gradlew (Unix Shell script)
gradlew.bat (Windows batch file)
gradle/wrapper/gradle-wrapper.jar (Wrapper JAR)
gradle/wrapper/gradle-wrapper.properties (Wrapper properties)
.gradle
是一个临时文件夹,不同的构建环境会导致其下的内容会频繁变化,因此需要将它排除在代码管理工具中,即在.gitignore中添加.gradle。构建过程中生成的.class文件及Jar包等都会存放到各个项目的build
文件夹下,所以也需要将它写入.gitignore。
在Ant中,只能通过说明依赖jar包的绝对路径或相对路径,而Gradle只需要定义依赖包的名称,而由其它层来确定依赖包的具体位置。相同的特性Ant可以通过Apache Ivy来实现,但是Gradle做的更好。依赖本身也会有依赖的包,这叫做传递依赖transitive dependencies
,这对Gradle来说不成问题。Gradle还可以将构建的包上传到远程的Maven或Ivy仓库,这叫做Publishing
。
build.gradle文件
apply plugin: 'java'
apply plugin: 'maven'
// maven仓库
repositories {
// Use a Maven central repoitory
mavenCentral ()
// Use a remote maven repoitory
maven {
url 'https://repo.gradle.org/gradle/libs-releases'
}
}
// External dependencies
dependencies {
compile group: 'org.hibernate' , name: 'hibernate-core' , version: '3.6.7.Final'
testCompile group: 'junit' , name: 'junit' , version: '4.+'
deployerJars "org.apache.maven.wagon:wagon-ssh:2.2"
}
// [Interacting with Maven repositories][maven-repo]
UploadArchives {
repositories {
mavenDeployer {
configuration = configurations . deployerJars
repository ( url: "scp://repos.mycompany.com/releases" ) {
authentications ( userName: "me" , password: "myPassword" )
}
}
}
}
Gradle的依赖被分组到不同的configurations
。每个configuration
都是简单的依赖名字集合,用于定义项目的外部依赖。
Java Plugin定义了一组标准的configurations,这些configuration代表了Java plugin使用的classpaths。除了上述的compile
,testCompile
,常见的还有runtime
,testRuntime
。
一个multi-project build的结构通常都包含如下部分:
根文件夹中的setting.gradle
根文件夹中的build.gradle
各子项目文件夹包含自己的*.gradle
(有些multi-project builds会忽略子项目的构建脚本)
可以使用子项目的文件夹名命名gradle构建脚本,或者更简单的方式是将所有的子项目的构建文件统一命名为build.gradle
。
从这一节开始,是Gradle的重点
Gradle中的所有东西都基于两个基本的概念:projects
和tasks
,每一个Gradle项目由一个或多个project
构成。
定义task
task hello {
doLast {
println 'Hello world!'
}
}
# 使用shortcut定义
task hello << {
println 'Hello world!'
}
# enhanced tasks
tasks.create( name: 'hello' )
# print task name
println hello.name
println project.hello.name
println tasks.hello.name
println tasks['hello' ] .name
Some useful definition
// 定义默认任务
defaultTasks 'clean' , 'run'
task clean << {
println 'Default Cleaning!'
}
// gradle -q distribution
task distribution << {
println "We build the zip with version=$version"
}
// gradle -q release
task release ( dependsOn: 'distribution' ) << {
println 'We release now'
}
gradle . taskGraph . whenReady { taskGraph ->
if ( taskGraph . hasTask ( release )) {
version = '1.0'
} else {
version = '1.0-SNAPSHOT'
}
}
task taskX << {
println 'taskX'
}
task taskY << {
println 'taskY'
}
taskX . dependsOn taskY
taskX . dependsOn {
tasks . findAll { task -> task . name . startsWith ( 'libs' )}
}
task lib1 << {
println 'lib1'
}
task lib2 << {
println 'lib2'
}
// Ordering tasks
taskY . mustRunAfter taskX
taskY . shouldRunAfter taskX
// Multi-project
allprojects {
task hello << { task -> println "I'm $task.project.name" }
}
subprojects {
hello << { print "- I denpend on water" }
}
project ( ':bluewhale' ). hello << { task ->
println "I'am subproject $task.project.name"
}
Up-to-data checks (增量构建)
增量构建避免了对同一个task重复执行,一旦你的源码被编译过了,那么在没有编写改变影响输出的代码前,不应该重新编译。
定义incremental task action
build.gradle
class IncrementalReverseTask extends DefaultTask {
@InputDirectory
def File inputDir
@OutputDirectory
def File outputDir
@Input
def inputProperty
@TaskAction
void execute ( IncrementalTaskInputs inputs ) {
println inputs . incremental ? "CHANGE inputs considered out of data"
: "All inputs considered out of data"
if ( ! inputs . incremental )
project . delete ( outputDir . listFiles ())
inputs . outOfDate { change ->
println "out of data: ${change.file.name}"
def targetFile = new File ( outputDir , change . file . name )
targetFile . text = change . file . test . reverse ()
}
inputs . removed { change ->
println "removed: ${change.file.name}"
def targetFile = new File ( outputDir , change . file . name )
targetFile . delete ()
}
}
}
// An incremental task in action
task incrementalReverse ( type: IncrementalReverseTask ) {
inputDir = file ( 'inputs' )
outputDir = file ( "$buildDir/outputs" )
inputProperty = project . properties [ 'taskInputProperty' ] ?: "original"
}
Java Plugin
这个插件自动向项目中添加一些任务来对Java源码编译和单元测试,并将它打包成JAR文件。常用的命令有:
# build and the tasks
$ gradle build
:compileJava
:processResources
:classes
:jar
:assemble
:compileTestJava
:processTestResources
:testClasses
:test
:check
:build
$ gradle clean
$ gradle assemble
$ gradle check
在项目根目录下执行如下命令,亲测很强大,稍改一下就可以build了。
$gradle init
该命令会逐一解析存在的POM文件并生成Gradle构建文件以及包含muilt-project各项目信息的settings.gradle文件。
需要注意的是assemblies并不会自动转换,需要手动编写。这需要借助到Application Plugin 。
如下是我写的gradle构建文件,仅供参考。提醒一点,拷贝文件时的filter对@someParam@有效,而不是${someParam}
build.gradle
apply plugin: 'maven-publish'
apply plugin: 'application'
def deployUsername = hasProperty ( "MAVEN_USERNAME" ) ? MAVEN_USERNAME : "your_repo_username"
def deployPassword = hasProperty ( "MAVEN_PASSWORD" ) ? MAVEN_PASSWORD : "your_repo_password"
publishing {
// 上传位置
repositories {
maven {
url "$artifactRepoBase/${project.version.endsWith('-SNAPSHOT')? 'snapshots':'releases'}"
credentials {
username deployUsername
password deployPassword
}
}
}
// 发布
publications {
// 上传Jar包
//mavenJava(MavenPublication) {
// from components.java
//}
pubZip ( MavenPublication ) {
groupId 'com.startimestv.server.upms'
artifactId "upms-${env}"
version '3.11-SNAPSHOT'
artifact distZip
}
}
}
上传命令:./gradlew publish
Another Gradle project
https://github.com/zhjwpku/ijkplayer/tree/master/android/ijkplayer
gradle的构建我不想写了,自己看吧…
– Updated 2016-11-21 –
Build C/C++
Gradle and C/C++ Workshop Part 1
Gradle and C/C++ Workshop Part 2
Reference:
1 Gradle User Guide
2 Building and testing with Gradle
3 Gradle Beyond the Basics