gradle学习(23)-Sonar runner
最后更新于:2022-04-01 06:52:29
# 前言
上一篇文章学习了Sonar,这篇文章来讲解sonar runner。区别在于sonar runner是将分析的数据上传到数据库中,而sonar则是采用各种方法将数据库中数据组织成不同的形式展现给用户。所以sonar依赖于sonar runner,没有数据也就无法展示。
# 知识点
## 1.sonar的版本与兼容性
gradle默认使用的Sonar Runner版本是2.3的,该版本支持Sonar的版本3.0以及更高。但是如果你非要支持3.0以前的(还是不要有这种事发生吧,勇敢的拥抱新的东西不好么),那就需要自己配置了。
## 2.plugin标识
~~~
apply plugin : 'sonar-runner'
~~~
## 3.如何执行
~~~
gradle sonarRunner
~~~
## 4.Sonar Runner的配置
与sonar的配置项差不多,只是配置方式不一样而已,这里配置的都是以key/value的形式,而在sonar中是以特殊的属性名赋值的方式。
~~~
<pre name="code" class="java">sonarRunner {
sonarProperties {
property "sonar.host.url", "http://localhost:9002/"
property "sonar.jdbc.url", "jdbc:mysql://localhost:3306/sonar"
property "sonar.jdbc.driverClassName", "com.mysql.jdbc.Driver"
property "sonar.jdbc.username", "sonar"
property "sonar.jdbc.password", "sonar"
}
}
~~~
## 5.版本配置
在第1节中讲了sonar runner默认版本是2.3,当然你可以修改,通过下面语句修改。
~~~
sonarRunner {
toolVersion = '2.3' // default
}
~~~
## 6.多项目的构建
这里也和sonar类似,我就直接贴配置方式,不再阐述了。
有全局和私有2种配置,可以为每个子项目配置共同属性,也可以单独拿出来为每一个子项目配置特殊的属性。
为每个项目都设置字符编码为UTF-8
~~~
subprojects {
sonarRunner {
sonarProperties {
property "sonar.sourceEncoding", "UTF-8"
}
}
}
~~~
为project1设置语言为groovy
~~~
project(":project1") {
sonarRunner {
sonarProperties {
property "sonar.language", "grvy"
}
}
}
~~~
为project2设置跳过质量管理。
~~~
project(":project2") {
sonarRunner {
skipProject = true
}
}
~~~
## 7.自定义sourcesets
sonar也有这些配置,so一笔带过。(为啥都配置呢)
~~~
sonarRunner {
sonarProperties {
properties["sonar.sources"] += sourceSets.custom.allSource.srcDirs
properties["sonar.tests"] += sourceSets.integTest.allSource.srcDirs
}
}
~~~
## 8.不止是Java
same as Sonar,就是可以分析其他的语言。上面已经讲过了如何设置语言了。
## 9.控制执行sonar runner进程
sonar runner和test进程一样,也是一个单独的进程。你可以设置该进程的内存以及系统属性。想要查看都有哪些可以设置的属性,可以在api中查看JavaForkOptions类。
~~~
sonarRunner {
forkOptions {
maxHeapSize = '512m'
}
}
~~~
gradle学习(22)-Sonar
最后更新于:2022-04-01 06:52:27
# 前言
sonar相关的信息分2章,一部分是sonar,一部分是sonar runner,这篇学习的是sonar。
sonar插件依靠于sonar runner插件,且sonar的版本要在2.1.1以上。如果你要执行sonar任务,就在命令行下敲gradle sonarAnalyze。sonarAnalyze任务是独立存在的,就是该任务来分析代码,该任务不依靠其他任务。该任务不依靠源码文件,而是针对class文件和build文件,所以尽量在使用前进行全build。正常情况下,会一天产生一次报告。
# 知识点
## 1.sonar 插件
~~~
apply plugin: 'sonar'
~~~
## 2.配置sonar的服务器和数据库
在build.gradle添加一个sonar(api中的SonarRootModel)任务,可以配置上面的信息
build.gradle
~~~
sonar{
server{
url = "http://localhost:9002/"
}
database{
url = "jdbc:mysql://localhost:3306/sonar"
driverClassName = "com.mysql.jdbc.Driver"
username = "sonar"
password = "sonar"
}
}
~~~
server(api中的SonarServer)里的url代表你在浏览器中访问sonar服务器的地址。
database(api中的SonarDataBase)里代表的是数据库的信息,包括访问地址,驱动,用户名和密码。
这里面要根据你的实际属性来填。
## 3.配置sonar的项目属性
sonar中的project(api中SonarProject)用来设置如何分析代码。
跟上面一样,它也是在sonar任务中添加,如下所示:
build.gradle:
~~~
sonar{
server{
url = "http://localhost:9002/"
}
database{
url = "jdbc:mysql://localhost:3306/sonar"
driverClassName = "com.mysql.jdbc.Driver"
username = "sonar"
password = "sonar"
}
project {
coberturaReportPath = file("$buildDir/cobertura.xml")
}
}
~~~
## 4.多项目的分析
在多项目的构建中,你可以在root project项目中配置sonar,让其分析各个子项目,这样的速度会比你一个一个单独分析要快。sonar能够在页面上展现出各个项目的树形结构。sonar中的server和database只需要在根项目配置即可,无需单独在每个子项目配置一篇。可以通过subprojects任务达到这种效果。但是project的一些自定义的设置需要单独去配置。例如下面的代码
### 设置编码for每一个子项目
build.gradle
~~~
subprojects {
sonar {
project {
sourceEncoding = "UTF-8"
}
}
}
~~~
如果你想为某一个特定的子项目设置,可以通过gradle中project任务来设置,格式为project('子项目名'),例如下面的代码(也是在root project中build.gradle)
build.gradle:
### 特定子项目的配置
~~~
project(":project1") {
sonar {
project {
skip = true
}
}
}
~~~
上面代码的作用是在进行sonar分析时,不对project1这个项目进行分析。自然在web页面中的树形结构中也就不会显示该项目。
还有一个语言项,用来配置子项目的编程语言。但是该属性一个子项目只能有一个语言,不能配置多个语言。下面的例子是为project2设置语言为groovy。
build.gradle
~~~
project(":project2") {
sonar {
project {
language = "groovy"
}
}
}
~~~
当配置的项只有一项时,下面的语法更简洁:
~~~
project(":project2").sonar.project.language = "groovy"
~~~
## 5.分析自定义的Source Set
默认情况下,sonar只会分析项目中main和test这两个sourceset。你也可以将自己自定义的sourceSets添加到sonar分析的范围中,这样sonar就会一并分析了。
你可以通过下面的方式来将自己自定义的sourcesets追加到sonar分析的目录中:
build.gradle:
~~~
sonar.project {
sourceDirs += sourceSets.custom.allSource.srcDirs
testDirs += sourceSets.integTest.allSource.srcDirs
}
~~~
或者
~~~
sonar{
server{
url = "http://localhost:9002/"
}
database{
url = "jdbc:mysql://localhost:3306/sonar"
driverClassName = "com.mysql.jdbc.Driver"
username = "sonar"
password = "sonar"
}
project {
coberturaReportPath = file("$buildDir/cobertura.xml")
sourceDirs += sourceSets.custom.allSource.srcDirs
testDirs += sourceSets.integTest.allSource.srcDirs
}
}
~~~
其中custom和integTest是你自定义的sourceSets。
## 6.分析非java语言项目
第4点已经讲过了,你可以为每一个子项目设置不同的语言。
## 7.设置sonar自定义属性
sonar中属性设置都是以key-value的格式设置的,下面的知识就过一遍吧,对于新手来说,这些东西还太遥远。只需要记住有2中定义方法,一种是全局定义方式,包括根项目和子项目,还有一种范围小的,只适用于本项目。分别通过withGlobalProperties和withProjectProperties来定义
### 全局
build.gradle:
~~~
sonar.withGlobalProperties { props ->
props["some.global.property"] = "some value"
// non-String values are automatically converted to Strings
props["other.global.property"] = ["foo", "bar", "baz"]
}
~~~
### 私有
~~~
sonar.project.withProjectProperties { props ->
props["some.project.property"] = "some value"
// non-String values are automatically converted to Strings
props["other.project.property"] = ["foo", "bar", "baz"]
}
~~~
## 7.命令行的方式配置sonar
(还是不要这么干的好,多累啊)
可设置的属性如下:
server.url:服务器地址
database.url:数据库地址
database.driverClassName:驱动名
database.username:数据库用户名
database.password:数据库密码
showSql:打印sql语言
showSqlResults:打印结果
verbose:log级别
forceAnalysis:强制分析
## 8.执行方式
在命令行下敲下面语句就能执行sonar任务
~~~
gradle sonarAnalyze
~~~
# 实战
为什么没有实战例子,因为我在公司,你懂的。等晚上再加:
~~~
FAILURE: Build failed with an exception.
* What went wrong:
Could not resolve all dependencies for configuration ':testCompile'.
> Could not resolve junit:junit:4.+.
Required by:
:TestNG_gradl:1.0
> Failed to list versions for junit:junit.
> Unable to load Maven meta-data from https://repo1.maven.org/maven2/juni
/junit/maven-metadata.xml.
> Could not GET 'https://repo1.maven.org/maven2/junit/junit/maven-meta
ata.xml'.
> Connection to https://repo1.maven.org refused
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug
option to get more log output.
BUILD FAILED
Total time: 28.194 secs
~~~
gradle复习(8)-Task中行为
最后更新于:2022-04-01 06:52:24
## Behaviour
之前在学习的时候说到了可以给任务添加行为behaviour,看一下下面的例子:
~~~
task hello << {
println 'Hello Earth'
}
hello.doFirst {
println 'Hello Venus'
}
hello << {
println 'Hello Jupiter'
}
hello.doLast {
println 'Hello Mars'
}
~~~
输出如下:
~~~
D:\gradle_product\0123>gradle -q hello
Hello Venus
Hello Earth
Hello Jupiter
Hello Mars
~~~
doFirst和doLast这些标识就是行为,代表你所定义的hello任务的一些附加行为,不同等级的行为,跟你在build.gradle中定义的顺序是没关系的,但是同一等级的就有关系,谁先定义的,就会先执行那一部分。
## 不同等级
比如我们将doFirst和doLast的位置调换,看执行的顺序:
~~~
task hello << {
println 'Hello Earth'
}
hello << {
println 'Hello Jupiter'
}
hello.doLast {
println 'Hello Mars'
}
hello.doFirst {
println 'Hello Venus'
}
~~~
输出:
~~~
D:\gradle_product\0123>gradle -q hello
Hello Venus
Hello Earth
Hello Jupiter
Hello Mars
~~~
经过实际验证,输出是没有变化的。
## 相同等级
比如hello.doLast和hello<<所定义的行为属于相同等级,都是在hello任务执行完再执行。这个时候的执行顺序是跟你定义的顺序是有关系的,比如我们将上面的hello<<移到doLast后面
~~~
task hello << {
println 'Hello Earth'
}
hello.doLast {
println 'Hello Mars'
}
hello << {
println 'Hello Jupiter'
}
hello.doFirst {
println 'Hello Venus'
}
~~~
输出:
~~~
D:\gradle_product\0123>gradle -q hello
Hello Venus
Hello Earth
Hello Mars
Hello Jupiter
~~~
gradle复习(7)-深入Jacoco
最后更新于:2022-04-01 06:52:22
接着昨天的写,昨天讲到jacoco可以test任务中配置,形式如下:
~~~
test {
useTestNG();
jacoco{
......
}
}
~~~
之前的文章讲了append和destinaFile这两个属性,下面接着讲第三个属性。
## 3.classDumpFile
将jacoco检测到的class文件保存到的文件
build.gradle
~~~
test {
useTestNG();
jacoco{
append = false
destinationFile = file("$buildDir/doctor/doctorq.exec")
classDumpFile = file("$buildDir/doctor/class")
}
}
~~~
执行gradle clean test后,会发现多了一个build/doctor/class文件:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466cef065.jpg)
这里面的文件和exec文件里列举的一样。这个属性不太需要,过了!
## 不止是test任务
我们之前讲的所有关于jacoco的内容,都是收集test任务的代码覆盖率。但是jacoco不只是用于这样一个测试任务,由于jacoco的原理是在jvm环境中进行插桩监控jvm中被调用的代码,那么只要是在java进程中执行的任务,都可以用jacoco来收集信息。好,下面来举例说明:当我们执行java程序main方法时,也可以通过jacoco来收集main方法的代码覆盖率。
新增main方法:
~~~
public class MyMain {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Person person = new Person("Larry");
System.out.println(person.getName());
}
}
~~~
build.gradle中新增任务
~~~
jacoco{
toolVersion = "0.7.1.201405082137"
reportsDir = file("$buildDir/customJacocoReportDir")
applyTo run
}
task applicationCodeCoverageReport(type:JacocoReport){
executionData run
sourceSets sourceSets.main
}
~~~
我们执行run任务再执行applicationCodeCoverageReport任务,看结果输出
~~~
D:\eclipse\workspare\TestNG_gradl>gradle clean run applicationCodeCoverageRepor
:clean
:compileJava
:processResources
:classes
:run
Larry
:applicationCodeCoverageReport
BUILD SUCCESSFUL
Total time: 6.333 secs
~~~
然后看代码覆盖率的报告
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466d09d5a.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466d281d4.jpg)
上面的例子中我们测试了main方法代码覆盖率,还有很多其他的任务可以获得代码覆盖率信息,等待我以后了解了再添加。
gradle复习(6)-深入Jacoco
最后更新于:2022-04-01 06:52:20
## Jacoco
之前在[构建eclipse项目](http://blog.csdn.net/itfootball/article/details/42710653)中已经涉及了一点,这篇文章详细的讲讲。jacoco是什么,大家google或者百度一下都可以,我就不自己定义了。
需要注意的一点是Jacoco的插件目前还是孵化阶段,以后会有变动。
## gradle中的Jacoco
## 1.jacoco api
要想在gradle项目中使用jacoco。需要加入下面的plugin语句:
~~~
apply plugin: 'jacoco'
~~~
jacoco在实际上是一个JacocoPluginExtension类型的实例。该类型包括下面的属性和方法:
### 属性
~~~
reportsDir:报告的存放路径
toolVersion:jacoco的jar包版本,默认为0.7.1.201405082137
~~~
### 方法
~~~
applyTo(tasks):所用于所有的任务
applyTo(task):创建jacoco的插件扩展
~~~
## 2.jacoco任务
**在build.gradle脚本文件中加入jacoco任务的语法如下:**
~~~
jacoco{
toolVersion = "0.7.1.201405082137"
reportsDir = file("$buildDir/customJacocoReportDir")
}
~~~
**jacoco只有上面2个属性可以设置,上面的报告设置的目录为customJacocoReportDir,这个时候我们执行测试任务**
~~~
D:\eclipse\workspare\TestNG_gradl>gradle clean test
:clean
:compileJava
:processResources
:classes
:compileTestJava
:processTestResources
:testClasses
:test
BUILD SUCCESSFUL
Total time: 6.443 secs
~~~
**然后再执行jacoco任务:**
~~~
D:\eclipse\workspare\TestNG_gradl>gradle jacoco
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jacocoTestReport
BUILD SUCCESSFUL
Total time: 4.883 secs
~~~
**为什么要按照这个顺序呢?因为jacoco是依托测试任务的,测试任务执行完,才能有代码覆盖率的结果。如果你直接执行clean然后执行jacoco,它会跳过jacoco任务。**
~~~
D:\eclipse\workspare\TestNG_gradl>gradle clean jacoco
:clean
:compileJava
:processResources
:classes
:jacocoTestReport SKIPPED
BUILD SUCCESSFUL
Total time: 4.246 secs
~~~
**好了,现在我们来看一下结果:**
**![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466c72a78.jpg)**
**会发现多了一个customJacocoReportDir文件目录(这可不是我新建的,是自动生成的),打开里面的html文件,就可以看到结果报告的数据啦:**
**![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466c84072.jpg)**
**(我的测试case都是简单的输出case,所以覆盖率是0,真实的数据可不是这样的)**
## 3.jacocoTestReport任务
**该任务继承自Report任务,report任务里面分为csv、xml和html格式的测试报告。下面我们来了解了解:**
~~~
jacocoTestReport{
reports{
xml.enabled true
csv.enabled true
html.enabled true
}
}
~~~
**上面我们设置了3种格式的报告都存在。然后执行构建任务:**
~~~
D:\eclipse\workspare\TestNG_gradl>gradle clean test jacoco
:clean
:compileJava
:processResources
:classes
:compileTestJava
:processTestResources
:testClasses
:test
:jacocoTestReport
BUILD SUCCESSFUL
Total time: 8.72 secs
~~~
**结果会生成3种报告,如下图所示:**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466c98297.jpg)
**下面我们在jacocoTestReport任务中去改变jacoco结果文件的保存目录**
build.gradle
~~~
jacocoTestReport{
reports{
xml.enabled true
csv.enabled true
html.enabled true
html.destination "${buildDir}/jacocoHtml"
xml.destination "${buildDir}/jacocoXml.xml"
csv.destination "${buildDir}/jacocoCsv.csv"
}
}
~~~
**上面三句设置destination的语句形式是一样的,但是是有区别的。区别在于html的设置是到目录的,其他两个是到文件的。因为html格式的结果页面中操作有很多,而且结果页面也不止一个,所以需要用文件夹保存,必须写到目录。而另外两个就是直接保存到文件的。直接看执行后的结果:**
**![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466ca9b17.jpg)**
**由结果可以看出,jacoco任务中设定的目录没有生成,而是生成了jacocoTestReport任务中定义的文件目录。**
## 4.在Test任务中jacoco配置
我们在Test任务中对jacoco进行了扩展,可以对jacoco的一些特殊的属性配置。
### 1.destinationFile属性
官网解释说是修改已执行的数据要写入的文件。其实就是修改test.exec文件目录或文件名。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466cbb8b7.jpg)
好,来例子试试。
~~~
test {
useTestNG();
jacoco{
destinationFile = file("$buildDir/doctor/doctorq.exec")
}
}
~~~
构建后
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466ccb897.jpg)
### 2.append
append的意思是如果上面的destinationFile 指定的路径存在,那么通过append来配置是否在原有的文件上追加,或者不追加。
如果append = false,那么无论destinationFile文件存不存在,都会生成新文件,意思就是先将之前的删掉,然后生成一个新的。
如果append = true,如果之前的exec文件存在,不会删除它,而是在目录下再生成一个新的。下面看实际操作:
**首先我们重新构建一下**
~~~
D:\eclipse\workspare\TestNG_gradl>gradle clean test
:clean
:compileJava
:processResources
:classes
:compileTestJava
:processTestResources
:testClasses
:test
BUILD SUCCESSFUL
Total time: 7.645 secs
~~~
**构建后的文件跟上面是一样的,build/doctor/下有一个doctorq.exec文件。build.gradle文件如下:**
~~~
test {
useTestNG();
jacoco{
append = true
destinationFile = file("$buildDir/doctor/doctorq.exec")
}
}
~~~
**现在我们将build.gradle文件修改一下,将doctorq.exec的文件名改为doctorq1.exec。**
~~~
test {
useTestNG();
jacoco{
append = true
destinationFile = file("$buildDir/doctor/doctorq1.exec")
}
}
~~~
**这个时候我们不能执行clean任务,因为clean会把之前的build文件下全部删除,我们就不好去比较了,那么怎么在不clean的情况还能执行test的任务呢?如果不做任何操作,直接执行test,输出如下:**
~~~
D:\eclipse\workspare\TestNG_gradl>gradle test
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
BUILD SUCCESSFUL
~~~
**会发现,所有的任务都没有执行,因为没有检测到新的更新,gradle的后台守护程序自然不会再执行,这是gradle优化执行时间的策略。那么这个时候怎么办?**
**其实修改一下case的文件就行,我们删除其中一个case文件或者增加一个都行,然后再执行gradle test。这个时候你会发现在build/doctor/目录下多了一个doctorq1.exec文件**
**![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466cda39a.jpg)
**
但是如果你把build.gradle的append属性设置为false。不管你怎么操作,它都会生成新的,而不是追加。我就不具体贴例子了。
gradle复习(5)-Test remote debug
最后更新于:2022-04-01 06:52:18
## 一句话引发的"血案"
官方文档中关于test的信息有这么一句话:
~~~
The test process can be started in debug mode (see getDebug()) in an ad-hoc manner by supplying the `--debug-jvm` switch when invoking the build.
gradle someTestTask --debug-jvm
~~~
但是如果我们在命令行下敲这个命令,会出现如下信息:
~~~
D:\eclipse\workspare\TestNG_gradl>gradle clean test --debug-jvm
:clean
:compileJava
:processResources
:classes
:compileTestJava
:processTestResources
:testClasses
:test
Listening for transport dt_socket at address: 5005
> Building 87% > :test
~~~
光标会一直在这闪烁,到底有啥用呢?搞了一上午,找到了[一篇文章](http://www.cnblogs.com/warrior/p/3835240.html),恍然大悟。
这个--debug-jvm的参数会在执行测试的时候一直监听5005端口的信息,那么这个信息从哪里来?我慢慢道来。
## 2.以TestNG为例
我的测试任务代码如下,就是启动TestNG测试,然后在测试前输出一句话,测试case中的输出信息也要输出。
~~~
test {
useTestNG()
//systemProperty 'some.prop', 'value'
//exclude 'org/gradle/SimpleTest.class'
//reports.html.enabled = true
//scanForTestClasses = false
beforeTest { descriptor ->
logger.lifecycle("Running test: " + descriptor)
}
onOutput { descriptor, event ->
logger.lifecycle("Test: " + descriptor + " produced standard out/err: " + event.message )
}
}
~~~
这个时候我们在eclipse中debug模式启动我们的case。但是要注意一点不是直接Debug as->TestNG。具体配置如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466c0ddb7.jpg)
进入Debug Configurations界面
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466c36b94.jpg)
点New以后会自动添加你要测试的类,但是这个时候要注意一定要把端口改成你在命令行监听的端口号5005。如果命令行上是其他端口,只要跟其一样就行。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466c52271.jpg)
这个时候你点击Debug就会发现,命令行有输出了。
~~~
Running test: test method aFastTest(org.gradle.SimpleTest)
Test: test method aFastTest(org.gradle.SimpleTest) produced standard out/err: fa
st test
Running test: test method aSlowTest(org.gradle.SimpleTest)
Test: test method aSlowTest(org.gradle.SimpleTest) produced standard out/err: sl
ow test
Test: test 'Gradle test' produced standard out/err: tearDown
Test: test 'Gradle test' produced standard out/err: setUp
Running test: test method aFastTest1(org.gradle.SimpleTest1)
Test: test method aFastTest1(org.gradle.SimpleTest1) produced standard out/err:
fast test1
Running test: test method aSlowTest1(org.gradle.SimpleTest1)
> Building 87% > :test > 3 tests completed
~~~
而且还会等待debug的case结束。这就是debug模式.
gradle复习(4)-Cannot find System Java Compiler
最后更新于:2022-04-01 06:52:15
这个问题的产生原因是你没有在gradle配置中设置JDK。
## 解决方法一
1.首先在build.gradle右键点击,选择Run As中第二个选项[2.Gradle build....]进入gradle配置。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466b71ed9.jpg)
2.选择Arguments选项卡,选择Execution Environment选项,点Configure EEs进入选择JDK。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466bac341.jpg)
3.选择你环境中jdk,选择,然后Apply就行了。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466bc3a02.jpg)
4.现在再运行就没事了
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466bda31e.jpg)
## 解决方法二
新建一个gradle.properties文件,在里面添加javahome的属性,指定jdk的根目录
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466bf257f.jpg)
也可以解决问题。建议采用第二种方法。
[](http://blog.csdn.net/itfootball/article/details/42963987#)[](http://blog.csdn.net/itfootball/article/details/42963987# "分享到QQ空间")[](http://blog.csdn.net/itfootball/article/details/42963987# "分享到新浪微博")[](http://blog.csdn.net/itfootball/article/details/42963987# "分享到腾讯微博")[](http://blog.csdn.net/itfootball/article/details/42963987# "分享到人人网")[](http://blog.csdn.net/itfootball/article/details/42963987# "分享到微信")
gradle复习(3)-在gradle项目中使用TestNG
最后更新于:2022-04-01 06:52:13
## 1.gradle项目
在eclipse中创建的java项目,用gradle管理。我要复习的是关于在gradle中使用TestNG的知识。首先TestNG环境配置如[上一篇文章](http://blog.csdn.net/itfootball/article/details/42918445)。
测试目录下有2个java文件。一个是PersonTest.java,一个是SimpleTest。
PersonTest.java:用junit写的测试类。
SimpleTest:TestNG写的测试类。
~~~
public class PersonTest {
@Test
public void canConstructAPersonWithAName() {
Person person = new Person("Larry");
assertEquals("Larry", person.getName());
}
}
~~~
~~~
public class SimpleTest {
@BeforeClass
public void setUp() {
System.out.println("setUp");
}
@Test(groups = { "slow" })
public void aSlowTest() {
System.out.println("slow test");
}
@Test(groups = { "fast" })
public void aFastTest() {
System.out.println("fast test");
}
}
~~~
build.gradle原始脚本如下:
build.gradle
~~~
apply plugin: 'java'
apply plugin: 'eclipse'
sourceCompatibility = 1.7
version = '1.0'
jar {
manifest {
attributes 'Implementation-Title': 'Gradle Quickstart', 'Implementation-Version': version
}
}
repositories {
mavenCentral()
}
dependencies {
compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
compile 'org.testng:testng:6.8.17'
testCompile group: 'junit', name: 'junit', version: '4.+'
}
task copyJars(type:Copy){
from configurations.runtime
into 'libs'
}
uploadArchives {
repositories {
flatDir {
dirs 'repos'
}
}
}
~~~
这个时候执行命令,你会发现默认会执行JUnit的case。
~~~
D:\eclipse\workspare\TestNG_gradl>gradle build
:compileJava
:processResources
:classes
:jar
:assemble
:compileTestJava
:processTestResources
:testClasses
:test
:check
:build
BUILD SUCCESSFUL
Total time: 5.179 secs
~~~
报告如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466ad9476.jpg)
那么我要如何选择TestNG的呢?
## 2.执行TestNG测试
很简单,在build.gradle添加如下语句
~~~
test {
useTestNG()
}
~~~
先执行clean任务清空之前的build文件。重新build:
~~~
D:\eclipse\workspare\TestNG_gradl>gradle clean
:clean
BUILD SUCCESSFUL
Total time: 4.355 secs
D:\eclipse\workspare\TestNG_gradl>gradle build
:compileJava
:processResources
:classes
:jar
:assemble
:compileTestJava
:processTestResources
:testClasses
:test
:check
:build
BUILD SUCCESSFUL
Total time: 5.319 secs
~~~
你会发现,执行的case就是TestNG的case啦。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466aedfce.jpg)
就是这么简单。
## 3.TestNG配置
### 报告配置
默认情况下报告中html文件是存在的,我们可以选择不生成html文件。
build.gradle:
~~~
test {
useTestNG()
reports.html.enabled = false
}
~~~
这个时候重新构建的话就不再生成reports文件了。
如果我们想将报告copy到其他路径。注意这里是copy不是重定向。
~~~
task testReport(type:TestReport){
destinationDir = file("D:/gradle_product/0120")
reportOn test
}
~~~
这个时候clean在执行testReport:
~~~
D:\eclipse\workspare\TestNG_gradl>gradle clean testReport
:clean
:compileJava
:processResources
:classes
:compileTestJava
:processTestResources
:testClasses
:test
:testReport
BUILD SUCCESSFUL
Total time: 5.447 secs
~~~
这个时候你会在相应目录下找到报告相关的文件。
## 分组测试
选择某一个组的case进行测试
build.gradle:
~~~
test {
useTestNG{
includeGroups 'slow'
}
//reports.html.enabled = false
}
~~~
我们选择测试slow分组的case。重新构建查看报告如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466b07c26.jpg)
发现就执行了一条case,点进去
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466b1a3a9.jpg)
分组配置成功。
## 分class文件测试
我们上面是根据TestNG中分组来进行测试的,也有按照一个测试类文件进行测试,比如我们就像测试一个java文件里的测试项。为了区分,我们添加了一个SimpleTest1.java的TestNG的case类。
~~~
public class SimpleTest1 {
@BeforeClass
public void setUp() {
System.out.println("setUp");
}
@Test(groups = { "slow" })
public void aSlowTest1() {
System.out.println("slow test1");
}
@Test(groups = { "fast" })
public void aFastTest1() {
System.out.println("fast test1");
}
}
~~~
这个时候我们如果想测SimpleTest1有两种方法。
1.include包含被测类文件
build.gradle:
~~~
test {
useTestNG()
systemProperty 'some.prop', 'value'
include 'org/gradle/SimpleTest1.class'
}
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466b28bc0.jpg)
2.由于我们这里面只有2个文件,这样我们通过剔除SimpleTest类来达到测试SimpleTest1类的效果。
测试的结果是和上面的结果一样的,就不再贴图了。
## 两种过滤条件的结合
现在我们将分组条件和分class文件这两个条件一起使用,看看效果。
上面我们只测试了SimpleTest1.class中的全部方法,这个时候我们用分组来过滤一下,只测试SimpleTest1.class里的slow分组的方法。
build.gradle:
~~~
test {
useTestNG{
includeGroups 'slow'
}
systemProperty 'some.prop', 'value'
exclude 'org/gradle/SimpleTest.class'
}
~~~
这个时候我们重新构建,执行后你会发现只测试了aSlowTest1方法。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466b37edd.jpg)
当然过滤分组还可以通过excludeGroups ‘fast’和上面的效果是一样的,就不再举例了。
下面要尝试的是,当这两个条件组合在一起的时候没找到要测试的case会发生什么?
刚才我们测试了SimpleTest1.class中的slow分组的case,现在把添加改成slow1,因为slow1是没有的,所以应该不会在SimpleTest1.class找到相应的case,看这次的构建结果是啥样的。
build.gradle
~~~
test {
useTestNG{
includeGroups 'slow1'
}
systemProperty 'some.prop', 'value'
exclude 'org/gradle/SimpleTest.class'
}
~~~
结果文件如下,结果文件是有的,但是可以看到结果文件没有case的相关信息,说明没有执行任何case。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466b4fa87.jpg)
3.利用testng.xml进行配置
gradle项目中也可以使用testng.xml来进行测试case的配置
我们首先新建一个testng.xml文件:
~~~
<?xml version="1.0" encoding="UTF-8"?>
<suite name="doctorng">
<!--enabled="true"让测试生效,可根据情况开启或关闭某些测试-->
<test name="test" enabled="true">
<!--指定参数-->
<parameter name="accesskey" value="f0af8e412cef7e5058beeb6df2012e1e"/>
<!--指定测试包,注意加 .* -->
<packages>
<package name="org.gradle.*"/>
</packages>
<!--指定测试类-->
<classes>
<class name="org.gradle.SimpleTest"/>
<!--过滤测试类的方法-->
<class name="org.gradle.SimpleTest1">
<methods>
<include name="aFastTest" />
</methods>
</class>
</classes>
<!--指定测试分组-->
<groups>
<run>
<!--包含-->
<include name="fast"/>
<!--排除-->
<exclude name="slow"/>
</run>
</groups>
</test>
</suite>
~~~
然后在build.gradle中获得该文件中的内容:
~~~
test {
useTestNG{
suites(file('src/test/resources/org/gradle/testng.xml'));
}
beforeTest { descriptor ->
logger.lifecycle("Running test: " + descriptor)
}
onOutput { descriptor, event ->
logger.lifecycle("Test: " + descriptor + " produced standard out/err: " + event.message )
}
}
~~~
重点就是useTestNG里的闭包代码,这就把testng.xml包含进来了。
~~~
D:\eclipse\workspare\TestNG_gradl>gradle clean test
:clean
:compileJava
:processResources
:classes
:compileTestJava
:processTestResources
:testClasses
:test
Running test: test method aFastTest(org.gradle.SimpleTest)
Test: test method aFastTest(org.gradle.SimpleTest) produced standard out/err: fa
st test
Running test: test method aFastTest1(org.gradle.SimpleTest)
Test: test method aFastTest1(org.gradle.SimpleTest) produced standard out/err: f
ast test1
Running test: test method aFastTest2(org.gradle.SimpleTest)
Test: test method aFastTest2(org.gradle.SimpleTest) produced standard out/err: f
ast test2
Running test: test method aFastTest3(org.gradle.SimpleTest)
Test: test method aFastTest3(org.gradle.SimpleTest) produced standard out/err: f
ast test3
Running test: test method aFastTest1(org.gradle.SimpleTest1)
Test: test method aFastTest1(org.gradle.SimpleTest1) produced standard out/err:
fast test1
BUILD SUCCESSFUL
Total time: 5.741 secs
D:\eclipse\workspare\TestNG_gradl>
~~~
测试报告如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466b61549.jpg)
gradle复习(2)-eclipse中添加依赖jar包
最后更新于:2022-04-01 06:52:11
**我所说的依赖,是指编写代码的时候,你找不到包,就是没有导入到项目的Library中。例如下面的情况:**
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466a8d2ad.jpg)
我在做TestNG学习的时候,用了TestNG的jar包,build中脚本如下:
~~~
apply plugin: 'java'
apply plugin: 'eclipse'
sourceCompatibility = 1.7
version = '1.0'
jar {
manifest {
attributes 'Implementation-Title': 'Gradle Quickstart', 'Implementation-Version': version
}
}
repositories {
mavenCentral()
}
dependencies {
compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
compile 'org.testng:testng:6.8.17'
testCompile group: 'junit', name: 'junit', version: '4.+'
}
test {
systemProperties 'property': 'value'
}
uploadArchives {
repositories {
flatDir {
dirs 'repos'
}
}
}
~~~
这个时候怎么把jar包放到类似于下面的引用库中呢?这样才能导入jar包。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466a9f509.jpg)
## 解法1
有人说可以通过下面脚本任务:
~~~
task copyJars(type:Copy){
from configurations.runtime
into 'libs'
}
~~~
执行该任务后,会添加一个libs目录,并把我们的jar包都放到该目录下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466aabd5d.jpg)
但这并不能解决问题。
## 2.gradle eclipse命令
其实上面直接敲gradle eclipse就可以解决
~~~
D:\eclipse\workspare\TestNG_gradl>gradle eclipse
:eclipseClasspath
:eclipseJdt
:eclipseProject
:eclipse
BUILD SUCCESSFUL
Total time: 4.103 secs
~~~
回到eclipse可以看到发生了下面的变化
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466abae6c.jpg)
多了一个Referenced Libraries,然后错误也没了。原来是我小白啦,唉,汗颜啊
[](http://blog.csdn.net/itfootball/article/details/42918445#)[](http://blog.csdn.net/itfootball/article/details/42918445# "分享到QQ空间")[](http://blog.csdn.net/itfootball/article/details/42918445# "分享到新浪微博")[](http://blog.csdn.net/itfootball/article/details/42918445# "分享到腾讯微博")[](http://blog.csdn.net/itfootball/article/details/42918445# "分享到人人网")[](http://blog.csdn.net/itfootball/article/details/42918445# "分享到微信")
gradle复习(1)-2种定义任务方式的区别
最后更新于:2022-04-01 06:52:08
## 之前的教程中,task定义的方式有2种:
~~~
task hello<<{
println 'Hello'
}
task world{
println 'Hello'
}
~~~
一种带方向键<<,一种不带。这两者到底有什么区别:
~~~
qianhuis-Mac-mini:0115 qianhui$ gradle hello
World
:hello
Hello
BUILD SUCCESSFUL
Total time: 2.526 secs
qianhuis-Mac-mini:0115 qianhui$ gradle world
World
:world UP-TO-DATE
BUILD SUCCESSFUL
Total time: 2.559 secs
~~~
从执行的结果可以看出,没有用<<定义的任务,默认情况下会先在当前任务执行前执行,有点像默认任务。
是否系统级任务都是这么定义的呢?来看一个默认任务的实例:
~~~
defaultTasks 'clean', 'run'
task clean << {
println 'Default Cleaning!'
}
task run << {
println 'Default Running!'
}
task other{
println "I'm not a default task!"
}
~~~
执行后的输出
~~~
D:\gradle_product\0123>gradle -q
I'm not a default task!
Default Cleaning!
Default Running!
~~~
可以看出不带
gradle学习(21)-在eclipse中构建java项目
最后更新于:2022-04-01 06:52:06
## 1.下载gradle for eclipse插件
根据[前人文章](http://blog.csdn.net/caolaosanahnu/article/details/17022321)找到的链接下载的,就不多说了。下载完成后,重启eclipse。
## 2.创建gradle项目
创建新项目的时候会发现多了一个gradle选项:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e46691c45c.jpg)
选择Gradle Project后会等待一段时间,等创建完成后会生一个gradle项目,看一下目录结构:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e4669369c0.jpg)
这些项目的目录结构其实都在之前的学习中多少有些涉及,这里就不再过多的废话了。直接看build.gralde脚本里都写了啥吧。
~~~
//java插件
apply plugin: 'java'
//eclipse插件
apply plugin: 'eclipse'
//指定JDK版本,改成你系统中版本
sourceCompatibility = 1.5
//版本号
version = '1.0'
//jar包配置文件的一些属性设置
jar {
manifest {
attributes 'Implementation-Title': 'Gradle Quickstart', 'Implementation-Version': version
}
}
//jar来源定义为Maven的中央库
repositories {
mavenCentral()
}
//依赖
dependencies {
compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
testCompile group: 'junit', name: 'junit', version: '4.+'
}
~~~
## 3.test任务
这个时候我们写一个任务来执行单元测试,这个时候有可能报JDK错误,[解决方法。](http://forums.gradle.org/gradle/topics/java_quickstart_problems)
由于eclipse执行gradle的方式让醉了,所以我还是选择命令行吧:
~~~
D:\eclipse\workspare\gradle_hello>gradle test
:compileJava
:processResources
:classes
:compileTestJava
:processTestResources
:testClasses
:test
BUILD SUCCESSFUL
Total time: 5.07 secs
~~~
然后我们看一下项目目录下的结构:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466946d0b.jpg)
生成了build目录,里面有classes、dependency-cache、resources、test-results、reports。主要来看一下我们生成的测试报告:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466a58b87.jpg)
## 4.加入JaCoco
想测试一下单元测试的代码覆盖率,在代码中添加如下内容:
~~~
//添加JaCoCo
apply plugin: 'jacoco'
jacoco{
toolVersion = "0.7.1.201405082137"
reportsDir = file("$buildDir/customJacocoReportDir")
}
jacocoTestReport {
reports {
xml.enabled false
csv.enabled false
html.destination "${buildDir}/jacocoHtml"
}
}
~~~
然后执行jacoco任务:
~~~
D:\eclipse\workspare\gradle_hello>gradle jacoco
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jacocoTestReport
Download https://repo1.maven.org/maven2/org/jacoco/org.jacoco.ant/0.7.1.20140508
2137/org.jacoco.ant-0.7.1.201405082137.pom
Download https://repo1.maven.org/maven2/org/jacoco/org.jacoco.core/0.7.1.2014050
82137/org.jacoco.core-0.7.1.201405082137.pom
Download https://repo1.maven.org/maven2/org/jacoco/org.jacoco.report/0.7.1.20140
5082137/org.jacoco.report-0.7.1.201405082137.pom
Download https://repo1.maven.org/maven2/org/ow2/asm/asm-debug-all/5.0.1/asm-debu
g-all-5.0.1.pom
Download https://repo1.maven.org/maven2/org/ow2/asm/asm-parent/5.0.1/asm-parent-
5.0.1.pom
Download https://repo1.maven.org/maven2/org/ow2/ow2/1.3/ow2-1.3.pom
Download https://repo1.maven.org/maven2/org/jacoco/org.jacoco.ant/0.7.1.20140508
2137/org.jacoco.ant-0.7.1.201405082137.jar
Download https://repo1.maven.org/maven2/org/jacoco/org.jacoco.core/0.7.1.2014050
82137/org.jacoco.core-0.7.1.201405082137.jar
Download https://repo1.maven.org/maven2/org/jacoco/org.jacoco.report/0.7.1.20140
5082137/org.jacoco.report-0.7.1.201405082137.jar
Download https://repo1.maven.org/maven2/org/ow2/asm/asm-debug-all/5.0.1/asm-debu
g-all-5.0.1.jar
BUILD SUCCESSFUL
Total time: 2 mins 5.018 secs
~~~
在build目录中会生成jacoco的目录报告:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466a69be6.jpg)
打开html文件显示如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466a79d3e.jpg)
这样我们就包含了jacoco的插件啦。
[](http://blog.csdn.net/itfootball/article/details/42710653#)[](http://blog.csdn.net/itfootball/article/details/42710653# "分享到QQ空间")[](http://blog.csdn.net/itfootball/article/details/42710653# "分享到新浪微博")[](http://blog.csdn.net/itfootball/article/details/42710653# "分享到腾讯微博")[](http://blog.csdn.net/itfootball/article/details/42710653# "分享到人人网")[](http://blog.csdn.net/itfootball/article/details/42710653# "分享到微信")
gradle学习(20)-详解java插件
最后更新于:2022-04-01 06:52:04
## 1.使用
在build.gradle中添加以下语句,表示插入java插件
~~~
apply plugin:'java'
~~~
## 2.source sets
java插件引入了sourceset这个概念,sourceset将编译时和执行时所要用到的source文件组合在一起,其中包含java的源文件和资源文件,有的插件还包括了groovy和Scala资源文件,sourceset与编译环境和运行环境都存在联系。
使用sourceset的目的是将一些源文件组合起来,为了某个特殊的目的在逻辑上进行分组。例如,你可能把测试的集合单独拿出来组合成一个sourceset,或者一些API啊,你项目中的实现类啊等等,都可以定义为一个sourceset。这只是个概念的问题,你知道知道你分组的意义:方便你管理文件。
java中定义了2个sourceset:一个是main group,还有一个是test group。
main:项目中的源文件,编译后组装到jar包中的。
test:项目的测试源码,例如JUint和TestNG写的测试代码。
## 3.任务
#### java plugin任务
**compileJava**:目的是编译java源文件,利用javac。依赖compile任务,属于JavaCompile类型。
~~~
apply plugin: 'java'
compileJava {
//enable compilation in a separate daemon process
options.fork = true
//enable incremental compilation
options.incremental = true
}
~~~
**processResource**:将项目的资源文件复制到项目class目录中。无依赖。属于Copy类型,严格来说是Copy的子类。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e4668005cc.jpg)
**classes** :组装class目录。依赖compileJava和processResource两个任务,还有一些编译的任务 。属于Task类型。
**compileTestJava**:编译测试源码,利用javac。依赖compile和一些产生测试编译环境的任务。属于JavaCompile类型。
**processTestResource**:将测试资源文件复制到项目class文件目录中。无依赖。属于Copy类型。
**testClasses**:组合测试的class目录。依赖compileTestJava和processTestResource和一些添加测试的编译任务。属于Task类型。
**jar**:组合成jar文件,依赖compile。属于Jar类型。
**javadoc**:生成java帮助文档,依赖compile。属于Javadoc类型。
**test**:执行测试case,依赖compile,compileTest等。属于Test类型
**uploadArchives**:上传存档文件。依赖那些在archives配置中产生镜像的任务,比如jar。属于UpLoad类型。
**clean**:删除build文件,使项目回归最原始状态.属于Delete类型。
**cleanTaskName**:删除由任务产生的文件,比如cleanJar就是删除有任务jar产生的文件,cleanTest就是删除由test产生的文件。属于Delete类型。
#### 专门处理sourceset的任务
**`compile<span class="replaceable">`SourceSet`</span>Java`**:利用javac编译sourceset定义的源文件,依赖所有产生sourceset编译类路径的任务。属于JavaCompile类型
**`process<span class="replaceable">`SourceSet`</span>Resources`**:将sourceset定义的资源文件复制到class目录中,无依赖,属于Copy类型。
**`<span class="replaceable">`sourceSet`</span>Classes`**:组合sourceset中定义的文件目录,依赖compileSourceSetJava和processSourceSetResources两个任务,属于Task类型。
#### 生命周期类任务
**assemble:**组合分析所有的档案文件。依赖所有的存档文件。属于Task类型。
**check:**执行所有的验证类任务,依赖所有验证类任务,包括test。属于Task类型。
**build:**执行构建,依赖check和assemble,属于Task类型。
**buildNeeded:**执行构建,依赖testCompile配置的子项目中build和buildNeeded任务,属于Task类型。
**buildDependents**:执行构建,依赖当前项目的子项目的build和buildDependents任务。属于Task类型。
**buildConfigName:**为特殊的配置类任务构建一个镜像,该任务都是隐式添加的。依赖configName代表的任务。属于Task类型。
**uploadConfigName**:为某一个配置任务分配镜像,然后上传镜像,该任务都是隐式添加的。依赖configName定义的任务,属于UpLoad类型。
#### 任务依赖关系图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466815a49.jpg)
## 4.项目布局
java plugin规定一些项目目录的结构,但是下面的目录并不是要求必须存在。java在编译的时候,会访问下面所有的目录,如果不存在,会记住这些没有的目录。
**src/main/java**:java源文件
**src/main/resources**:项目资源文件
**src/test/java**:测试源文件
**src/test/resources**:测试资源文件
**src/sourceSet/java**:sourceset定义的java源文件,注意其中的sourceSet一级是根据sourceSet定义的目录名替换的。下同
**src/sourceSet/resourcs**:sourceset定义的资源文件
#### 修改上面默认的目录
通过下面的方式可以修改默认的目录结构,定义你自己想要的目录
~~~
sourceSets{
main{
java{
srcDir 'src/java'
}
resources{
srcDir 'src/resources'
}
}
}
~~~
这是sourceSets很重要的应用。
## 5.依赖管理
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e46682e1c2.jpg)
上面的图是从文档上截取下来的,它列举了任务之间的依赖关系,比如第一个compile是被compileJava所依赖的,即执行compile前,会先执行compile。所以要理解Name列是被依赖的任务,一般是全局任务,不是java插件特有的,而Used by tasks一般是plugin特有的。Extends则说明继承的任务,说明该任务是在被继承任务的基础上扩展的。
再来看另外一张图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e4668483ef.jpg)
上面的图更加的清晰展现出java任务和gradle任务之间的联系,浅蓝色的代表java任务,绿色代表gradle任务。用used by标注的代表箭头指向的任务使用了左边的任务。
而uploadArchives和archives之前的关系是uploads,说明uploadArchives依赖于archives任务,等archives任务生成存档文件后,uploadArchives就会把这些存档文件上传。
而jar 和archives、runtime之间的关系是,在后者执行的时候,jar任务会将定义的jar包添加其中。那么这个依赖关系到底怎么算,是说jar任务依赖runtime,还是runtime依赖jar呢,应该是jar依赖runtime。因为runtime其实不管你有没有jar任务,如果有才会将jar任务定义的东西执行,如果没有,也是不会影响它。但是jar任务就不同了,没有runtime,jar还有用么?
下面再来看看谁能sourceSet任务的依赖配置,于java plugin正常任务的图是一样的,就不多解释了。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e46685f19a.jpg)
## 6.常用属性
#### 目录属性
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e466871466.jpg)
目录属性都是成双成对出现,目录名+目录。目录名是一个文件夹的名称,相对于build目录,而目录则是加上build目录。一些默认值上面都列举出来,不想浪费时间一一列举了。
#### 其他属性
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e4668947d5.jpg)
比较重要的是sourceSets,包含了项目中定义的sourcesets,下面一节会详细介绍。archivesBaseName项目压缩包的名称。manifest操作MANIFEST文件的。
## 7.操作source set
(终于到了能写点代码的时候了)
#### 获取sourceset属性
~~~
apply plugin:'java'
println sourceSets.main.output.classesDir
println sourceSets['main'].output.classesDir
sourceSets{
println main.output.classesDir
}
sourceSets{
main{
println output.classesDir
}
}
sourceSets.all{
println name
}
~~~
执行gradle命令后输出:
~~~
D:\GRADLE~2\0112>gradle -q
D:\gradle_product\0112\build\classes\main
D:\gradle_product\0112\build\classes\main
D:\gradle_product\0112\build\classes\main
D:\gradle_product\0112\build\classes\main
main
test
Welcome to Gradle 2.2.1.
To run a build, run gradle <task> ...
To see a list of available tasks, run gradle tasks
To see a list of command-line options, run gradle --help
~~~
说明想要获得sourcesets中属性值的方法有很多种,上面的程序中就列举了4种。
#### 配置sourceset属性
~~~
apply plugin:'java'
sourceSets{
main{
java{
srcDir 'src/java'
}
resources{
srcDir 'src/resources'
}
}
}
println sourceSets.main.java.srcDirs
println sourceSets['main'].resources.srcDirs
sourceSets{
println main.java.srcDirs
}
sourceSets{
main{
println java.srcDirs
}
}
sourceSets.all{
println name
}
~~~
根据实际操作来看,我们只是添加了一个java目录和一个resources目录。
~~~
D:\GRADLE~2\0112>gradle -q
[D:\gradle_product\0112\src\main\java, D:\gradle_product\0112\src\java]
[D:\gradle_product\0112\src\main\resources, D:\gradle_product\0112\src\resources
]
[D:\gradle_product\0112\src\main\java, D:\gradle_product\0112\src\java]
[D:\gradle_product\0112\src\main\java, D:\gradle_product\0112\src\java]
main
test
Welcome to Gradle 2.2.1.
To run a build, run gradle <task> ...
To see a list of available tasks, run gradle tasks
To see a list of command-line options, run gradle --help
~~~
#### 设置sourcesets属性
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e4668abfc5.jpg)
上面的表格列举了一些sourceset重要的属性,其中的一些属性在之前的2个例子中也有涉及。
#### 定义新的sourceset属性
~~~
apply plugin:'java'
sourceSets{
intTest
}
dependencies {
intTestCompile 'junit:junit:4.11'
intTestRuntime 'org.ow2.asm:asm-all:4.0'
}
~~~
在上面的代码中,我们定义了一个新的属性,叫做intTest,然后定义了2个依赖任务intTestCompile和intTestRunTime。这个我们在第5节.依赖管理中讲过,sourceset的依赖管理中可以根据属性值设置任务,刚好可以回过头复习复习。执行下任务:
~~~
D:\GRADLE~2\0112>gradle intTestClasses
:compileIntTestJava UP-TO-DATE
:processIntTestResources UP-TO-DATE
:intTestClasses UP-TO-DATE
BUILD SUCCESSFUL
Total time: 2.699 secs
~~~
#### sourceset相关样例
~~~
apply plugin:'java'
sourceSets{
intTest
}
dependencies {
intTestCompile 'junit:junit:4.11'
intTestRuntime 'org.ow2.asm:asm-all:4.0'
}
//将intTest输出文件打成jar包
task intTestJar(type:Jar){
from sourceSets.intTest.output
}
//为intTest中所有的java文件生成java帮助文档
task intTestJavadoc(type:Javadoc){
source sourceSets.intTest.allJava
}
//为intTest添加测试
task intTest(type:Test){
testClassesDir = sourceSets.intTest.output.classesDir
classpath = sourceSets.intTest.runtimeClasspath
}
~~~
首先我们执行intTest任务:
~~~
D:\GRADLE~2\0112>gradle intTestJar
:compileIntTestJava UP-TO-DATE
:processIntTestResources UP-TO-DATE
:intTestClasses UP-TO-DATE
:intTestJar
BUILD SUCCESSFUL
Total time: 4.337 secs
~~~
会在build/libs目录下生成jar包
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e4668ca5e0.jpg)
然后我们执行intTestJavadoc来生成java帮助文档:
~~~
D:\GRADLE~2\0112>gradle intTestJavadoc
:intTestJavadoc UP-TO-DATE
BUILD SUCCESSFUL
Total time: 2.73 secs
~~~
执行成功,但是不会生成javadoc,因为我没有定义intTest具体细节
最后来执行测试
~~~
D:\GRADLE~2\0112>gradle intTest
:compileIntTestJava UP-TO-DATE
:processIntTestResources UP-TO-DATE
:intTestClasses UP-TO-DATE
:intTest UP-TO-DATE
BUILD SUCCESSFUL
Total time: 2.793 secs
~~~
## 8.Javadoc
任务javadoc是Javadoc类的一个实例,支持核心java文档选项和标准[doclet](http://unmi.cc/javadoc-customize-doclet/)格式,为了完整的继承这些特性,gradle定义了2个类:CoreJavadocOptions和StandardJavadocDocletOptions。详细信息也可以去这两个类中去查查。
javadoc中的一些属性:
**classpath**:执行环境,sourceSets.main.output代表的目录,以及sourceSets.main.compileClasspath代表的目录。是FileCollection的
**source**:源码文件目录,sourceSets.main.allJava代表的目录,是FileTree类型的。
**destinationDir**:生成的文档存放目录
**title**:项目的版本和名称
## 9.clean
该任务是Delete的一个实例,删除用dir描述的目录
## 10.Resources
是sourceset中的属性,一般是通过Copy处理资源相关信息。会被ProcessResources用到:
ProcessResources.srcDirs引用的是sourceSet.resources的值。
ProcessResources.destinationDir引用的是sourceSet.output.resourcesDir的值。
## 11.CompileJava
java plugin为项目中的每一个sourceset都提供一个CompileJava对象实例。在脚本中是下面的样式
~~~
apply plugin: 'java'
compileJava {
//enable compilation in a separate daemon process
options.fork = true
//enable incremental compilation
options.incremental = true
}
~~~
**classpath**:FileCollction,默认值是sourceSet.compileClasspath
**source**:FileTree,默认值是sourceSet.java
**destinationDir**:File.默认值是sourceSet.output.classesDir
## 12.增量式的java编译
该功能正在孵化中,以后有可能更改。
该功能的主要目的是
1.避免在编译了并不需要编译的源文件,提高编译的速度。
2.可能只是改变一点的输出,并不需要重新编译一些没有任何改变的目录。这一点对[JRebel](http://baike.baidu.com/link?url=JjWdQDfooN_XFDMP5Kd23QJX_T7qrzL-dKzc4OXp5aagOHyG0vUW1ofaf4GDOQvqj9OQ7qeroVAUDcsP4Jikb_)很有用。
这个功能肯定是一个高级且难以理解的东西,因为它涉及到底层的算法。所以我想我是不可能弄明白的了,且对我有啥用。只是知道有这么个东西就行。
## 13.Test
test任务是一个Test实例,它会自动识别和执行source set定义test目录下所有的单元测试,而且会生成测试报告(这个好像挺爽的啊,看来测试驱动开发看能能很容易的实现啦)。
支持JUnit和TestNG。
#### 测试执行
测试的执行是独立于JVM的,和构建的主进程也是分离的,其中关于jvm和运行时状态属性是可以通过API进行修改的。
1.你可以指定是否平行的执行测试。
2.你可以指定执行一定数量的case后重启执行进程。
3.设置case失败后的行为。
4.设置测试输出的log的等级。
#### 调试
可以通过Test.getDebug属性来让jvm进入5005端口,进入调试模式。而且你可以通过命令行模式参数--debug -jvm进入debug模式。
#### 测试过滤
从gradle1.0开始,就具备了执行某些特殊的case,或者根据匹配模式删选case来执行。可以做到以下几点过滤:
1.依据测试方法的等级过滤,执行一个单一的测试。
2.依据自定义的注解(以后实现)
3.依据测试的层次,执行继承某一个基础类的测试(以后实现)
4.依据一些运行时的规则,例如一些特殊的系统属性值或静态状态(以后实现)
说了这么多废话,如果在脚本中定义过滤条件呢?
~~~
test {
filter{
//方法名
includeTestsMatching "*UiCheck"
//包名过滤
includeTestsMatching "org.gradle.internal.*"
//整合的case
includeTestsMatching "*IntegTest"
}
}
~~~
也可以通过gradle命令行的参数来过滤:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e4668e0699.jpg)
#### 根据系统属性来执行单一测试
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e4668ee08a.jpg)
#### 测试检测
gradle可以自动识别出哪些class是测试类,如果做到的呢,在编译阶段,gradle观察这些编译的java类。而扫描哪些文件夹里的类,你可以去设置,包括哪些文件,不包括哪些文件,都可以自定义。而且它也会扫描出用JUnit和TestNG框架写的case。以JUnit为例来看看。
当使用JUnit的时候,我们会扫描junit3和junit4写的case。
1.类或超类继承与TestCase或者GroovyTestCase。
2.类或超类使用了@RunWith。
3.类或超类包含@Test注解的方法。
当使用TestNG的话,我们只扫描@Test注解的方法。
#### 测试分组
junit分组
~~~
test{
useJUnit{
includeCategories 'org.gradle.junit.CategoryA'
includeCategories 'org.gradle.junit.CategoryB'
}
}
~~~
TestNG分组
~~~
test{
useJUnit{
includeCategories 'org.gradle.junit.CategoryA'
includeCategories 'org.gradle.junit.CategoryB'
}
useTestNG{
excludeGroups 'integrationTests'
includeGroups 'unitTests'
}
}
~~~
#### 测试报告
gradle默认情况下生成下面3种报告:
1.HTML格式
2.XML格式
3.二进制
可以使用任务testReports来生成测试报告:
~~~
subprojects{
apply plugin:'java'
test{
reports.html.enabled = false
}
}
task testReport(type:TestReport){
destinationDir = file("$buildDir/report/allTest")
reportOn subprojects*.test
}
~~~
## 14.Jar任务
jar任务会将项目的源文件和资源文件打成jar包。
jar中manifest属性,定义了jar包的版本和名称。文件位于tmp//下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e46690818a.jpg)
gradle学习(19)-log系统
最后更新于:2022-04-01 06:52:02
## 1.log信息的分类
除了常用的 debug,info,warning,error ,gradle自己特有的quiet和lifecycle。
## 2.选择log等级
-q/--quiet
-i//--info
-d/--debug
引申出
-s/--stacktrace
-S/--full-stacktrace
## 3.编写自己的log信息
println 后跟信息,gradle会将其重定向到日志系统中,默认为quiet等级。
当然你可以使用logger属性来编写不同等级的,继承与slf4j logger接口,然后加上一些gradle自己的定义。
~~~
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.')
~~~
~~~
qianhuis-Mac-mini:0112 qianhui$ gradle
An info log message which is always logged.
An error log message.
A warning log message.
A lifecycle info log message.
:help
Welcome to Gradle 2.2.1.
To run a build, run gradle <task> ...
To see a list of available tasks, run gradle tasks
To see a list of command-line options, run gradle --help
BUILD SUCCESSFUL
Total time: 2.265 secs
~~~
## 4.SLF4J日志
~~~
import org.slf4j.Logger
import org.slf4j.LoggerFactory
Logger slf4jLogger = LoggerFactory.getLogger('some-logger')
slf4jLogger.info('An info log message logged using SLF4j')
~~~
## 5.外部工具和库的logging
因为gradle是基于ant喝ivy的,所以前辈的logging输出应该被考虑,gradle会把它们的log重定向到gradle的log系统中,对于gradle有的等级,可以很好
的重定向,而对于gradle没有的,比如ant和ivy的trace等级,gradle会将其映射为debug等级,说明gradle默认情况智慧输出ant/ivy 的error和warning信息。
对于一些使用标准输出的工具,默认情况下,gradle重定向为quiet等级,标准的错误会被定义为error等级,但是这都是可以配置的。project对象提供一个LoggingManager,
该对象允许你去改变默认的重定向log等级。
~~~
logging.captureStandardOutput LogLevel.INFO
println 'A message which is logged at info level'
~~~
将log等级定位成info等级,执行命令后,如果加-q后会无法显示:
~~~
qianhuis-Mac-mini:0112 qianhui$ gradle -q
Welcome to Gradle 2.2.1.
To run a build, run gradle <task> ...
To see a list of available tasks, run gradle tasks
To see a list of command-line options, run gradle --help
~~~
## 6.为任务定义个log等级
~~~
println 'A message which is logged at info level'
task logInfo {
logging.captureStandardOutput LogLevel.INFO
doFirst {
println 'A task message which is logged at INFO level'
}
}
println 'end'
~~~
执行命令后会发现,任务中的log被隐藏掉了,因为任务中定义了log的等级为info,小于quiet等级,所以没有输出。说明任务内定义log等级只在任务内有效。
~~~
qianhuis-Mac-mini:0112 qianhui$ gradle -q logInfo
A message which is logged at info level
end
~~~
## 7.改变gradle日志
使用Gradle.useLogger()方法,具体做法是通过API中的接口重新定义log器。
~~~
useLogger(new CustomEventLogger())
class CustomEventLogger extends BuildAdapter implements TaskExecutionListener {
public void beforeExecute(Task task) {
println "[$task.name]"
}
public void afterExecute(Task task, TaskState state) {
println()
}
public void buildFinished(BuildResult result) {
println 'build completed'
if (result.failure != null) {
} }
~~~
但是上面的程序暂时还无法运行成功,暂时不太清楚为什么,以后再找答案。
要想自定义log器,可以研究下面几个接口:
BuildListener
ProjectEvaluationListener
TaskExecutionGraphListener
TaskExecutionListener
TaskActionListener
gradle学习(18)-ant的属性
最后更新于:2022-04-01 06:51:59
## 1.设置ant属性
build.xml:
~~~
<project>
<target name="hello">
<echo>buildDir=${buildDir}</echo>
</target>
</project>
~~~
我们要给buildDir属性设值
~~~
ant.importBuild ('build.xml'){
antTargetName ->'a-'+antTargetName
}
task intro << {
println 'Hello,from gradle'
}
ant.buildDir = buildDir
ant.properties.buildDir = buildDir
ant.properties['buildDir'] = buildDir
ant.property(name:'buildDir',location:buildDir)
~~~
## 2.获取ant属性值
build.xml中定义属性:
~~~
<project>
<property name="antProp" value="a property defined in an Ant build"/>
<target name="hello">
<echo>buildDir=${buildDir}</echo>
</target>
</project>
~~~
gradle.xml获取属性
~~~
ant.importBuild ('build.xml'){
antTargetName ->'a-'+antTargetName
}
task intro << {
println 'Hello,from gradle'
}
ant.buildDir = buildDir
ant.properties.buildDir = buildDir
ant.properties['buildDir'] = buildDir
ant.property(name:'buildDir',location:buildDir)
println ant.antProp
~~~
执行gradle命令,输出如下:
~~~
qianhuis-Mac-mini:0112 qianhui$ gradle intro
a property defined in an Ant build
:intro
Hello,from gradle
BUILD SUCCESSFUL
Total time: 3.253 secs
~~~
## 3.设置reference的值
build.xml
~~~
<project>
<property name="antProp" value="a property defined in an Ant build"/>
<path refid="classpath"/>
<target name="hello">
<echo>buildDir=${buildDir}</echo>
</target>
</project>
~~~
最后三行是设置代码
~~~
ant.importBuild ('build.xml'){
antTargetName ->'a-'+antTargetName
}
task intro << {
println 'Hello,from gradle'
}
ant.buildDir = buildDir
ant.properties.buildDir = buildDir
ant.properties['buildDir'] = buildDir
ant.property(name:'buildDir',location:buildDir)
println ant.antProp
ant.path(id:'classpath',location:'libs')
ant.references.classpath = ant.path(location:'libs')
ant.references['classpath'] = ant.path(location:'libs')
~~~
## 4.获取reference的值
build.xml
~~~
<project>
<property name="antProp" value="a property defined in an Ant build"/>
<path refid="classpath"/>
<path id="antPath" location="libs"/>
<target name="hello">
<echo>buildDir=${buildDir}</echo>
</target>
</project>
~~~
build.gradle
~~~
ant.importBuild ('build.xml'){
antTargetName ->'a-'+antTargetName
}
task intro << {
println 'Hello,from gradle'
}
ant.buildDir = buildDir
ant.properties.buildDir = buildDir
ant.properties['buildDir'] = buildDir
ant.property(name:'buildDir',location:buildDir)
println ant.antProp
ant.path(id:'classpath',location:'libs')
ant.references.classpath = ant.path(location:'libs')
ant.references['classpath'] = ant.path(location:'libs')
println ant.references.antPath
println ant.references['antPath']
~~~
输出如下:
~~~
qianhuis-Mac-mini:0112 qianhui$ gradle intro
a property defined in an Ant build
/Users/qianhui/Documents/Developer/gradle_project/0112/libs
/Users/qianhui/Documents/Developer/gradle_project/0112/libs
:intro
Hello,from gradle
BUILD SUCCESSFUL
Total time: 3.001 secs
~~~
gradle学习(17)-被合并的ant
最后更新于:2022-04-01 06:51:57
## 1.在gradle使用ant
~~~
task hello << {
String greeting = 'hello ant'
ant.echo(message:greeting)
}
~~~
使用的是ant中的echo任务执行打印信息
~~~
qianhuis-Mac-mini:0112 qianhui$ gradle hello
:hello
[ant:echo] hello ant
BUILD SUCCESSFUL
Total time: 2.629 secs
~~~
上面是使用嵌套的方法传递信息个 ant,现在直接传递试试
~~~
task hello << {
ant.echo("Hello ant")
}
~~~
~~~
qianhuis-Mac-mini:0112 qianhui$ gradle hello
:hello
[ant:echo] Hello ant
BUILD SUCCESSFUL
Total time: 3.556 secs
~~~
你还可以个 ant任务传递闭包代码块:
~~~
task zip << {
ant.zip(destfile:'archive.zip'){
fileset(dir:'src'){
include(name:'**.xml')
exclude(name:'**.java')
}
}
}
~~~
将src下的文件打包成archive.zip压缩包,只打包xml文件,不打包java文件。
先在src下随便建几个xml文件,执行命令:
~~~
qianhuis-Mac-mini:0112 qianhui$ gradle zip
:zip
BUILD SUCCESSFUL
Total time: 2.801 secs
~~~
会在根目录下生成了一个压缩文件:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e4667dbff5.jpg)
## 2.ant类型
操作ant.path对象。
~~~
task list << {
def path = ant.path{
fileset(dir:'libs',includes:'*.jar')
}
path.list().each{
println it
}
}
~~~
~~~
qianhuis-Mac-mini:0112 qianhui$ gradle list
:list
/Users/qianhui/Documents/Developer/gradle_project/0112/libs/1.jar
BUILD SUCCESSFUL
Total time: 2.943 secs
~~~
## 3.使用自定义的ant任务
使用taskdef或者typedef关键字来自定义任务,与你在build.xml中是一样的效果。
~~~
task check <<{
ant.taskdef(name:'pmd',
classname:'net.sourceforge.pmd.ant.PMDTask',
classpath:configurations.pmd.asPath)
ant.pmd(shortFilenames:'true',
failonruleviolation:'true',
rulesetfiles:file('pmd-rules.xml').toURI.toString()){
formatter(type:'text',toConsole:'true')
fileset(dir:'src')
}
}
configurations{
pmd
}
dependencies{
pmd group:'pmd',name:'pmd',version:'4.2.5'
}
~~~
## 4.导入ant的构建脚本build.xml
#### 包含ant脚本
大家都知道ant的构建脚本是 build.xml文件,但是你不知道的是gradle可以包含该build.xml能解析里面的信息。看gradle是如何做的:
首先生成一个build.xml 文件:
~~~
<project>
<target name="hello">
<echo>Hello, from Ant</echo>
</target>
</project>
~~~
build.gradle包含该xml文件:
~~~
ant.importBuild 'build.xml'
~~~
执行命令,我们执行build.xml中定义的hello任务试试:
~~~
qianhuis-Mac-mini:0112 qianhui$ gradle hello
:hello
[ant:echo] Hello, from Ant
BUILD SUCCESSFUL
Total time: 2.775 secs
~~~
嘿,还真的可以。
#### 添加依赖
既然可以将ant的任务当成gradle的任务,那么我们添加一个依赖试试:
~~~
ant.importBuild 'build.xml'
task intro(dependsOn:hello) << {
println 'Hello,from gradle'
}
~~~
执行任务试试
~~~
qianhuis-Mac-mini:0112 qianhui$ gradle intro
:hello
[ant:echo] Hello, from Ant
:intro
Hello,from gradle
BUILD SUCCESSFUL
Total time: 3.726 secs
~~~
一样可以。
#### 使用task behaviour
~~~
ant.importBuild 'build.xml'
task intro(dependsOn:hello) << {
println 'Hello,from gradle'
}
hello <<{
println 'Hello,from gradle'
}
~~~
~~~
qianhuis-Mac-mini:0112 qianhui$ gradle hello
:hello
[ant:echo] Hello, from Ant
Hello,from gradle
BUILD SUCCESSFUL
Total time: 3.704 secs
~~~
一样有效果。
#### 在ant脚本中添加依赖
我们还可以在build.xml中依赖我们在build.gradle 中定义的任务。修改build.xml文件:
~~~
<project>
<target name="hello" depends="intro">
<echo>Hello, from Ant</echo>
</target>
</project>
~~~
修改build.gradle
~~~
ant.importBuild 'build.xml'
task intro << {
println 'Hello,from gradle'
}
hello <<{
println 'Hello,from gradle'
}
~~~
执行命令:
~~~
qianhuis-Mac-mini:0112 qianhui$ gradle hello
:intro
Hello,from gradle
:hello
[ant:echo] Hello, from Ant
Hello,from gradle
BUILD SUCCESSFUL
Total time: 2.713 secs
~~~
#### 修改build.xml中target名称
build.xml
~~~
<project>
<target name="hello" >
<echo>Hello, from Ant</echo>
</target>
</project>
~~~
build.gradle
~~~
ant.importBuild ('build.xml'){
antTargetName ->'a-'+antTargetName
}
task intro << {
println 'Hello,from gradle'
}
~~~
这个时候执行命令一定要在hello前加a-
~~~
qianhuis-Mac-mini:0112 qianhui$ gradle a-hello
:a-hello
[ant:echo] Hello, from Ant
BUILD SUCCESSFUL
Total time: 3.631 secs
~~~
gradle学习(16)-操作文件
最后更新于:2022-04-01 06:51:55
## 1.定位文件
Project类中有一个file方法可以用来定位文件。
build.gradle:
~~~
File configFile = file('src/config.xml')
configFile = file(configFile.absolutePath)
println configFile.path
configFile = file(new File('src/config.xml'))
~~~
执行gradle -q命令:
~~~
D:\GRADLE~2\0112>gradle -q
D:\gradle_product\0112\src\config.xml
Welcome to Gradle 2.2.1.
To run a build, run gradle <task> ...
To see a list of available tasks, run gradle tasks
To see a list of command-line options, run gradle --help
~~~
虽然我们目录中没有src\config.xml。但是file方法仍然定位了该文件,且能正确输出文件的绝对路径。
从上面的脚本中可以看出,file方法可以接受字符串和File对象作为参数。file方法一般都会将项目的根目录作为默认的父目录,而不是当前工作目录。
## 2.文件集合-FileCollection
项目集合在gradle中用FileCollection接口来表示。而且gradle中很多对象都是实现该接口的,例如依赖配置就是实现该接口的。
调用Project.files()方法可以获得FileCollection实例,你可以传入任意数量的对象,然后会被转换为File对象的集合,下面创建一个FileCollection集合。
~~~
FileCollection collection = files('src/file1.txt',new File('src/file2.txt'),['src/file3.txt','src/file4.txt'])
~~~
上面的files方法里传入的参数真是“乱其八糟”的,有字符串,文件,文件集合。下面来看看文件集合的一些特性:
#### 可迭代
文件结合是可迭代的,支持下面的操作:
as操作符:将集合转化为其他类型
+操作符:合并2个文件集合
-操作符:从一个集合扣除一个集合
看下面一个综合的例子:
~~~
FileCollection collection = files('src/file1.txt'
,new File('src/file2.txt'),
['src/file3.txt', 'src/file4.txt'])
collection.each{File file ->
println file.name
}
Set set = collection.files
Set set2 = collection as Set
List list = collection as List
String path = collection.asPath
def union = collection + files('src/file5.txt')
def different = collection - files('src/file5.txt')
~~~
执行命令:
~~~
D:\GRADLE~2\0112>gradle -q
file1.txt
file2.txt
file3.txt
file4.txt
Welcome to Gradle 2.2.1.
To run a build, run gradle <task> ...
To see a list of available tasks, run gradle tasks
To see a list of command-line options, run gradle --help
~~~
#### 闭包和可回调方法
~~~
task list << {
File srcDir
// Create a file collection using a closure
FileCollection collection = files { srcDir.listFiles() }
srcDir = file('src')
println "Contents of $srcDir.name"
collection.collect { relativePath(it) }.sort().each { println it }
srcDir = file('src2')
println "Contents of $srcDir.name"
collection.collect { relativePath(it) }.sort().each { println it }
}
~~~
build.gradle在之前的基础上添加了一个list任务,可以看到files方法中传入的是什么?是一个闭包代码块,然后当调用sort()方法来查询集合内容的时候,才会调用闭包代码块,所以srcDir写在了collection的定义后。
## 3.文件的树形结构-FileTree
FileTree接口继承于FileCollection接口,在gradle中有一些对象是继承FileTree,比如SourseSet。
#### 获得FileTree对象
Project.fileTree方法,
~~~
FileTree tree = fileTree(dir: 'src/main')
tree.include '**/*.java'
tree.exclude '**/Abstract*'
tree = fileTree('src').include('**/*.java')
tree = fileTree('src') {
include '**/*.java'
}
tree = fileTree(dir: 'src', include: '**/*.java')
tree = fileTree(dir: 'src', includes: ['**/*.java', '**/*.xml'])
tree = fileTree(dir: 'src', include: '**/*.java', exclude: '**/*test*/**')
~~~
第一个定义中,是将src/main目录(包括子目录的子目录)下的文件生成fileTree文件,包括以java结尾的文件,不包括已Abstract开头的文件。
第二个定义中,采用连写的方式,包括src目录下的所有的java文件。
第三个定义中,类似第二种,只是采用的方式是闭包形式来定义所包含的文件。
第四种-第六中,根据构造函数的重载特性,传入不同的参数创建的。
#### 使用FileTree对象
~~~
task findTree(dependsOn:'create') <<{
FileTree filtered = tree.matching{
include 'org/gradle/api/**'
}
FileTree sum = tree + fileTree(dir:'src/test')
tree.visit{
element ->
println "$element.relativePath =>$element.file"
}
}
task create {
File testFile = file('src/test/java')
testFile.mkdirs()
}
~~~
执行命令
~~~
D:\GRADLE~2\0112>gradle -q findTree
main =>D:\gradle_product\0112\src\main
main/java =>D:\gradle_product\0112\src\main\java
test =>D:\gradle_product\0112\src\test
test/java =>D:\gradle_product\0112\src\test\java
~~~
## 4.由压缩文件得到FileTree
针对压缩文件有2个方法zipTree和tarTree两个方法。
~~~
FileTree zip = zipTree('someFile.zip')
FileTree tar = tarTree('someFile.tar')
FileTree someTar = tarTree(resources.gzip('someTar.ext'))
~~~
## 5.指定输入文件的集合
~~~
compile{
source = file('src/main/java')
}
compile{
source = 'src/main/java'
}
compile{
source = ['src/main/java','../shared/java']
}
compile{
source={
file('src').listFiles.findAll{
it.name.endsWith('.zip')
}.collect{
zipTree(it)
}
}
}
~~~
上面的4中方式都是重新设置了source文件夹的位置,也就是输入文件的位置。
Project下还有一个source方法来定义输入文件。
~~~
compile{
source 'src/main/java','src/main/groovy'
source file('../shared/java')
source{
file('src/test').listFiles()
}
}
~~~
## 6.复制文件
继承Copy对象来复制文件
~~~
task copyTask(type:Copy){
from 'src/main/webapp'
into 'build/explodedWar'
}
~~~
from定义要被复制的文件目录,into定义的是复制到的目录。
from的来源有很多,类似files()
1.目录:目录下的所有文件都会包含在内
2.文件
3.不存在的文件:忽略
4.任务:任务的输出文件
into方法类似file方法
~~~
task copyTask(type:Copy){
from 'src/main/webapp'
into 'build/explodedWar'
}
task copyTaskWithPaths(type:Copy){
from 'src/main/webapp'
into 'build/explodedWar'
include '**/*.html'
include '**/*.jsp'
exclude{
details ->details.file.name.endsWith('/html')&&
details.file.text.contains('staging')
}
}
task anotherCopyTask(type:Copy){
from 'src/main/webapp'
from 'index.html'
from copyTask
from copyTaskWithPaths.outputs
from zipTree(asserts.zip)
into {
getDestDir()
}
}
~~~
任务copyTaskWithPaths可以来删选所选目录中的文件。
anotherCopyTask任务说明from的来源有多种形式。
还有一种复制文件的方式是调用Project的copy方法,需要注意的一点是,在任务中使用copy方法,必须显示的定义输入和输出文件。
~~~
task copyMethod <<{
copy{
from 'src/main/webapp'
into 'build/explodedWar'
include '**/*.html'
include '**/*.jsp'
}
}
~~~
## 7.重命名文件
~~~
task rename(type:Copy){
from 'src/main/webapp'
into 'build/explodedWar'
rename{
String fileName->
fileName.replace('1','2')
}
}
~~~
上面的任务是将src/main/webapp目录下的文件移到build/explodedWar下,在移动后,将文件名为1的文件重命名为2。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e4667c67f8.jpg)
~~~
task rename(type:Copy){
from 'src/main/webapp'
into 'build/explodedWar'
rename{
String fileName->
fileName.replace('1','2')
}
rename '(.+)1(.+)','$1$2'
rename(/(.+)1(.+)/,'$1$2')
}
~~~
第2个和第三个rename中,使用了正则表达式。$1表示表达式中第一个匹配项。
## 8.过滤文件
~~~
import org.apache.tools.ant.filters.FixCrLfFilter
import org.apache.tools.ant.filters.ReplaceTokens
task filter(type:Copy){
from 'src/main/webapp'
into 'build/explodedWar'
expand(copyright:'2009',version:'2.3.2')
expand(project.properties)
filter(FixCrLfFilter)
filter(ReplaceTokens,tokens:[copyright:'2009',version:'2.3.1'])
filter{
"[$line]"
}
}
~~~
expand 和filter都是寻找一个叫token的东西,形式有点像@tokenName@
## 9.CopySpec的使用
复制规范形成一个层次结构。一份规范继承目的地路径,包括模式、排除模式,复制操作,名称映射和过滤器。嵌套复制:
~~~
task nestedSpecs(type:Copy){
into 'build/explodedWar'
exclude '**/*staging'
from('src/dist'){
include '**/*.html'
}
into('libs'){
from configurations.runtime
}
}
~~~
但是执行的时候报错。是因为我们没有定义runtime的依赖,所以会报如下错误。
~~~
qianhuis-Mac-mini:0112 qianhui$ gradle -q nestedSpecs
FAILURE: Build failed with an exception.
* Where:
Build file '/Users/qianhui/Documents/Developer/gradle_project/0112/build.gradle' line: 8
* What went wrong:
A problem occurred evaluating root project '0112'.
> Could not find property 'runtime' on configuration container.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
~~~
## 10 .Sync任务
同步任务继承与copy任务,当它执行的时候,将source文件copy到目标目录中,然后删除目标文件中不是copy过来的文件。
~~~
task libs(type:Sync){
from configurations.runtime
into "$buildDir/libs"
}
repositories{
mavenCentral()
}
dependencies{
runtime 'junit:junit:4.11'
}
~~~
将runtime运行时所依赖的jar文件复制到build/libs目录下。
## 11.压缩文件
~~~
apply plugin:'java'
version = 1.0
task myZip(type:Zip){
from 'src'
}
println myZip.archiveName
println relativePath(myZip.destinationDir)
println relativePath(myZip.archivePath)
~~~
执行命令输出:
~~~
qianhuis-Mac-mini:0112 qianhui$ gradle -q myZip
0112-1.0.zip
build/distributions
build/distributions/0112-1.0.zip
~~~
gradle学习(15)-任务
最后更新于:2022-04-01 06:51:52
## 1.任务覆盖
所谓任务覆盖就是,就是2个任务名称相同,但是却还可以同时存在。这是为啥,因为有overwrite,类似于java中重写。
~~~
task copy << {
println "this is a first"
}
task copy(overwrite:true)<<{
println "this is a second"
}
~~~
执行命令
~~~
D:\GRADLE~2\0112>gradle -q copy
this is a second
~~~
如果不在第二个copy中写overwrite的话,就会报错,提示任务已经存在。
~~~
D:\GRADLE~2\0112>gradle -q copy
FAILURE: Build failed with an exception.
* Where:
Build file 'D:\gradle_product\0112\build.gradle' line: 5
* What went wrong:
A problem occurred evaluating root project '0112'.
> Cannot add task ':copy' as a task with that name already exists.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug
option to get more log output.
~~~
## 2.跳过任务
gradle有很多种方式达到跳过任务执行的方式,下面一一列举:
#### 使用断言
~~~
task hello <<{
println 'hello world'
}
hello.onlyIf{!project.hasProperty('skipHello')}
~~~
如果执行gradle -q hello命令
~~~
D:\GRADLE~2\0112>gradle -q hello
hello world
~~~
但是在该命令后面加上一些-P参数,来添加属性,命令的执行结果就不一样了:
~~~
D:\GRADLE~2\0112>gradle -q hello -P skipHello
D:\GRADLE~2\0112>
~~~
没有打印任何信息,说明任务hello被跳过了。
#### 抛出StopExecutionException
和java类似,可以通过抛出异常,来跳过某个任务的执行
~~~
task compile <<{
println "We are doing the compile"
}
compile.doFirst{
if(true){throw new StopExecutionException()}
}
task myTask(dependsOn:'compile')<<{
println 'I am not affected'
}
~~~
执行结果如下:
~~~
D:\GRADLE~2\0112>gradle -q myTask
I am not affected
~~~
#### 任务的enabled属性
每一次任何都有一个enabled属性,默认情况下该属性是true的。但是你也可以根据自己的需要将该属性设置为false,来达到跳过任务的作用的。
~~~
task disableMe <<{
println 'ni dou bu hui zhi xing ,ni hai shuo zhe me duo hua'
}
disableMe.enabled = false
~~~
执行命令后的是没有任何输出的:
~~~
D:\GRADLE~2\0112>gradle disableMe
:disableMe SKIPPED
BUILD SUCCESSFUL
Total time: 2.216 secs
~~~
## 3.跳过up-to-date式的任务
~~~
task transform{
ext.srcFile = file('mountains.xml')
ext.destDir = new File(buildDir,'generated')
doLast{
println "Transforming source file"
destDir.mkdirs()
def mountains = new XmlParser().parse(srcFile)
mountains.mountain.each{
mountain ->
def name = mountain.name[0].text()
def height = mountain.height[0].text()
def destFile = new File(destDir,"${name}.txt")
destFile.text = "$name -> ${height}\n"
}
}
}
~~~
这个时候执行任务是会报错的:
~~~
D:\GRADLE~2\0112>gradle -q transform
Transforming source file
FAILURE: Build failed with an exception.
* Where:
Build file 'D:\gradle_product\0112\build.gradle' line: 7
* What went wrong:
Execution failed for task ':transform'.
> D:\gradle_product\0112\mountains.xml (系统找不到指定的文件。)
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug
option to get more log output.
~~~
因为需要添加一个mountains.xml文件。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e4667b1153.jpg)
那么这个xml文件里写啥呢?不能说定义一个空的xml文件吧。那就要从任务的定义来看了,因为build.gradle中要读的属性有mountain,name,height。mountain又包含name和height。那么我的xml应该定义如下:
~~~
<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
<mountain>
<name>doctorq</name>
<height>111</height>
</mountain>
~~~
这个时候执行命令:
~~~
D:\GRADLE~2\0112>gradle transform
:transform
Transforming source file
BUILD SUCCESSFUL
Total time: 3.416 secs
D:\GRADLE~2\0112>gradle transform
:transform
Transforming source file
BUILD SUCCESSFUL
Total time: 2.309 secs
~~~
从上面可以看出2次执行的结果输出的结果是一样的,没有什么差异。那么如果能让任务是一个up-to-date式呢。
我们修改一下build.gradle中transform任务的定义,添加2个属性inputs和outputs:
~~~
task transform{
ext.srcFile = file('mountains.xml')
ext.destDir = new File(buildDir,'generated')
inputs.file srcFile
outputs.file destDir
doLast{
println "Transforming source file"
destDir.mkdirs()
def mountains = new XmlParser().parse(srcFile)
mountains.mountain.each{mountain ->
def name = mountain.name[0].text()
def height = mountain.height[0].text()
def destFile = new File(destDir,"${name}.txt")
destFile.text = "$name -> ${height}\n"
}
}
}
~~~
这个时候我们再执行任务的时候就会发现,第一次执行的时候是没有任何差异的,但是以后执行的时候,任务后面就会多了一个up-to-date的标识:
~~~
D:\GRADLE~2\0112>gradle transform
:transform
Transforming source file
BUILD SUCCESSFUL
Total time: 2.496 secs
D:\GRADLE~2\0112>gradle transform
:transform UP-TO-DATE
BUILD SUCCESSFUL
Total time: 2.34 secs
D:\GRADLE~2\0112>gradle transform
:transform UP-TO-DATE
BUILD SUCCESSFUL
Total time: 2.325 secs
~~~
当你把build/generated目录删除掉以后,重新执行gradle transform任务的时候,是没有UP-TO-DATE标识的。但是一旦有build/generated目录就会显示UP-TO-DATE目录,提示你是最新生成的。
上面的2两个属性分别所属类别如下:
inputs属于TaskInputs类型下的
outputs属于TaskOutputs类型下的
需要注意的是,如果一个任务没有定义outputs属性,就永远都不会出现UP-TO-DATE的。对于下面情形,task任务仍然被看作UP-TO-DATE。
1.outputs定位的不是文件
2.TaskOutpusts.upToDateWhen方法被调用
#### 原理
上面说了那么多,那么我们让任务出现UP-TO-DATE到底有什么用呢?
gradle在任务第一次被执行的时候,会记住任务的输入文件以及里面的内容。执行成功后,会记住任务的输出文件以及里面的内容。在下次执行任务的时候,同样会记住这些内容,我们称这些为记忆。
在新的一次任务执行来了以后,在该任务执行前,gradle会生成新的记忆。然后将该新的记忆和之前的记忆来比对,如果输出的文件和文件中的内容没有任何变化,就标记为UP-TO-DATE且跳过该任务,只有不同的时候,才会执行该任务。这样的话可以使任务的执行更快。
## 4.任务规则
任务的行为依赖传入的参数,这个时候需要提供任务的规则。
~~~
tasks.addRule("Pattern:ping<ID>"){
String taskName ->
if(taskName.startsWith("ping")){
task(taskName)<<{
println taskName
println "Pinging: " + (taskName-'ping')
}
}
}
~~~
输出:
~~~
D:\GRADLE~2\0112>gradle -q pingServer1
pingServer1
Pinging: Server1
~~~
build.gradle文件中,addRule中的参数是一个字符串,它其实是该rule的描述信息。可以通过gradle tasks查看:
~~~
Rules
-----
Pattern:ping<ID>
To see all tasks and more detail, run with --all.
BUILD SUCCESSFUL
~~~
上面我们是通过命令行来传入,下面来看看通过依赖的方式,调用。
~~~
tasks.addRule("Pattern:ping<ID>"){
String taskName ->
if(taskName.startsWith("ping")){
task(taskName)<<{
println "Pinging: " + (taskName-'ping')
}
}
}
task groupPing{
dependsOn pingServer1,pingServer2
}
~~~
~~~
D:\GRADLE~2\0112>gradle -q groupPing
Pinging: Server1
Pinging: Server2
~~~
## 5.Finalizer任务
这让我想起了java中Object类中的finalize方法。需要注意的是,该特性目前还不完善,以后可能发生改变。
在这个里面我们要理解2个概念,一个是终止的任务finalizer task,一个是被终止的任务finalized task。
finalizer task是我们这节要讲的task,finalized task是被finalizer修饰的task。看下面的例子:
~~~
task taskX << {
println 'taskX'
}
task taskY << {
println 'taskY'
}
taskX.finalizedBy taskY
~~~
~~~
D:\GRADLE~2\0112>gradle -q taskX
taskX
taskY
~~~
上面的任务taskX ,taskY哪一个是finalizer,哪个是finalized呢?
被修饰的是taskX,所以taskX是finalized,taskY是finalizer,知道这两个概念是很重要的。
下面再来研究一下finalizer任务的特性:
#### 被修饰任务失败,finalizer任务也会照常执行
~~~
task taskX << {
throw new RuntimeException()
println 'taskX'
}
task taskY << {
println 'taskY'
}
taskX.finalizedBy taskY
~~~
执行命令后输出:
~~~
D:\GRADLE~2\0112>gradle -q taskX
taskY
FAILURE: Build failed with an exception.
* Where:
Build file 'D:\gradle_product\0112\build.gradle' line: 2
* What went wrong:
Execution failed for task ':taskX'.
> java.lang.RuntimeException (no error message)
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug
option to get more log output.
~~~
这个时候你会发现在taskX执行失败的情况下,taskY却正常执行。finalizer任务是依赖于taskX的,如果taskX不执行,那么taskY也不执行,那么至于taskX执行的成不成功,不是finalizer任务所关系的。
gradle学习(14)-任务
最后更新于:2022-04-01 06:51:50
## 1.任务的定义
之前我们定义任务的时候采用的是task + 任务名的方式。例如
~~~
task hello << {
println "hello"
}
~~~
现在再介绍另外两种方式,和上面的定义是等价的。
~~~
task(hello)<<{
println "hello"
}
~~~
~~~
task('hello')<<{
println "hello"
}
~~~
gradle还提供了一个tasks容器来创建任务,通过调用create方法:
~~~
tasks.create(name:'hello')<<{
println "hello"
}
~~~
## 2\. 任务的定位
**将任务看成项目的属性的方式**
~~~
task hello
println hello.name
println project.hello.name
~~~
~~~
qianhuis-Mac-mini:0111_1 qianhui$ gradle -q hello
hello
hello
~~~
**使用tasks容器来定位**
~~~
task hello
println tasks.hello.name
println tasks['hello'].name
~~~
~~~
qianhuis-Mac-mini:0111_1 qianhui$ gradle -q hello
hello
hello
~~~
**tasks.getByPath()方式来获得**
~~~
task hello
println tasks.getByPath('hello').path
println tasks.getByPath(':hello').path
~~~
~~~
qianhuis-Mac-mini:0111_1 qianhui$ gradle -q hello
:hello
:hello
~~~
## 3.配置任务
~~~
task myCopy(type:Copy)
println myCopy.name
~~~
定义一个Copy类型名为myCopy的任务。
~~~
qianhuis-Mac-mini:0111_1 qianhui$ gradle -q myCopy
myCopy
~~~
**配置任务的第一种方式**
~~~
Copy myCopy = task(myCopy,type:Copy)
myCopy.from 'resources'
myCopy.into 'target'
myCopy.include('**/*.txt','**/*.xml','**/*.properties')
~~~
这种方式每次都需要先显式的定义一个对象。比较麻烦。下面有另外一种更好的方式:
**配置任务的第二种方式**
~~~
Copy myCopy = task(myCopy,type:Copy)
myCopy{
from 'resources'
into 'target'
include('**/*.txt','**/*.xml','**/*.properties')
}
~~~
这种方式适用于任何任务,第二行myCopy是tasks.getByName的缩写方式。这种方式在配置的时候是有作用的,在执行的时候是不起作用的。
**配置任务的第三种方式**
闭包方式:
~~~
task copy(type:Copy){
from 'resources'
into 'target'
include('**/*.txt','**/*.xml','**/*.properties')
}
~~~
## 4.配置任务依赖
#### 以任务名的方式定义依赖
依赖其他项目的任务,要想实现该例子,首先要记得settings.gradle在多项目的必要性,所以我们先编写settings.gradle:
~~~
include "projectA","projectB"
~~~
这样我们的build.gradle就能识别出这两个子项目,接下来在build.gradle中实现依赖其他项目的任务。
~~~
project('projectA'){
task taskX(dependsOn:':projectB:taskY')<<{
println 'taskX'
}
}
project('projectB'){
task taskY <<{
println 'taskY'
}
}
~~~
执行命令:
~~~
qianhuis-Mac-mini:0111_1 qianhui$ gradle -q taskX
taskY
taskX
~~~
这是在根项目的build.gradle来定义2个子项目的任务。在正常情况下,我们应该是在各自的项目中,然后来定义项目之间的依赖,所以我们现在分拆taskX 和taskY两个任务到各自的目录中。
在projectA的build.gradle文件中添加:
~~~
task taskX(dependsOn:':projectB:taskY')<<{
println 'taskX'
}
~~~
在projectB 的build.gradle文件中添加:
~~~
task taskY <<{
println 'taskY'
}
~~~
然后将刚才跟目录中的build.gradle中的内容删除,避免造成任务冲突。然后在projectA 下执行命令:
~~~
qianhuis-Mac-mini:projectA qianhui$ gradle -q taskX
taskY
taskX
~~~
#### 你可以通过task对象来配置依赖
~~~
task taskX << {
println 'taskX'
}
task taskY <<{
println 'taskY'
}
taskX.dependsOn taskY
~~~
输出:
~~~
qianhuis-Mac-mini:0111_1 qianhui$ gradle -q taskX
taskY
taskX
~~~
#### 使用闭包的方式配置依赖
~~~
task taskX << {
println 'taskX'
}
taskX.dependsOn{
tasks.findAll{
task -> task.name.startsWith('lib')
}
}
task lib1<<{
println 'lib1'
}
task lib2 <<{
println 'lib2'
}
~~~
执行任务输出:
~~~
qianhuis-Mac-mini:0111_1 qianhui$ gradle -q taskX
lib1
lib2
taskX
~~~
## 5.任务顺序
(该功能处在孵化阶段,以后可能会改变,所以需要注意)
任务顺序的两个规则:must run after 和 should run after。
当你定义taskX和taskY的关系是必须后执行,那就说明taskX必须在taskY任务执行完才能执行,无论在什么时候。
当你定义taskX和taskY的关系时应该后执行,那就说明不是强制要求的,只是这么做更好点,但是我们应该意识到我们讲should 等价于must还是比较好的。
现在来试试这两种顺序的执行的不同:
#### mustRunAfter
在build.gradle定义如下:
~~~
task taskX << {
println 'taskX'
}
task taskY <<{
println 'taskY'
}
taskX.mustRunAfter taskY
~~~
上面定义了taskX必须在taskY后执行,但是如果这个时候我们只执行taskX呢?
~~~
qianhuis-Mac-mini:0111_1 qianhui$ gradle -q taskX
taskX
~~~
只执行taskY:
~~~
qianhuis-Mac-mini:0111_1 qianhui$ gradle -q tasky
taskY
~~~
从上面可以看出,单独执行一个任务,是没有什么一样的。那现在我们同时执行两个任务:
~~~
qianhuis-Mac-mini:0111_1 qianhui$ gradle -q tasky taskx
taskY
taskX
qianhuis-Mac-mini:0111_1 qianhui$ gradle -q taskx tasky
taskY
taskX
~~~
注意上面我们执行了两次,但是taskX和taskY顺序不一样,但是结果却是一样的,这就说明mustRunAfter奏效了。
#### shouldRunAfter
修改build.gradle文件:
~~~
task taskX << {
println 'taskX'
}
task taskY <<{
println 'taskY'
}
taskX.shouldRunAfter taskY
~~~
我们同样执行4次,输出如下:
~~~
qianhuis-Mac-mini:0111_1 qianhui$ gradle -q tasky
taskY
qianhuis-Mac-mini:0111_1 qianhui$ gradle -q taskx
taskX
qianhuis-Mac-mini:0111_1 qianhui$ gradle -q taskx tasky
taskY
taskX
qianhuis-Mac-mini:0111_1 qianhui$ gradle -q tasky taskx
taskY
taskX
~~~
看出不同了么?我是没看出来,所以官方文档上的例子还是无法说明他们的区别,等有时间再去质问他们吧。
上面需要注意点的是:
taskX.shouldRunAfter taskY == taskX.shouldRunAfter(taskY)
taskX.mustRunAfter taskY == taskX.mustRunAfter(taskY)
其中的括号和空格时等效的。所以我们要了解的是gradle中调用方法的方式多种的。
有2种情况会使上面定义的顺序失效:2个任务之间有依赖和使用 --continue参数
来看看使之失效的例子,上面的例子taskX在taskY后执行,那么这个时候我们定义taskY依赖taskX呢。按我们之前学习依赖中得到的知识,依赖的任务要先执行到。那么看实际执行情况:
~~~
task taskX << {
println 'taskX'
}
task taskY <<{
println 'taskY'
}
taskY.dependsOn taskX
taskX.shouldRunAfter taskY
~~~
~~~
qianhuis-Mac-mini:0111_1 qianhui$ gradle -q tasky taskx
taskX
taskY
~~~
说明依赖的优先级要高于顺序的定义。
## 6.添加任务描述
build.gradle定义如下:
~~~
task taskX {
description 'this a beatiful world'
println 'taskX'
}
task taskY <<{
description 'this a bad world'
println 'taskY'
}
~~~
执行gradle tasks命令输出:
~~~
Other tasks
-----------
taskX - this a beatiful world
taskY
~~~
为什么taskX和taskY的有一个没有输出描述信息,在于taskY的定义中有<<,所以没能输出描述信息。那就说明任务的定义中这两种方式还是有一点区别的。
gradle学习(13)-有的没的
最后更新于:2022-04-01 06:51:48
## 1.用脚本创建目录
通常情况下,创建目录使用mkdir来创建目录,但是有一种更好的方式来做到,避免每次都手动创建。定义一个任务来创建目录,然后用dependsOn来依赖该任务,这有可以在需要的时候创建目录。
~~~
def classesDir = new File('build/classes')
task resources << {
classesDir.mkdirs()
}
task compile(dependsOn:'resources')<<{
if(classesDir.isDirectory()){
println 'The class directory exists.I canoperate'
}
}
~~~
执行compile任务
~~~
qianhuis-Mac-mini:0111 qianhui$ gradle -q compile
The class directory exists.I canoperate
~~~
然后就会在该目录下生成build/classes目录
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-07_568e46678dbda.jpg)
## 2.gradle属性和系统属性
有3种方式添加属性,命令行中的-P和-D参数,以及属性文件gradle.properties
**通过命令行中的-P命令传递属性。首先build.gradle定义属性**
~~~
task printProps <<{
println commandLineProjectProp
}
~~~
在命令行中执行该命令,如果不带参数会出错。
~~~
~~~
qianhuis-Mac-mini:0111 qianhui$ gradle -q printProps
FAILURE: Build failed with an exception.
* Where:
Build file '/Users/qianhui/Documents/Developer/gradle_project/0111/build.gradle' line: 2
* What went wrong:
Execution failed for task ':printProps'.
> Could not find property 'commandLineProjectProp' on task ':printProps'.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
~~~
~~~
gradle -q printProps -PcommandLineProjectProp=thisisacommandlineprop
~~~
输出如下
~~~
qianhuis-Mac-mini:0111 qianhui$ gradle -q printProps -PcommandLineProjectProp=thisisacommandlineprop
thisisacommandlineprop
~~~
上面的写法一定要注意,-P后面也可以有空格,执行的效果是一样的。
~~~
qianhuis-Mac-mini:0111 qianhui$ gradle -q printProps -P commandLineProjectProp=thisisacommandlineprop
thisisacommandlineprop
~~~
但是等号(=)两端不能有空格,且属性值也不能有空格。但是如果你用单引号 或者双引号包裹的话可以有空格。
~~~
qianhuis-Mac-mini:0111 qianhui$ gradle -q printProps -P commandLineProjectProp =this is acommandlineprop
FAILURE: Build failed with an exception.
* What went wrong:
Task '=this' not found in root project '0111'.
* Try:
Run gradle tasks to get a list of available tasks. Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
~~~
用单引号或双引号包裹
~~~
qianhuis-Mac-mini:0111 qianhui$ gradle -q printProps -P commandLineProjectProp='this is a commandline prop'
this is a commandline prop
~~~
~~~
qianhuis-Mac-mini:0111 qianhui$ gradle -q printProps -P commandLineProjectProp="this is a commandline prop"
this is a commandline prop
~~~
**-D添加gradle属性。**
build.gradle文件如下
如果这个时候我们还想刚才那样执行命令的话,会报错
~~~
qianhuis-Mac-mini:0111 qianhui$ gradle -q printProps -P commandLineProjectProp="this is a commandline prop"
this is a commandline prop
FAILURE: Build failed with an exception.
* Where:
Build file '/Users/qianhui/Documents/Developer/gradle_project/0111/build.gradle' line: 3
* What went wrong:
Execution failed for task ':printProps'.
> Could not find property 'systemProjectProp' on task ':printProps'.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
~~~
说明build.gradle定义的systemProjectProp不存在,这个时候就需要用-D来添加gradle属性
~~~
qianhuis-Mac-mini:0111 qianhui$ gradle -q printProps -P commandLineProjectProp="this is a commandline prop" -D org.gradle.project.systemProjectProp="this a gradle property"
this is a commandline prop
this a gradle property
~~~
**gradle.properties文件添加属性**
build.gradle文件添加一个属性
~~~
task printProps <<{
println commandLineProjectProp
println systemProjectProp
println gradleFileProperties
}
~~~
这个时候执行命令,会报错
~~~
qianhuis-Mac-mini:0111 qianhui$ gradle -q printProps -P commandLineProjectProp="this is a commandline prop" -D org.gradle.project.systemProjectProp="this a gradle property"
this is a commandline prop
this a gradle property
FAILURE: Build failed with an exception.
* Where:
Build file '/Users/qianhui/Documents/Developer/gradle_project/0111/build.gradle' line: 4
* What went wrong:
Execution failed for task ':printProps'.
> Could not find property 'gradleFileProperties' on task ':printProps'.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
~~~
在gradle.properties 文件中添加属性
~~~
gradleFileProperties = gradleFileProperties
~~~
这个时候执行命令
~~~
qianhuis-Mac-mini:0111 qianhui$ gradle -q printProps -P commandLineProjectProp="this is a commandline prop" -D org.gradle.project.systemProjectProp="this a gradle property"
this is a commandline prop
this a gradle property
gradleFileProperties
~~~
## 3.包含其他构建脚本
gradle 允许你包含其他构建的脚本,比如一些模版脚本,这样的扩展性挺好。
定义一个模版脚本:template.gradle
~~~
task hello <<{
println "this a template"
}
~~~
然后在build.gradle脚本中包含改脚本
~~~
apply from:'template.gradle'
~~~
执行任务hello
~~~
qianhuis-Mac-mini:0111 qianhui$ gradle -q hello
this a template
~~~
## 4.自定义对象
~~~
task configure <<{
def pos = configure(new java.text.FieldPosition(10)){
beginIndex = 1
endIndex = 5
}
println pos.beginIndex
println pos.endIndex
}
~~~
## 5.使用外部的脚本自定义对象
template.gradle修改如下:
~~~
beginIndex = 1
endIndex = 5
~~~
build.gradle修改如下
~~~
task configure <<{
def pos = new java.text.FieldPosition(10)
apply from:'template.gradle',to:pos
println pos.beginIndex
println pos.endIndex
}
~~~
其中的apply from:'template.gradle',to :pos将template的属性注入到pos对象中。执行命令后,输出是一样的。
~~~
qianhuis-Mac-mini:0111 qianhui$ gradle -q configure
1
5
~~~
## 6.缓存
gradle创建了一个.gradle目录用来缓存编译的脚本。只有变换的时候,才会重新生成缓存的脚本。
gradle学习(12)-groovy一些基础语法
最后更新于:2022-04-01 06:51:46
## 1.setter和getter语法
~~~
println project.buildDir
println getProject().getBuildDir()
project.buildDir = 'target'
getProject().setBuildDir('target')
task hello<<{
println 'Hello world'
println project.buildDir
}
~~~
执行gradle命令
~~~
/Users/qianhui/Documents/Developer/gradle_project/0110_1/build
/Users/qianhui/Documents/Developer/gradle_project/0110_1/build
Hello world
/Users/qianhui/Documents/Developer/gradle_project/0110_1/target
~~~
## 2.方法的调用
方法的调用可以带括号也可以不带。
~~~
test.systemProperty 'some.prop','value'
test.systemProperty('some.prop','value')
~~~
## 3.遍历
~~~
apply plugin:'java'
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<String,String> map = [key1:'value',key2:'value2']
~~~
## 4.将闭包作为方法的最后一个参数
~~~
repositories{
println "in a closure"
}
repositories(){
println "in a closure"
}
repositories({
println "in a closure"
})
~~~
在方法被调用的时候,其实可以用闭包语句来代替,因为方法其实也是执行闭包里的代码块。
## 5.代理闭包
每一个闭包都有可以代理对象,你可以通过该代理对象进行某些属性的设置,比如下面的dependencies任务的代理对象delegate。可以设置该任务的testCompile属性。
~~~
dependencies {
assert delegate == project.dependencies
testCompile('junit:junit:4.11')
delegate.testCompile('junit:junit:4.11')
}
~~~