build.gradle

// Gradle学习笔记
// Gradle里的任何东西都是基于两个基本概念:projects(项目)和tasks(任务).
// 每一个构建都是由一个或多个projects构成的。每一个project是由一个或多个tasks构成的。
// 一个task代表一些更加细化的构建,可能是编译一些classes、创建一个jar或者生成某个目录的压缩文件等。
 
/********************************************************************/
 
// 通过gradle命令可以运行一个Gradle构建,gradle命令会在当前目录中查找build.gradle文件,我们称该文件为一个构建脚本(build script),
// 但是严格来说它是一个构建配置脚本,这个脚本定义了一个project和它的tasks。
task hello { // 定义一个独立的task,叫做hello,并且加入了一个action,该action包含了一些groovy代码的闭包
	doLast {
		println 'Hello world!'
	}
}
 
// 命令行里进入脚本所在文件夹输入 gradle -q hello 执行叫做hello的task,也就是执行了上面定义的action:
// #补充#命令行里的 -q 代表quite模式,它不会生成Gradle的日志信息,所以只能看到task的输出,它使得输出更加清晰。
/**********输出结果*************/
//Hello world!
/**********输出结果*************/
 
/********************************************************************/
 
// 定义了一个叫做hello2的任务,该任务是一个可以执行的闭包。
// 该例子把doLast替换成 <<,它俩功能一样。
task hello2 << { 
	println 'hello world'
}
/**********输出结果*************/
//hello world
/**********输出结果*************/
 
/********************************************************************/
 
// 任务依赖
task water << {
	println 'water'
}
 
task fish(dependsOn: water) << {
	println 'fish depend on water'
}
 
// 执行 gradle -q fish
/**********输出结果*************/
//water
//fish depend on water
/**********输出结果*************/
// haha依赖于hehe,所以执行haha时,hehe任务会被优先执行来作为执行haha任务的条件。
 
/********************************************************************/
 
// 在加入一个依赖之前,这个所依赖的任务不需要提前定义
task dog(dependsOn: 'food') << { // 依赖于暂未定义的任务时单引号不能省略
	println 'dog depend on food'
}
 
task food << {
	println 'food'
}
 
// 执行 gradle -q dog
/**********输出结果*************/
//food
//dog depend on food
/**********输出结果*************/
 
/********************************************************************/
 
// 动态任务
4.times { // times表示从0开始循环到(4 - 1)即3
	number ->
		task "task$number" << {
			println "I am task number $number"
		}
}
 
// 执行 gradle -q task3
/**********输出结果*************/
//I am task number 3
/**********输出结果*************/
 
/********************************************************************/
 
// 使用已经存在的任务
 
task0.dependsOn task2, task3 // 给创建好的任务添加依赖
 
// 执行 gradle -q task0
/**********输出结果*************/
//I am task number 2
//I am task number 3
//I am task number 0
/**********输出结果*************/
 
task zhangsan << {
	println "I'm zhangsan"
}
 
zhangsan.doFirst { // 给创建好的任务添加行为
	println 'My first name is zhang'
}
 
zhangsan.doLast {
	println 'My last name is san'
}
 
zhangsan << {
	println "I'm the best"
}
 
// 执行 gradle -q zhangsan
/**********输出结果*************/
//My first name is zhang
//I'm zhangsan
//My last name is san
//I'm the best
/**********输出结果*************/
// doFirst和doLast可以被执行许多次,它们分别可以在任务动作列表的开始和结束加入动作,
// 当任务执行的时候,在动作列表里的动作将被按顺序执行。
 
/********************************************************************/
 
// 任务属性
task taskP << {
	println 'hello'
}
 
taskP.doLast {
	println "the task name is $taskP.name" // 这里的name是任务的默认属性,代表当前任务的名称
}
 
// 执行 gradle -q taskP
/**********输出结果*************/
//hello
//the task name is taskP
/**********输出结果*************/
 
// 自定义任务属性
task myTask {
	ext.myProperty = "myValue" // 自定义属性并赋初始值
}
 
task printTaskProperties << {
	println myTask.myProperty // 像任务默认属性那样读取属性
}
 
// 执行 gradle -q printTaskProperties
/**********输出结果*************/
//myValue
/**********输出结果*************/
 
/********************************************************************/
 
// 默认任务
// 例子:Gradle允许在脚本中定义一个或多个默认任务
defaultTasks 'default1', 'default2'
 
task default1 << {
	println 'default task default1'
}
 
task default2 << {
	println 'default task default2'
}
 
task other << {
	println "I'm not a default task"
}
 
// 执行 gradle -q // 等价于 gradle -q default1 default2
/**********输出结果*************/
//default task clean
//default task run
/**********输出结果*************/
// 在一个多项目构建中,每一个子项目都可以有它特别的默认任务
// 如果一个子项目没有特别的默认任务,父项目的默认任务将会被执行
 
/********************************************************************/
 
// 通过DAG配置
// Gradle有一个配置阶段和执行阶段。在配置阶段后,Gradle将会知道应执行的所有任务
// Gradle为你提供了一个“钩子”,以便利用这些信息。
// 例子:判断发布的任务是否在要执行的任务当中,根据该判断可以给任务中的一些变量指定不同的值
 
task debug << {
	println "the debug version is $version"
}
 
task release(dependsOn: debug) << {
	println "the release version is $version"
}
 
gradle.taskGraph.whenReady {
	it ->
		if (it.hasTask(release)) {
			version = 'beta1.0'
		} else {
			version = 'alpha1.0'
		}
}
 
// 执行 gradle -q release
/**********输出结果*************/
//the debug version is beta1.0
//the release version is beta1.0
/**********输出结果*************/
// 最重要的是whenReady在release任务执行之前就已经影响了release任务。
// 因为该方法是在解析任务后、执行任务前执行的
 
/********************************************************************/
 
// Java插件
// Java插件是基于合约的,这意味着插件已经给项目的许多方面定义了默认的参数,比如Java源文件的位置,
// 如果在项目中遵从这些合约,则写构建脚本时不需要在里面加入太多东西。
 
// 例子:一个基础的Java项目
apply plugin: 'java' // 加入该代码就可以使用Java插件
// 它会把Java插件加入到你的项目中,这意味着许多预定制的任务被自动加入到了你的项目里
// Gradle希望能在 src/main/java 找到你的源代码,在 src/test/java 找到测试代码,也就是说Gradle默认地
// 在这些路径里查找资源。另外,任何在 src/main/resources 目录下的文件都将被包含在JAR文件里,同时任何
// 在 src/test/resources 目录下的文件会被加入到 classpath 中以运行测试代码。所有的输出文件将会被创建
// 在构建目录里,JAR文件存放在 build/libs 文件夹里。
 
// 建立项目
// Java插件在你的项目里加入了许多任务。然而你只会用到其中的一小部分任务。最常用的任务是 build 任务,
// 它会建立你的项目。当你运行 gradle build 命令时,Gradle将会编译和测试你的代码,并且创建一个包含类
// 和资源的JAR文件。
// 执行 gradle build
/**********输出结果*************/
//:compileJava UP-TO-DATE
//:processResources UP-TO-DATE
//:classes UP-TO-DATE
//:jar
//:assemble
//:compileTestJava UP-TO-DATE
//:processTestResources UP-TO-DATE
//:testClasses UP-TO-DATE
//:test UP-TO-DATE
//:check UP-TO-DATE
//:build
//
//BUILD SUCCESSFUL
//
//Total time: 38.093 secs
/**********输出结果*************/
 
// 其余一些有用的任务是:
// clean
// 删除 build 生成的目录和所有生成的文件
 
// assemble
// 编译并打包你的代码,但是并不运行单元测试。其他插件会在这个任务里加入更多的东西。如你使用War插件,
// 这个任务将根据你的项目生成一个WAR文件。
 
// check
// 编译并测试你的代码。其他的插件会加入更多的检查步骤。如你使用checkstyle插件,这个任务将会运行Checkstyle来检查你的代码
 
/********************************************************************/
 
// 外部的依赖
// 通常,一个Java项目有许多外部的依赖,即是指外部的JAR文件。为了在项目里面引入这些JAR文件,你需要告诉Gradle
// 去哪里找它们。在Gradle中,JAR文件位于一个仓库中,这里的仓库类似于MAVEN的仓库,仓库可以被用来提取依赖,或者
// 放入一个依赖,或者两个皆可。如我们将使用开放的Maven仓库:
// 例子:加入Maven仓库
repositories {
	mavenCentral()
}
// Gradle是怎样找到那些外部依赖的文件的呢?Gradle会在一个repository(仓库)里找到这些文件。仓库其实就是文件的集合,
// 通过group,name,version整理分类。默认地,Gradle不提前定义任务仓库,在使用外部依赖之前,需要至少定义一个库。
// Gradle能解析好几种不同的仓库形式,如本地文件系统或者http,使用本地Ivy目录:
/*
repositories {
	ivy {
		url "../local-repo"
	}
}
// 使用远程Ivy仓库:
repositories {
	ivy {
		url "http://repo.mycompany.com/repo"
	}
}
*/
// 一个项目可以有好几个库,Gradle会根据依赖定义的顺序在各个库里寻找它们,在第一个库里找到了就不会再在第二个仓库里找它了
 
// 接下来让我们加入一些依赖。这里假设我们的项目在编译阶段有一些依赖:
// 例子:加入依赖
dependencies {
	// 引用一个外部依赖需要使用group,name和version属性。有一种简写形式,只使用一串字符串"group:name:version"
	// 如:'commons-collections:commons-collections:3.2'
	compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
	testCompile group: 'junit', name: 'junit', version: '4.+'
}
// 可以看到commons-collections被加入到了编译阶段,junit也被加入到了测试编译阶段。
// 这个构建脚本声明commons-collections 3.2最终被用来编译项目的源代码。言外之意是在运行阶段
// 需要commons-collections和它的依赖。构建脚本同样声明了需要junit >=4.0的版本来编译项目测试。
// 它告诉Gradle到Maven中央仓库里找任何需要的依赖。
 
// 依赖配置
// 在Gradle里,依赖可以组合成配置。Java插件定义了许多标准的配置:
// compile : 用来编译项目源代码的依赖
// runtime : 在运行时被生成的类使用的依赖。默认的,也包含了编译时的依赖。
 
/********************************************************************/
 
// 定制项目
// Java插件给项目加入了一些属性(property),这些属性已经被赋予了默认的值,已经足够来开始构建项目了。
// 如果你认为不合适,改变它们的值也是很简单的。看下面的例子,这里我们将指定Java项目的版本号,以及
// 我们所使用的Java的版本,我们同样也加入了一些属性在jar的清单里。
// 例子:定制 MANIFEST.MF文件
sourceCompatibility = 1.5 // Java的版本号
version = '1.0' // 项目的版本号
jar {
	manifest {
		attributes 'Implementation-Title': 'Gradle Quickstart', 'Implementation-Version': version
	}
}
// Java插件加入的任务是常规性的任务,准确地说,就如同它们在构建文件里声明的一样。这意味着你可以定制自己的任务。
// 如你可以设置一个任务的属性,在任务里加入行为,改变任务的依赖,或者完全重写一个任务,我们将撇嘴一个测试任务,
// 当测试执行的时候它会加入一个系统属性:
// 例子:测试阶段加入一个系统属性
test {
	systemProperties 'property': 'value'
}
// 哪些属性是可用的?
// 你可以使用 gradle properties 命令来列出项目的所有属性。这样你就可以看到Java插件加入的属性已经它们的默认值。
 
/********************************************************************/
 
// 发布JAR文件
// 通常JAR文件需要在某个地方发布。为了完成这一步你需要告诉Gradle在哪里发布JAR文件。在Gradle里,生成的文件比如JAR
// 文件将被发布到仓库里。在我们的例子里,我们将发布到一个本地的目录。你也可以发布到一个活多个远程的地点。
// 例子:发布JAR文件
uploadArchives {
	repositories {
		flatDir {
			dirs 'repos'
		}
	}
}
// 运行 gradle uploadArchives 命令来发布JAR文件,文件会发布到repos目录下,Gradle也会生成ivy.xml。
 
/********************************************************************/
 
// 多项目的Java构建
// 例子:多项目构建 - 分层布局
/*
multiproject/
	api/
	services/webservice/
	shared/
// 现在我们能有三个项目。项目的应用程序接口(API)产生一个JAR文件,这个文件将提供给用户,给用户
// 提供基于XML的网络服务。项目的网络服务是一个网络应用,它返回XML.shared目录,包含被api和
// webservice共享的代码
// 定义一个多项目构建
// 为了定义一个多项目构建,你需要创建一个设置文件。设置文件放在源代码的根目录,它指定要包含哪个项目。
// 它的名字必须叫做 settings.gradle 。在这个例子中,我们使用一个简单的分层布局。下面是对应的设置文件:
// 例子:多项目构建 - settings.gradle
include "shared", "api", "services:webservice", "services:shared"
*/
 
/********************************************************************/
 
// 通用配置
// 对于绝大多数多项目构建,有一些配置对所有项目都是常见的或者说是通用的。在我们的例子里,我们将在根项目
// 里定义一个这样的通用配置,使用一种叫做配置注入的技术。这里,根项目就像一个容器, subprojects 方法遍历
// 这个容器的所有元素并且注入指定的配置。通过这种方法,我们可以很容易的定义所有档案和通用依赖的内容清单:
// 例子:多项目构建 - 通用配置
subprojects {
	apply plugin: 'java'
	
	repositories {
		mavenCentral()
	}
	
	dependencies {
		testCompile 'junit: junit:4.11'
	}
	
	version = '1.0'
	
	jar {
		manifest.attributes provider: 'gradle'
	}
}
// 注意我们例子中,Java插件被应用到了每一个子项目中的plugin to each.
// 这意味着这些任务和属性都可以在子项目中被调用。所以你可以通过在根目录里运行 gradle build 命令编译、测试和JAR所有的项目
 
/********************************************************************/
 
// 项目之间的依赖
// 你可以在同一个构建里加入项目之间的依赖,如一个项目的JAR文件被用来编译另外一个项目。在api构建文件里
// 我们将加入一个由shared项目产生的JAR文件的依赖。由于这个依赖,Gradle将确保shared项目总数在api之前被构建。
// 例子:多项目构建 - 项目之间的依赖
/*
dependencies {
	compile project(':shared')
}
*/
/********************************************************************/
 
// 创建一个发行版本
// 多项目构建 - 发行文件
/*
task dist(type: Zip) {
	dependsOn spiJar
	from 'src/dist'
	into('libs') {
		from spiJar.archivePath
		from configurations.runtime
	}
}
artifacts {
	archives dist
}
*/
/********************************************************************/
 
// 多任务调用
task taskA << {
	println 'taskA'
}
 
task taskD << {
	println 'taskD'
}
 
task taskB(dependsOn: taskD) << {
	println 'taskB'
}
 
task taskC(dependsOn: [taskA, taskB]) << {
	println 'taskC'
}
 
// 执行 gradle -q taskC taskB // 执行多任务时,只需要顺序排列即可
/**********输出结果*************/
//taskA
//taskD
//taskB
//taskC
/**********输出结果*************/
// 由于taskC依赖于taskA和taskB,所以先执行taskA和taskB,并且每个任务仅能执行一次。
 
// 排除任务
// 执行 gradle -q taskC -x taskB // 用命令行选项 -x 来排除某些任务
/**********输出结果*************/
//taskA
//taskC
/**********输出结果*************/
// 可以看到即使taskC依赖于taskB,taskB也并没有被执行,所以taskB所依赖的taskD也不会被执行。
 
// 简化任务名
// 当你试图调用某个任务时,你并不需要输入任务的全名,只需要提供可以唯一区分出该任务的字符即可。
// 如上面的例子,要调用任务taskB时可以直接写 gradle -q tB
// 执行 gradle -q taskB
/**********输出结果*************/
//taskD
//taskB
/**********输出结果*************/
 
/********************************************************************/
 
// 失败后继续执行构建
// 默认情况下,只要有任务调用失败,Gradle就会中断执行,但后面任务潜藏的可能错误的地方就没法发现了。
// 所以可以使用 --continue 选项在一次调用中尽可能多的发现所有问题。
// 使用该选项Gradle会调用每一个任务以及它们依赖的任务,一旦某个任务执行失败,那么所有依赖该任务的
// 子任务都不会被执行。最后所有的错误信息都会被列出来。
 
/********************************************************************/
 
// 选择文件构建
// 调用 gradle 命令时,默认情况下总是会构建当前目录下的文件,可以使用 -b 参数选择其他目录的构建文件,
// 并且当你使用该参数时 settings.gradle 文件将不会生效。
// 例子:选择文件构建 subdir/myproject.gradle
/*
task world << {
	println "using build file '$buildFile.name' in '$buildFile.parentFile.name'."
}*/
// 执行 gradle -q -b subdir/myproject.gradle world
/**********输出结果*************/
// 理论上输出:using build file 'myproject.gradle' in 'subdir'
// 实际上报了错,但是未找到报错原因
/**********输出结果*************/
 
// 另外,你可以使用 -p 参数来指定构建的目录,例如在多项目构建中你可以用 -p 来替代 -b 参数
// 例子:选择构建目录
// 执行 gradle -q -p subdir world
/**********输出结果*************/
// 理论上输出:using build file 'myproject.gradle' in 'subdir'
// 实际上报了错,但是未找到报错原因
/**********输出结果*************/
// -b 参数用以指定脚本具体所在的位置,格式为 dirpwd/build.gradle
// -p 参数用以指定脚本目录即可
 
/********************************************************************/
 
// 获取构建信息
// Gradle提供了许多内置任务来收集构建信息。这些内置任务对于了解依赖结构以及解决问题都是很有帮助的。
 
// 可以在项目中用 description 属性来指定项目的描述信息
// 例子:为项目添加描述信息
description = 'The project is just a demo'
 
// 项目列表
// 执行 gradle projects 命令会为你列出子项目名称列表。
// 例子:收集项目信息
// 执行 gradle -q projects
/**********输出结果*************/
//
//------------------------------------------------------------
//Root project
//------------------------------------------------------------
//
//Root project 'aaa' - The project is just a demo
//No sub-projects
//
//To see a list of the tasks of a project, run gradle <project-path>:tasks
//For example, try running gradle :tasks
/**********输出结果*************/
 
/********************************************************************/
 
// 任务列表
// 可以通过为任务设置 group 属性和 description 属性来把这些信息展示到结果中
task hehehaha {
	group = 'groupA'
	description = 'this is a demo task who contains group and description'
	println 'hehehaha'
}
// 执行 gradle tasks 命令会列出项目中所有任务。这会显示项目中所有的默认任务以及每个任务的描述
//hehehaha
//------------------------------------------------------------
//All tasks runnable from root project - The project is just a demo
//------------------------------------------------------------
//
//Default tasks: default1, default2
//
//Build tasks
//-----------
//assemble - Assembles the outputs of this project.
//build - Assembles and tests this project.
//buildDependents - Assembles and tests this project and all projects that depend on it.
//buildNeeded - Assembles and tests this project and all projects it depends on.
//classes - Assembles main classes.
//clean - Deletes the build directory.
//jar - Assembles a jar archive containing the main classes.
//testClasses - Assembles test classes.
//
//Build Setup tasks
//-----------------
//init - Initializes a new Gradle build. [incubating]
//wrapper - Generates Gradle wrapper files. [incubating]
//
//Documentation tasks
//-------------------
//javadoc - Generates Javadoc API documentation for the main source code.
//
//GroupA tasks
//------------
//hehehaha - this is a demo task who contains group and description
//
//Help tasks
//----------
//buildEnvironment - Displays all buildscript dependencies declared in root project 'aaa'.
//components - Displays the components produced by root project 'aaa'. [incubating]
//dependencies - Displays all dependencies declared in root project 'aaa'.
//dependencyInsight - Displays the insight into a specific dependency in root project 'aaa'.
//help - Displays a help message.
//model - Displays the configuration model of root project 'aaa'. [incubating]
//projects - Displays the sub-projects of root project 'aaa'.
//properties - Displays the properties of root project 'aaa'.
//tasks - Displays the tasks runnable from root project 'aaa'.
//
//Upload tasks
//------------
//uploadArchives - Uploads all artifacts belonging to configuration ':archives'
//
//Verification tasks
//------------------
//check - Runs all checks.
//test - Runs the unit tests.
//
//Other tasks
//-----------
//default1
//default2
//dog
//fish
//hello
//hello2
//myTask
//other
//printTaskProperties
//release
//task0
//task1
//taskC
//taskP
//zhangsan
//
//Rules
//-----
//Pattern: clean<TaskName>: Cleans the output files of a task.
//Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
//Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.
//
//To see all tasks and more detail, run gradle tasks --all
//
//To see more detail about a task, run gradle help --task <task>
 
// 当然也可以用 -all 参数来收集更多任务信息。这会列出项目中所有任务以及任务之间的依赖关系。
 
/********************************************************************/
 
// 获取任务具体信息
// 执行 gradle help --task someTask 可以显示指定任务的详细信息。或者多项目构建中相同任务名称的所有任务的信息
// 例子:获取任务帮助
// 执行 gradle -q help --task hehehaha
/**********输出结果*************/
//hehehaha
//Detailed task information for hehehaha
//
//Path
//     :hehehaha
//
//Type
//     Task (org.gradle.api.Task)
//
//Description
//     this is a demo task who contains group and description
//
//Group
//     groupA
/**********输出结果*************/
// 这些结果包含了任务的路径、类型以及描述信息等
 
/********************************************************************/
 
// 获取依赖列表
// 执行 gradle dependencies 命令会列出项目的依赖列表,所有依赖会根据任务区分,以树型结构展示
// 如: gradle -q dependencies 会列出项目中所有的依赖列表
// gradle -q dependencies 子项目:dependencies 会列出指定子项目的依赖列表
 
/********************************************************************/
 
// 获取项目属性列表
// 执行 gradle properties 可以获取项目所有属性列表
 
/********************************************************************/
 
// 构建日志
// --profile 参数可以收集一些构建期间的信息并保存到 build/reports/profile 目录下,并且会以构建时间命名这些文件.
 
/********************************************************************/
 
// 编写构建脚本
 
// 项目API
// 前面使用了apply()方法,这个方法从哪里来的呢?我们之前说过Gradle在构建脚本中定义了一个项目,
// 对于构建脚本中每个项目,Gradle都创建了一个 Project 类型的对象用来关联此项目。当构建脚本执行时,它会去配置所关联的 Project 对象
// ①构建脚本中每个被调用的方法(未在构建脚本中定义的方法)都被委托给当前 Project 对象(使用 Project 对象引用方法)
// ②构建脚本中每个被操作的属性(未在构建脚本中定义的属性)都被委托给当前 Project 对象(使用 Project 对象引用属性)
// 例子:操作项目对象的属性
println name
println project.name
// 执行 gradle -q check
/**********输出结果*************/
//hehehaha // 该任务为何总是被输出???
//aaa
//aaa
/**********输出结果*************/
// 可以看到两个println语句都输出了相同的属性,第一个输出使用的是自动委托,因为当前属性并没有在构建脚本中定义。
// 另一个语句使用了项目的一个属性,该属性在任务构建脚本中都可用,它的返回值是被关联的 Project 对象的目录名。
// 当你定义了一个属性或者一个方法和 Project 对象的某个成员的属性相同时,应用使用第二种方法指明是哪个项目的。
 
/********************************************************************/
 
// 标准项目属性
// Project 对象提供了一些标准的属性,在构建脚本中可以很方便的使用他们。
//   Name         Type        Default Value
// project      Project      Project实例对象
// name         String       项目目录的名称
// path         String       项目的绝对路径
// description  String       项目描述
// projectDir   File         包含构建脚本的目录
// build        File         projectDir/build
// group        Object       未具体说明
// version      Object       未具体说明
// ant          AntBuilder   Ant实例对象
// 建议:构建脚本只是个很简单的Groovy代码,不过它会再调用Gradle API,Project接口通过调用Gradle API让我们
// 可以操作任何事情,因此如果想知道哪个标签(tags)可以在构建脚本中使用,可以翻阅Project接口的说明文档
 
/********************************************************************/
 
// 脚本API
// 当Gradle执行一个脚本时,它会将这个脚本编译为实现了Script的类,也就是说所有的属性和方法都是在Script接口中
// 声明的,由于你的脚本实现了Script接口,所以你可以在自己的脚本中使用它们。
 
// 声明变量
// 在Gradle构建脚本中有两种类型的变量可以声明:局部变量(local)和扩展属性(extra).
 
// 局部变量
// 局部变量使用关键字def来声明,其只在声明它的地方可见,局部变量是Groovy语言的一个基本特性
// 例子:使用局部变量
def dest = 'dest'
task copy(type: Copy) { // 拷贝source目录下的文件到dest目录下,前提:source文件夹必须存在,dest文件夹可以不存在。
	from "source"
	into dest
}
 
// 扩展属性
// 在Gradle领域模型中所有被增强的对象能够拥有自己定义的属性,这包括但不限于projects、tasks、sourceSets。
// Project对象可以添加、读取、更改扩展的属性,另外使用ext扩展块儿可以一次添加多个属性
// 例子:使用扩展属性
apply plugin: 'java'
 
ext {
	springVersion = "3.1.0.Release"
	emailNofification = "build@master.org"
}
 
sourceSets.all {
	ext.purpose = null
}
 
sourceSets {
	main {
		purpose = "production"
	}
	test {
		purpose = "test"
	}
	plugin {
		purpose = "production"
	}
}
 
task printProperties << {
	println springVersion
	println emailNofification
	sourceSets.matching {
		it.purpose == "production"
	}.each {
		println it.name
		}
}
// 执行 gradle -q printProperties
/**********输出结果*************/
//3.1.0.Release
//build@master.org
//main
//plugin
/**********输出结果*************/
// 在上面的例子中,一个ext扩展块向Project对象添加了两个扩展属性。名为purpose的属性被添加到每个sourceSets,
// 然后设置 ext.purpose 等于 null。当这些扩展属性被添加后,它们就像预定义的属性一样可以被读取、修改。
 
/********************************************************************/
 
// 深入了解tasks
 
// 定义tasks
// 之前学习了定义任务的形式,这些出现了些小的变化来适应某些特殊的情况,对比发现任务名被括号括起来了,
// 因为之前定义简单任务的形式在表达式里是不起作用的。
// 例子:定义tasks
task (helloT) << {
	println "helloT"
}
 
task(copyT, type: Copy) {
	from(file('srcDir'))
	into(buildDir)
}
// 你也可以使用strings来定义任务的名字:
task('helloTK') << {
	println "helloTK"
}
 
task('copyTK', type: Copy) {
	from(file('srcDir'))
	into(buildDir)
}
// 还有另外一种语法形式来定义任务,更加直观:
tasks.create(name: 'helloTKs') << {
	println "helloTKs"
}
 
tasks.create(name: 'copyTKs', type: Copy) {
	from(file('srcDir'))
	into(buildDir)
}
// 这里实际上我们把任务加入到 tasks collection 中了。
 
/********************************************************************/
 
// 定位tasks
// 你经常需要定位你定义的tasks,如:为了配置它们或者使用它们作为依赖。有许多种方式都可以来实现定位,
// 首先每一个任务都必须是一个Project的有效属性,使用任务名来作为属性名:
// 例子:通过属性获取tasks
task helloP
println helloP.name
println project.helloP.name
 
// tasks也可以通过 tasks collection 来得到
// 例子:通过 tasks collection 获取tasks
task helloL
println tasks.helloL.name
println tasks['helloL'].name
 
// 在项目中使用tasks.getByPath()得到task的路径来访问任务。可以通过任务名、相对路径、绝对路径来调用getByPath()方法
// 通过路径获取tasks
task helloA
println tasks.getByPath('helloA').path
println tasks.getByPath(':helloA').path
 
/********************************************************************/
 
// 配置tasks
// 举一个例子,让我们看一看Gradle自带的Copy task,为了创建一个Copy task,你需要在你的构建脚本里先声明它:
// 创建一个Copy task
task myCopy(type: Copy)
// 它创建了一个没有默认行为的Copy task,这个task可以通过它的API来配置,接下来例子展示了不同的实现方法
// 补充说明一下,这个task的名字是“myCopy”,但是它是"Copy"类型的。
// 例子:配置一个任务 - 不同的方法
//Copy myCopy = task(myCopy, type: Copy)
//myCopy.from 'resources'
//myCopy.into 'target'
//myCopy.include('**/*.txt', '**/*.xml', '**/*.properties')
// 这跟我们通过Java配置对象是一样的形式。但是你每次都必须在语句里重复上下文(myCopy)。这种方式可能读起来不是那么漂亮
 
// 下面一种方式就解决了这个问题,是公认的最具可读性的方式
// 配置一个任务 - 通过闭包 closure
//task myCopy(type: Copy)
 
//myCopy {
//	from 'resources'
//	into 'target'
//	include('**/*.txt', '**/*.xml', '**/*.properties')
//}
// 上面例子中的第三行是tasks.getByName()方法的一个简洁的写法。特别要注意的是如果你通过闭包的形式来
// 实现getByName()方法,这个闭包会在task配置的时候执行而不是在task运行的时候执行
 
// 你也可以直接在定义task时使用闭包
// 例子:通过定义一个任务
//task copy(type: Copy) {
//	from 'resources'
//	into 'target'
//	include('**/*.txt', '**/*.xml', '**/*.properties')
//}
// 请不要忘了构建的各个阶段
// 一个任务有配置和动作,当使用 << 时,你只是简单的使用了捷径定义了动作。定义在配置区域的代码只会在
// 构建的配置阶段执行
 
/********************************************************************/
 
// 给task加入依赖
// 有许多种依赖的方式,之前已经学习了如何使用任务的名称定义依赖。任务名称可以指向同一个项目里的任务,
// 也可以是其他项目里的任务。为了指向其他项目必须在任务的名称前加入项目的路径。
// 例子:从另外一个项目给任务加入依赖(给projectA:taskX 加入依赖projectB:taskY)
/*
project('projectA') {
	task taskX(dependsOn: ':projectB:taskY') << {
		println 'taskX'
	}
}
project('projectB') {
	task taskY << {
		println 'taskY'
	}
}*/
 
// 除了使用任务名也可以使用对象定义依赖
// 例子:通过任务对象加入依赖
/*
task taskX << {
	println 'taskX'
}
task taskY << {
	println 'taskY'
}
taskX.dependsOn taskY*/
 
// 更加先进的用法是可以通过闭包定义一个任务的依赖,闭包只能返回一个单独的Task或者Task对象的collection,
// 这些返回的任务就将被当做依赖。接下来的例子给taskX加入了一个复杂的依赖,所有以lib开头的任务都将在taskX
// 之前执行:
// 例子:通过闭包加入依赖
/*
task taskX << {
	println 'taskX'
}
taskX.dependsOn {
	tasks.findAll {
		task -> task.name.startsWith('lib')
	}
}
task lib1 << {
	println 'lib1'
}
task lib2 << {
	println 'lib2'
}
task notALIb << {
	println 'notALIb'
}*/
 
/********************************************************************/
 
// 给task排序
// 在某些情况下,我们希望能控制任务的执行顺序,这种控制并不是像之前那样加入依赖关系,最主要的区别是
// 我们设定的排序规则不会影响那些要被执行的任务,只是影响执行的顺序本身
// 目前有两种可用的排序规则:"must run after" 和 "should run after"
// 当使用 "must run after" 时即意味着无论taskA和taskB是否将要运行,taskB必须在taskA之后运行
// taskB.mustRunAfter(taskA)
 
// "should run after" 规则其实和 "must run after" 很像,只是没有那么的严格,在两种情况下它会被忽略:
// 1.使用规则来阐述一个循环的执行
// 2.当并行执行且一个任务的所有依赖除了"should run after" 任务其余都满足了,那么这个任务无论它的
// "should run after" 依赖是否执行,它都可以执行
// 例子:加入must run after
task taskX << {
	println 'taskX'
}
task taskY << {
	println 'taskY'
}
//taskY.mustRunAfter taskX
 
// 例子:加入should run after
taskY.shouldRunAfter taskX
// 执行 gradle -q taskY taskX
/**********输出结果*************/
//taskX
//taskY
/**********输出结果*************/
// 任务排序不影响任务的执行,即taskX和taskY是可以单独执行的
 
// 例子:should run after任务的忽略
task taskZ << {
	println 'taskZ'
}
taskX.dependsOn taskY
taskY.dependsOn taskZ
taskZ.shouldRunAfter taskX
// 执行 gradle -q taskX
/**********输出结果*************/
//taskZ
//taskY
//taskX
/**********输出结果*************/
 
/********************************************************************/
 
// 替换tasks
// 例子:复写一个任务
task heiheihaha << {
	println 'hi'
}
task heiheihaha(overwrite: true) << {
	println 'i am a new one method'
}
// 执行 gradle -q heiheihaha
/**********输出结果*************/
//i am a new one method
/**********输出结果*************/
// 这种方式将用你自己定义的任务替换之前定义的任务,因为它使用了同样的名字,故重新定义时
// 必须设置overwrite属性为true。
 
/********************************************************************/
 
// 跳过tasks
// Gradle提供了好几种跳过一个任务的方法
// 1.使用判断条件
// 可以使用onlyIf()方法来为一个任务加入判断条件。它和Java里面的if语句一样。
// 例子:使用判断条件跳过一个任务
task skipTask << {
	println 'hello skip me'
}
skipTask.onlyIf {
	!project.hasProperty('skipTrue')
}
// 执行 gradle skipTask -PskipTrue// 执行skipTask任务,并给该任务设置属性skipTrue
/**********输出结果*************/
//:skipTask SKIPPED
//
//BUILD SUCCESSFUL
//
//Total time: 42.264 secs
/**********输出结果*************/
 
// 2.使用 StopExecutionException
// 如果想要跳过一个任务的逻辑并不能被判断条件通过表达式表达出来,此时可以使用StopExecutionException.
// 如果这个异常是被一个任务要执行的动作抛出的,这个动作之后的执行以及所有紧跟它的动作都会被跳过。
// 例子:通过 StopExecutionException 跳过任务
task compile << {
	println 'we are doing the compile'
}
compile.doFirst {
	if(true) {
		throw new StopExecutionException()
	}
}
task myTaskD(dependsOn: 'compile') << {
	println 'i am not affected'
}
// 执行 gradle -q myTaskD
/**********输出结果*************/
//i am not affected
/**********输出结果*************/
 
// 激活和注销tasks
// 每一个任务都有一个已经激活的标记,这个标记默认值为真,将它设置为假,那它的任何动作都不会被执行了
// 例子:激活和注销tasks
task go << {
	println 'this should not be printed if the task is disabled'
}
go.enabled = false
// 执行 gradle go
/**********输出结果*************/
//:go SKIPPED
//
//BUILD SUCCESSFUL
//
//Total time: 39.705 secs
/**********输出结果*************/
 
/********************************************************************/
 
// task规则
// 有时候也想要一个任务的行为是基于已经定义好的取值范围或者特定规则
// 例子:任务规则
tasks.addRule("Pattern: ping<ID>") {
	String taskName ->
		if(taskName.startsWith("ping")) {
			task(taskName) << {
				println "Pinging: " + (taskName - 'ping') // 字符串减操作
			}
		}
}
// 执行 gradle -q pingServer1
/**********输出结果*************/
//Pinging: Server1
/**********输出结果*************/
// 这里的 String 参数就是用来定义规则的。
// 规则并不只是在通过命令行使用任务的时候执行,也可以基于规则来创建依赖关系:
// 例子:基于规则的任务依赖
task groupPing {
	dependsOn pingServer2, pingServer3
}
// 执行 gradle -q groupPing
/**********输出结果*************/
//Pinging: Server2
//Pinging: Server3
/**********输出结果*************/
// 如果你运行"gradle -q tasks",并不能找到名为"pingServer1"或者"pingServer2"的任务,但是这个脚本
// 仍然会执行这些任务。
 
/********************************************************************/
 
// 终止时tasks
// 终止时tasks指的是一个无论运行结果如何最后都会被执行的任务
// 例子:加入一个终止时任务
task taskR << {
	println 'taskR'
}
task taskS << {
	println 'taskS'
}
taskR.finalizedBy taskS
// 执行 gradle -q taskR
/**********输出结果*************/
//taskR
//taskS
/**********输出结果*************/
// 即使执行终止时任务之前的任务失败了,终止时任务仍会继续执行
// 例子:任务失败时继续执行
/*
task taskE << {
	println 'taskE'
	throw new RuntimeException()
}
task taskF << {
	println 'taskF'
}
taskE.finalizedBy taskF*/
// 执行 gradle -q taskE
/**********输出结果*************/
//taskE
//taskF
/**********输出结果*************/
 
/********************************************************************/
 
// 文件集合
// 文件集合表示一组文件,Gradle使用 FileCollection 接口表示文件集合,Gradle API 中的许多项目都实现了
// 这个接口,如dependency configurations
// 获取 FileCollection 实例的一种方法是使用 Project.files() 方法,你可以传递任何数量的对象参数,这个
// 方法能将你传递的对象集合转换为一组文件对象。.files() 方法接收任何类型对象参数,每一个 file() 方法
// 都依赖于项目目录。files() 方法也接收 collections、iterables、maps 和 arrays 类型参数,这些参数的
// 内容会被解析,然后被转换为文件对象
// 例子:创建文件集合
//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
//File file = collection.singleFile
//File file2 = collection as File
// 增加和减少文件集合
//def union = collection + files('src/file3.txt')
//def different = collection - files('src/file3.txt')
 
// 也可以向 files() 方法传递一个闭包或者可回调的实例参数。当查询集合的内容时就会调用它,
// 然后将返回值转换为一些文件实例
// 例子:实现一个文件集合
/*
task list << {
	File srcDir
	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
		}
}*/
 
/********************************************************************/
 
// 文件树
 
// 文件树是一个按照层次结构分布的文件集合,如一个文件树可以代表一个目录树结构或者一个Zip压缩文件的内容。
// 它被抽象为 FileTree 结构,FileTree 继承自 FileCollection ,所以可以像处理文件集合那样处理文件树,
// Gradle有些对象实现了 FileTree 接口,使用 Project.fileTree() 方法可以得到 FileTree 的实例,它会创建一个
// 基于基准目录的对象,然后视需要使用一些 Ant-style 的包含和去除规则
// 例子:创建文件树
FileTree tree = fileTree(dir: 'src/main') // 以一个基准目录创建一个文件树
// 添加包含和排除规则
tree.include '**/*.java'
tree.exclude '**/Abstract*'
// 使用路径创建一个树
tree = fileTree('src').include('**/*.java')
// 使用闭包创建一个树
tree = fileTree('src') {
	include '**/*.java'
}
// 使用map创建一个树
tree = fileTree(dir: 'src', include: '**/*.java')
tree = fileTree(dir: 'src', includes: ['**/*.java', '**/*.xml'])
tree = fileTree(dir: 'src', include: '**/*.java', exclude: '**/*test*/**')
 
// 例子:使用文件树
//tree.each {
//	File file -> println file
//}
 
//FileTree filtered = tree.matching {
//	include 'org/gradle/api/**'
//}
 
//FileTree sum = tree + fileTree(dir: 'src/test')
 
//tree.visit { // 访问文件的元素
//	element -> println "$element.relativePath => $element.file"
//}
 
/********************************************************************/
 
// 使用一个归档文件的内容作为文件树
// 可以使用Zip或者Tar等压缩文件的内容作为文件树,Project.zipTree() 和 Project.tarTree() 方法返回一个
// FileTree 实例,可以像使用其他文件树或者文件集合一样使用它
// 例子:使用压缩文档作为文件树
FileTree zip = zipTree('someFile.zip')
FileTree tar = tarTree('someFile.tar')
// tar tree 能够根据文件扩展名得到压缩方式,如果想明确指定压缩方式,可以使用下面方法:
FileTree someTar = tarTree(resources.gzip('someTar.ext'))
 
/********************************************************************/
 
// 指定一组输入文件
// 在Gradle中有一些对象的某些属性可以接收一组输入文件,如JavaCompile任务中有一个source属性,
// 它定义了编译的源文件,你可以设置这个属性的值,只要files()方法支持。这意味着可以使用File、
// String、collection、FileCollection甚至是使用一个闭包去设置属性的值
// 例子:指定文件
//compile {
//	source = file('src/main/java') // 使用一个File对象设置源文件
//	source = 'src/main/java' // 使用一个字符路径设置源文件
//	source = ['src/main/java', '../shared/java'] // 使用一个集合设置多个源文件 ../父级目录 ./当前目录
//	source = fileTree(dir: 'src/main/java').matching {
//		include 'org/gradle/api/**'
//	} // 使用FileCollection或者FileTree设置源文件
//	source = { // 使用一个闭包设置源文件
//		file('src').listFiles.findAll {
//			it.name.endsWith('.zip')
//		}.collect {
//			zipTree(it)
//			}
//	}
//}
 
/********************************************************************/
 
// 复制文件
// 可以使用复制任务(Copy)去复制文件,复制任务扩展性很强,能够过滤复制文件的内容、映射文件名
// 使用复制任务时需要提供想要复制的源文件和一个目标目录,如果要指定复制文件时的转换方式,可以
// 使用复制规则,复制规则被 CopySpec 接口抽象,复制任务实现了该接口,使用 CopySpec.from() 方法
// 指定源文件,使用 CopySpec.into() 方法指定目标目录
// 例子:使用复制任务复制文件
task copyTask(type: Copy) {
	from 'src/main/webapp'
	into 'build/explodedWar'
}
// from() 方法接收任何 files() 方法支持的参数,当参数被解析为一个目录时,在该目录下的任何文件都会
// 被递归地复制到目标目录(但不是目录本身),当一个参数解析为一个文件时,该文件被复制到目标目录中;
// 当参数被解析为一个不存在的文件时,该参数会被忽略。如果这个参数是一个任务,任务的输出文件会被复制,
// 然后这个任务会被自动添加为复制任务的依赖。
// 例子:指定复制任务的源文件和目标目录
/*
task anotherCopyTask(type: Copy) {
	from 'src/main/webapp' // 复制 src/main/webapp 目录下的所有文件
	from 'src/staging/index.html' // 复制一个单独文件
	from copyTask // 复制一个任务输出的文件
	from copyTaskWithPatterns.outputs // 显式使用任务的 outputs 属性复制任务的输出文件
	from zipTree('src/main/assets.zip') // 复制一个zip压缩文件的内容
	into { // 最后指定目标目录
		getDestDir()
	}
}*/
 
// 例子:选择要复制的文件
//task copyTaskWithPatterns(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')
//	}
//}
 
// 也可以使用 Project.copy() 方法复制文件,它的工作方式有一些限制,首先该方法不是增量的,
// 其次,当一个任务被用作复制源时,copy() 方法不能够实现任务依赖,因为它是一个普通的方法
// 不是一个任务。因此,如果使用copy()方法作为一个任务的一部分功能,需要显式的声明所有的
// 输入和输出以确保获得正确的结果。
// 例子:不使用最新检查方式下用copy()方法复制文件
task copyMethod << {
	copy {
		from 'src/main/webapp'
		into 'build/explodedWar'
		include '**/*.html'
		include '**/*.jsp'
	}
}
 
// 例子:使用最新的检查方式下用copy()方法复制文件
task copyMethodWithExplicitDependencies {
	inputs.file copyTask // 对输入做最新检查,添加 copyTask 作为依赖
	outputs.dir 'some-dir' // 对输出做最新检查
	doLast {
		copy {
			from copyTask
			into 'some-dir'
		}
	}
}
 
// 建议尽可能的使用复制任务,因为它支持增量化的构建和任务依赖推理,而不需要去额外的费力处理这些。
 
// 重命名文件
// 例子:在复制时重命名文件
task rename(type: Copy) {
	from 'src/main/webapp'
	into 'build/explodedWar'
	rename { // 使用闭包映射文件名
		String fileName -> fileName.replace('-staging-', '')
	}
	// 使用正则表达式映射文件名
	rename '(.+)-staging-(.+)', '$1$2'
	rename (/(.+)-staging-(.+)/, '$1$2')
}
 
// 过滤文件
// 例子:在复制时过滤文件
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.1')
    expand(project.properties)
	
    // 使用 Ant 提供的过滤器
    filter(FixCrLfFilter)
    filter(ReplaceTokens, tokens: [copyright: '2009', version: '2.3.1'])
	
    // 用一个闭包来过滤每一行
    filter {
		String line -> "[$line]"
    }
	
    // 使用闭包来删除行
    filter {
		String line -> line.startsWith('-') ? null : line
    }
}
// 在源文件中扩展和过滤操作都会查找的某个标准 token ,如果它的名字是 tokenName,它的格式
// 应该类似于@tokenName@
 
// 使用 CopySpec 类
// 复制规范来自于层次结构,一个复制规范继承其目标路径、包含模式、排除模式、复制操作、名称映射和过滤器
// 例子:嵌套复制规范
task nestedSpecs(type: Copy) {
	into 'build/explodedWar'
	exclude '**/*staging*'
	from('src/dist') {
		include '**/*.html'
	}
	into('libs') {
		from configurations.runtime
	}
}
 
/********************************************************************/
 
// 使用同步任务
// 同步任务(Sync)继承自复制任务(Copy),执行它时会复制源文件到目标目录中,然后从目标目录中删除所有非复制的文件,
// 这种方式非常有用,比如安装一个应用、创建一个文档的副本或者维护项目的依赖关系副本
// 例子:使用Sync任务复制依赖关系
task libs(type: Sync) { // 维护 build/libs 目录下项目在运行时的依赖
	from configurations.runtime
	into "$buildDir/libs"
}
 
/********************************************************************/
 
// 创建归档文件
// 一个项目可以有很多JAR文件,可以向项目中添加WAR、ZIP和TAR等文档,使用归档任务可以创建这些文档。
// 例子:创建一个ZIP文档
/*
apply plugin: 'java'
task zip(type: Zip) {
	from 'src/dist'
	into('libs') {
		from configurations.runtime
	}
}*/
// 所有归档任务的工作机制和复制任务相同,它们都实现了CopySpec接口
 
// 重命名文档
// projectName-version.type 格式用来生成文档名
// 例子:创建压缩文档
apply plugin: 'java'
version = 1.0
task myZip(type: Zip) {
	from 'somedir'
}
println myZip.archiveName
println relativePath(myZip.destinationDir)
println relativePath(myZip.archivePath)
// 执行 gradle -q myZip
/**********输出结果*************/
//aaa-1.0.zip // aaa是项目名
//build\distributions
//build\distributions\aaa-1.0.zip
/**********输出结果*************/
// 上面例子使用一个名为 myZip 的Zip归档任务生成名称为 aaa-1.0.zip 的Zip文档。
// 可以通过设置项目属性 archivesBaseName 的值来修改生成文档的默认名。
// 归档任务中有些属性可以配置
//  属性名           类型                默认值                                                   描述
// archiveName      String      baseName-appendix-version-classifier.extension,如果其中          归档文件的基本文件名
//                              任何一个都是空的,则不会添加名称
// archivePath      File        destinationDir/archiveName                                       生成归档文件的绝对路径
// destinationDir   File        取决于文档类型,JAR和WAR使用project.buildDir/distributions       归档文件的目录
//                              ZIP和TAR使用project.buildDir/libraries
// baseName         String      project.name                                                     归档文件名的基础部分
// appendix         String      null                                                             归档文件名的附加部分
// version          String      project.version                                                  归档文件名的版本部分
// classifier       String      null                                                             归档文件名的分类部分
// extension        String      取决于文档类型和压缩类型:zip、jar、war、tar等                   归档文件的扩展名
task myZip1(type: Zip) {
	from 'somedir'
	baseName = 'customName'
}
println myZip1.archiveName
// 执行 gradle -q myZip1
/**********输出结果*************/
//customName-1.0.zip
/**********输出结果*************/
 
// 例子:配置归档任务 - 附加其他后缀
archivesBaseName = 'gradle'
version = 1.1
task myZip2(type: Zip) {
	appendix = 'wrapper'
	classifier = 'src'
	from 'somedir'
}
println myZip2.archiveName
// 执行 gradle -q myZip2
/**********输出结果*************/
//gradle-wrapper-1.1-src.zip
/**********输出结果*************/

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值