转载请注明出处:http://blog.csdn.net/lastsweetop/article/details/79038161
构建的大部分工作是基于文件的,Gradle提供了一些api和概念来帮助你来进行操作
检索文件
你可以通过以项目目录为baseDir的相对路径来检索文件:
//相对路径
File configFile = file 'src/config.cson'
//绝对路径
configFile = file configFile.absolutePath
//使用相对路径的文件对象
configFile = file new File('src/config.cson')
//使用相对路径的java.nio.file.Path对象
configFile = file Paths.get('src', 'config.cson')
//使用绝对路径的java.nio.file.Path对象
configFile = file Paths.get(System.getProperty('user.home')).resolve('global-config.cson')
你可以传任意对象给file()
方法,都会被转为一个绝对路径的File
对象,通常传入的是String,File或者Path的实例。如果路径的是绝对地址,那么就会直接创建绝对路径的文件,如果路径是相对路径,那么就将项目目录作为baseDir来转换成绝对路径的文件。还要注意一点,file()
方法不会接收标准的URL,比如file:/some/path.xml
要注意的一点是new File(somePath)
是基于当前工作目录的,而file()
是基于项目目录的,具体怎么选择看你自己了。
File collections
file collection
仅仅是一组文件集合,它的代表是FileCollection
接口,Gradle的API中的很多对象都实现了这个接口,比如dependency configurations。
可以通过Project.files(java.lang.Object[])
方法来获取FileCollection
实例,方法可以传入任意数量的对象,然后把这些对象转换成一组File
对象。files()
可以接受任意类型的参数,就单个参数来说类似于file()
方法,除此之外你可以传集合,迭代器,map和数组,它们都会解包成多个的File
文件。
FileCollection fileCollection = files('src/file1.txt',
new File('src/file2.txt'),
['src/file3.txt', 'src/file4.txt'],
Paths.get('src', 'file5.txt'))
file collection
是可迭代的,并且可以用as
操作符,你也可以通过+
操作符添加文件,或者用-
操作符来移除文件,如下面的例子所示:
//迭代fileCollection
fileCollection.each {
// println it.name
}
//把fileCollection转化为其他类型
Set set = fileCollection.files
Set set1 = fileCollection as Set
List list = fileCollection as List
String path = fileCollection.asPath
File file=fileCollection.singleFile
File file1=fileCollection as File
//添加或者移除fileCollection元素
fileCollection -= files('src/file4.txt')
fileCollection += files('src/file4.txt')
你还可以给files()
方法传个闭包或者Callable
的实例,当这个集合的上下文被调用时,闭包和Callable的返回值就会转换为一组File实例,它的返回值可以是任何可以传给files()
的类型。
task listSrc {
doLast {
File srcDir
fileCollection = files {
srcDir.listFiles()
}
srcDir = file('src')
println "contents of $srcDir.name"
fileCollection.collect {
relativePath(it)
}.sort().each {
println it
}
srcDir = file('src2')
println "contents of $srcDir.name"
fileCollection.collect {
relativePath(it)
}.sort().each {
println it
}
}
}
然后执行任务
± % gradle listSrc -q !1846
contents of src
src/config.cson
src/file1.txt
src/file2.txt
src/file3.txt
src/file4.txt
src/file5.txt
contents of src2
src2/dir1
还有一些其他的你可以传给files()
的类型:
FileCollection
把FileCollection解包成多个File实例并且把它们包含到fileCollection
中Task
任务的输出文件列表被包含到fileCollection
中TaskOutputs
TaskOutputs
的输出文件被包含到fileCollection
中
一定要注意的一点是fileCollection
是惰性求值的,意味着你可以先定义一个fileCollection
,然后在集合中的文件在将来使用的时候再进行创建并使用。
File trees
file tree
是层级结构排列的文件集合。比如,它可以表示目录树也可以表示ZIP文件的内容。它的代表是FileTree
接口,这个接口是从FileCollection
接口继承的,因此你可以像使用FileCollection
那样使用它,Gradle中的很多对象都实现了这个接口,比如source sets
获得FileTree
实例的一种方式就是通过Project.fileTree(java.util.Map)
方法,它可以使用一个baseDir创建FileTree
,还可以使用ant模式include
和exclude
进行操作。
//使用一个baseDir来创建fileTree
FileTree tree = fileTree(dir: 'src')
tree.include '**/*.cson'
tree.exclude '**/*.txt'
//使用path创建fileTree
tree = fileTree('src').include('**/*.cson')
//使用闭包创建fileTree
tree = fileTree('src') {
include '**/*.txt'
}
//使用map创建fileTree
tree = fileTree dir: 'src', include: '**/*.cson'
tree = fileTree dir: 'src', includes: ['**/*.cson', '**/*.txt']
tree = fileTree dir: 'src', include: '**/*.cson', exclude: '**/*.txt'
你可以像操作file collection
一样操作file tree
,你还可以通过visit访问它,或者通过模式来选择它的子层file tree
task treeView {
doLast {
tree = fileTree('src2')
//tree的迭代
tree.each {
println it
}
//通过pattern找到子层的tree
def subtree = tree.matching {
include '**/*.txt'
}
//合并两个tree
tree+=fileTree 'src'
//访问tree的元素
tree.visit {
println "$it.relativePath => $it.file"
}
}
}
注意,file tree
会默认exclude
掉一些文件:
**/*~
**/#*#
**/.#*
**/%*%
**/._*
**/CVS
**/CVS/**
**/.cvsignore
**/SCCS
**/SCCS/**
**/vssver.scc
**/.svn
**/.svn/**
**/.DS_Store
**/.git
**/.git/**
**/.gitattributes
**/.gitignore
**/.gitmodules
**/.hg
**/.hg/**
**/.hgignore
**/.hgsub
**/.hgsubstate
**/.hgtags
**/.bzr
**/.bzr/**
**/.bzrignore
用归档文件的内容创建fileTree
你可以使用归档文件创建fileTree,比如zip,tar文件,分别对应Project.zipTree(java.lang.Object)
和Project.tarTree(java.lang.Object)
,这两个方法将返回的FileTree
实例可以像其他file tree
和file collection
那样使用,比如你可以通过复制FileTree
的内容来实现解压,还可以合并归档文件。
task archiveView {
doLast {
//使用zip文件创建FileTree
tree = zipTree('archive/CineGIF.zip')
//使用tar文件创建FileTree
tree = tarTree('someFile.tar')
//有时候你需要先将gz先解压成tar格式
tree=tarTree(resources.gzip('archive/tools.tar.gz'))
}
}
指定输入文件集合
在Gradle中有很多对象都有可以接收一组输入的属性。比如JavaCompile
任务的source
属性,它定义了可以需要被编译的源文件。你可以使用上面的不同参数file()
方法那样来设置这个属性的值,这意味着你可以使用的参数有文件,字符串,FileCollection,还有闭包。
还有一个和属性同名的方法source
一样也可以使用,和files()
方法一样使用
使用属性指定
//使用文件
compileJava {
source = file('src/main/java2')
}
//使用字符串
compileJava {
source = 'src/main/java2'
}
//使用集合
compileJava {
source = ['src/main/java2', 'src2']
}
//使用FileCollection或者FileTree
compileJava {
source = fileTree('src/main/').matching {
include 'java2/*'
}
}
//使用闭包
compileJava {
source = {
file('build').listFiles().findAll {
it.name.endsWith('zip')
}.collect { zipTree(it) }
}
}
使用方法指定
compileJava {
source 'src/main/java2', 'src/main/groovy'
source 'src/main/java2'
source fileTree('src/main/').matching {
include 'java2/*'
}
}
复制文件
你可以使用Copy
任务来复制文件,而且非常灵活,你可以使用过滤器来过滤需要复制的文件,映射文件的名字。
为了使用Copy
任务,你需要指定需要复制的一组文件,并且指定需要复制到的目录,你还可以指定在复制文件时如何进行转换,你可以使用Copy规范来实现他们,Copy规范的代表是CopySpec
接口,Copy
任务实现了这个接口,你可以使用CopySpec.from(java.lang.Object[])
方法指定源文件集合,可以用CopySpec.into(java.lang.Object)
指定输出目录。
task copyTask(type: Copy) {
from 'src/main/java2'
into 'dst'
}
from()
接受的参数种类和files()
一样,当参数是一个目录是,则目录下的所有文件(不包括本目录)将递归的复制到目标目录。当参数是一个文件时,那么这个文件将会被复制到目标目录,如果参数对应的文件不存在,那么参数将会被忽略掉,当参数是个任务,那么该任务的输出文件将会被拷贝到目标目录,并且这个任务将会自动增加为当前任务的依赖。into()
接受的参数种类和files()
一样.示例如下:
task anotherCopyTask(type: Copy) {
//递归目录所有文件
from 'src2/dir1'
//单个文件
from 'src2/bb.txt'
//任务的输出文件
from copyTask
//任务输出的文件
from copyTask.outputs
def destdir
//闭包惰性求值
into {
destdir
}
destdir = file 'dist2'
}
你还可以通过ant格式的include模式和exclude模式来选择需要拷贝的文件
task copyTaskWithPatterns(type: Copy) {
from 'src'
into 'dist2'
include '**/*.txt'
include '**/*.java'
exclude {
it.file.name.startsWith('file') && it.file.text.contains('label')
}
}
你还可以使用工程的Project.copy(org.gradle.api.Action)
方法来拷贝文件,和使用Copy任务的方式差不多的,但是最大的区别就是copy()
方法不支持增量构建
task copyMethod {
doLast {
copy {
from 'src'
into 'dist2'
include '**/*.txt'
include '**/*.java'
exclude {
it.file.name.startsWith('file') && it.file.text.contains('label')
}
}
}
}
还有一个主要的区别就是copy()
方法中from()
的参数是Task对象时,无法被自动添加依赖,因为它是个方法而不是一个任务。这种情况下只能手动的添加输入输出,支持增量构建的同时支持任务依赖的推断,上一章也讲过了任务依赖推断就是增量构建的福利,如下:
task copyMethod2 {
inputs.files copyTask
outputs.dir 'dist3'
doLast {
copy {
from copyTask
into 'dist3'
}
}
}
最好就是使用Copy任务,因为它在不需要进行额外操作的情况下就可以支持增量构建和任务依赖推断。而copy()
方法使用的场景仅仅是在自定义任务的时候作为任务的一部分存在,在这种场景下,记得要声明任务的输入输出,以便支持增量构建和任务依赖推断。
文件重命名
task renameTask(type: Copy) {
from 'src'
into 'dist4'
//用闭包来映射
rename {
it.replace('file', 'fileDist')
}
//用正则
rename 'file(.+)', 'fileDist$1'
}
过滤文件
task filter(type: Copy) {
from 'src'
into 'dist5'
expand copyright: '2018', version: '1.0.0'
expand project.properties
filter {
"[$it]"
}
filter {
it.startsWith('//') ? null : it
}
filteringCharset = 'UTF-8'
}
使用expand()
方法将会将文件中的${tokenName}
格式的符号进行替换,这个特性要注意使用,防止和源文件冲突。
filteringCharset
可以指定源文件和目标文件的文件编码格式。
CopySpec类
CopySpec形成了一个层次结构,Copy任务的规范都是从这个类继承来的。
task nestedSpecs(type: Copy) {
from('src2') {
exclude 'dir1'
}
exclude '**/*.java'
into 'dist7'
into('dist6') {
from 'src'
}
}
使用Sync
任务
Sync
任务是继承是Copy
任务的,执行的时候会讲源文件拷贝到目标目录,然后把目标目录不是Sync
任务复制的删除掉,相当于scp和rsync的区别,比较常用的场景有安装程序,解压归档文件,备份项目的依赖。下面的代码就是备份项目依赖的例子:
task libs(type: Sync) {
from configurations.runtime
into "$buildDir/libs"
}
文件归档
项目中常常可以见到很多jar归档文件,你也可以自己给项目添加WAR,ZIP或者TAR归档文件,归档文件可以由不同的归档任务创建:War,Zip,Tar,Jar还有Ear,他们的方式都差不多,我们以ZIP为例子:
apply plugin: 'java'
task zipTask(type: Zip) {
from 'src'
into('libs') {
from configurations.runtime
}
doLast {
zipTree("$buildDir/distributions/workingWithFiles.zip").each {
println it
}
}
}
java插件为Zip任务增加了一些默认值,这就是你为什么没有看到into()
方法的原因,只看到个嵌套的CopySpec
的into()
方法,归档任务的工作方式和Copy任务一样。像使用Copy任务一样使用它即可。
归档命名
projectName-version.type
是Gradle归档任务的默认格式,例如:
apply plugin: 'java'
version = '1.1.0'
task myZip(type: Zip) {
from 'src'
println myZip.archiveName
println relativePath(myZip.destinationDir)
println relativePath(myZip.archivePath)
}
执行任务:
± % gradle myZip -q
workingWithFiles-1.1.0.zip
build/distributions
build/distributions/workingWithFiles-1.1.0.zip
可以看到格式是workingWithFiles-1.1.0.zip,归档的名字可以通过archivesBaseName
更改。
apply plugin: 'java'
version = '1.1.0'
task myZip1(type: Zip) {
from 'src'
baseName 'sweetop'
doLast {
println myZip1.archiveName
}
}
然后执行任务:
% gradle myZip1 -q
sweetop-1.1.0.zip
还可以进一步的自定义归档文件的名字:
apply plugin: 'java'
version = '1.1.0'
archivesBaseName = 'gradle'
task myZip2(type: Zip) {
from 'src'
appendix = 'init'
classifier = 'src'
doLast {
println myZip2.archiveName
}
}
执行任务:
± % gradle myZip2 -q
gradle-init-1.1.0-src.zip
归档任务的属性表:
属性名 | 类型 | 默认值 | 描述 |
---|---|---|---|
archiveName | String | baseName-appendix-version-classifier.extension ,如果属性是null将不会增加到最终的归档文件的名字 |
生成的归档文件的名字 |
archivePath | File | destinationDir/archiveName |
归档文件的绝对路径 |
destinationDir | File | 取决于文件类型,如果是jar或者war,那么在project.buildDir/libraries ,如果是zip或者tar,那么就是project.buildDir/distributions |
归档任务所指向的输出目录 |
baseName | String | project.name |
归档文件的baseName位置 |
appendix | String | null | 归档文件的appendix位置 |
version | String | project.version |
归档文件的version位置 |
classifier | String | null | 归档文件的classifier位置 |
extension | String | 归档类型:zip,jar,war,tar,tgz,tbz2 | 归档文件的扩展名 |
你可以使用Project.copySpec(org.gradle.api.Action)
在多个归档任务之间共享内容
可重现的归档过程
有时候你要确保相同代码在不同机器上生成的归档文件从字节这一层都是一样的,这无疑是个极大的挑战,因为不同机器上归档时候的文件顺序都可能不一样,但Gradle为你实现了这一点,只需要添加如下设置即可:
tasks.withType(AbstractArchiveTask) {
preserveFileTimestamps = false
reproducibleFileOrder = true
}
属性文件
在java的开发过程中,属性文件会经常见到,Gradle提供了在构建过程中创建属性文件的方法,就是通过WriteProperties
任务。
WriteProperties
还修复了Properties.store()
的bug,这个bug会对增量构建造成影响。标准的java会每次都生成一个唯一的属性文件,即使很多时候属性值根本没有改变,因为在注释中包含了时间戳,Gradle的WriteProperties
任务则不同,它生成的每个属性文件从字节码上都是相同的,做到这样主要是进行了三个调整:
- 没有时间戳的注释增加到属性文件中
- 换行符是依赖于系统的,当然你可以自定义,默认是
\n
- 所有属性按字母列表存储