四时宝库

程序员的知识宝库

Gradle学习记录007 用Gradle操作文件 part3

学习如何使用Gradle操作文件,第三部分。该学习记录基于Gradle官方网站资料。本篇参考链接如下:

https://docs.gradle.org/current/userguide/working_with_files.html

11.文件目录深度拷贝

用Gradle拷贝文件的基本步骤如下

  • 定义一个类型为Copy的任务
  • 指定拷贝对象文件或目录
  • 指定拷贝目标文件夹

其中,指定对象文件或目录使用from方法。指定目标文件夹使用into方法。它们都是CopySpec接口的方法。可以接受所有能被files方法接受的参数。

task anotherCopyTask (type: Copy) {
 // 拷贝 src/main/webapp 下的内容
 from 'src/main/webapp'
 // 拷贝单个文件
 from 'src/staging/index.html'
 // 拷贝copyTask任务的输出
 from copyTask
 // 拷贝copyTaskWithPatterns的输出, 这里显示指定了outputs
 from copyTaskWithPatterns.outputs
 // 拷贝一个zip压缩包内的文件树
 from zipTree('src/main/assets.zip')
 // 使用闭包来实现Lazy模式指定拷贝目标文件夹
 into { getDestDir() }
}

文件过滤

之前我们遇到过使用include和exlude来过滤文件集合。他们也是CopySpec接口的方法。

task copyTaskWithPatterns (type: Copy) {
 from 'src/main/webapp'
 into "$buildDir/explodedWar"
 include '**/*.html'
 include '**/*.jsp'
 exclude { FileTreeElement details ->
 details.file.name.endsWith('.html') &&
 details.file.text.contains('DRAFT')
 }
}

当指定的include和exclude重叠的时候exclude会发生作用,把文件或者文件夹排除在拷贝对象之外。

重命名文件

学习记录005 part2里有重命名的示例。值得提到的是,Gradle里的rename方法可以使用标准java的Pattern类来实现正则表达式。使用这个方法的注意点:

  • 第一个参数是斜杠包含的字符串是,rename必须有括号
	rename(/(.+)-staging-(.+)/, '$1$2')
  • 第二个参数最好用单引号包裹,但如果需要用反斜杠转义$符号的时候,则需要用双引号比如"\$1\$2"
  • 第二个参数可以为null,表明不需要更改文件名称
  • rename会对每一个拷贝对象都做重命名处理, 所以需要考虑效率问题。

过滤文件内容

// 引入方法2使用的ant类
import org.apache.tools.ant.filters.FixCrLfFilter
import org.apache.tools.ant.filters.ReplaceTokens
task filter(type: Copy) {
 from "$buildDir/sourceFiles"
 into "$buildDir/destination"
 
 // 方法1
 expand(copyright: '2009', version: '2.3.1')
 expand(project.properties)
 // 方法2,使用filter方法和ant
 // 将所有的换行替换为\r\n
 filter(FixCrLfFilter)
 // 将ant的token替换
 filter(ReplaceTokens, tokens: [copyright: '2019', version: '5.2.1'])
 
 // 删除拷贝对象文件里以-开头的行
 filter { String line ->
 line.startsWith('-') ? null : line
 }
 // 将拷贝对象文件的每一行都用[]括起来
 filter { String line ->
 "[$line]"
 }
 filteringCharset = 'UTF-8'
}

任务执行前的拷贝对象文件

copyright is ${copyright}

-empty line

version is ${version}

-empty line

copyright is @copyright@

-empty line

version is @version@

任务执行后的拷贝文件

[copyright is 2009]

[version is 2.3.1]

[copyright is 2019]

[version is 5.2.1]

活用CopySpec接口

  • 在工程内统一定义,令所有任务都可以使用
CopySpec webAssetsSpec = copySpec {
 from 'src/main/webapp'
 include '**/*.html', '**/*.png', '**/*.jpg'
 rename '(.+)-staging(.+)', '$1$2'
}
task copyAssets (type: Copy) {
 into "$buildDir/inPlaceApp"
 with webAssetsSpec
}
task distApp(type: Zip) {
 archiveFileName = 'my-app-dist.zip'
 destinationDirectory = file("$buildDir/dists")
 // 任务内定义的相同属性,会覆盖工程定义
 from appClasses
 with webAssetsSpec
}

下面的示例不使用CopySpec接口,实现相同功能

def webAssetPatterns = {
 include '**/*.html', '**/*.png', '**/*.jpg'
}
task copyAppAssets(type: Copy) {
 into "$buildDir/inPlaceApp"
 // webAssetPatterns作为参数传递给from方法
 // 同样,这种方式也适用于into,rename等方法
 from 'src/main/webapp', webAssetPatterns
}
task archiveDistAssets(type: Zip) {
 archiveFileName = 'distribution-assets.zip'
 destinationDirectory = file("$buildDir/dists")
 // webAssetPatterns作为参数传递给from方法
 // 同样,这种方式也适用于into,rename等方法
 from 'distResources', webAssetPatterns
}
  • 当需要实现如图的需求时,可以使用子指定(child spesification)
task nestedSpecs(type: Copy) {
 // 主目标文件夹
 into "$buildDir/explodedWar"
 exclude '**/*staging*'
 
 // 过滤html,png,jpg文件从src/dist拷贝到explodedWar
 from('src/dist') {
 // 子指定
 include '**/*.html', '**/*.png', '**/*.jpg'
 }
 // 从sourceSets输出的class文件拷贝到explodedWar/WEB-INF/classes
 from(sourceSets.main.output) {
 // 子指定。主目标文件夹下的路径explodedWar/WEB-INF/classes
 into 'WEB-INF/classes'
 }
 // 从runtimeClasspath将类库拷贝到
 // 主目标文件夹下的路径explodedWar/WEB-INF/lib
 into('WEB-INF/lib') {
 // 子指定
 from configurations.runtimeClasspath
 }
}

使用自定义任务拷贝文件

有时候希望在自定义的任务内,实现一些拷贝功能。可以不使用Copy类型,而是使用Project.copy方法。

task copyMethod {
 doLast {
 copy {
 from 'src/main/webapp'
 into "$buildDir/explodedWar"
 include '**/*.html'
 include '**/*.jsp'
 }
 }
}

需要注意,copy方法不是增量模式的。需要显示标记它的输入(inputs)和输出(outputs)来进行up-to-date检查。

task copyMethodWithExplicitDependencies {
 // 对输入进行up-to-date 检查
 // 将copyTask作为当前任务的依赖
 inputs.files copyTask
 // 对输出进行up-to-date 检查
 outputs.dir 'some-dir'
 doLast{
 copy {
 // 拷贝copyTask任务的输出作为copy方法的输入
 from copyTask
 into 'some-dir'
 }
 }
}

镜像目录或文件集合

Syns类型是Copy的子类。它会把文件拷贝到目标文件夹, 并且删除目标文件夹中的其他内容。

task libs(type: Sync) {
 from configurations.runtime
 into "$buildDir/libs"
}

gradle也提供了Project.sync方法,以用于自定义任务内部使用。

12.深入了解归档

归档,就是压缩文件。在Gradle里得到了很好的支持。

与之前的学习相同的部分不再赘述。这里学习如下几点。

归档文件的命名规则

归档文件的命名规则不是硬性规则,但是一般情况下最好遵守。规则如下:

[projectName]-[version].[type]

plugins {
 id 'base'
}
version = 1.0
task myZip(type: Zip) {
 from 'somedir'
 doLast {
 println archiveFileName.get()
 // zip默认保存的目录build/distributions/
 println relativePath(destinationDirectory)
 println relativePath(archiveFile)
 }
}

得到的zip文件名字为zipProject-1.0.zip。默认保存在zipProject/build/distributions下面

如果需要指定文件名与保存目录, 可以使用archiveFileName和destinationDirectory属性。

另外可以使用如下的属性, 来指定文件名字的各个部位

[archiveBaseName]-[archiveAppendix]-[archiveVersion]-[archiveClassifier].[archiveExtension]

比如

version = 1.0
task myCustomZip(type: Zip) {
 archiveBaseName = 'customName'
 from 'somedir'
 doLast {
 println archiveFileName.get()
 }
}

输出的文件名字是customName-1.0.zip

还可以使用archivesBaseName属性来全局指定所有压缩文件的名字。

plugins {
 id 'base'
}
version = 1.0
archivesBaseName = "gradle"
task myZip(type: Zip) {
 from 'somedir'
}
task myOtherZip(type: Zip) {
 archiveAppendix = 'wrapper'
 archiveClassifier = 'src'
 from 'somedir'
}
task echoNames {
 doLast {
 println "Project name: ${project.name}"
 println myZip.archiveFileName.get()
 println myOtherZip.archiveFileName.get()
 }
}

输出如下

$ gradle -q echoNames

Project name: zipProject

gradle-1.0.zip

gradle-wrapper-1.0-src.zip

关于压缩文件的属性,可以参照 AbstractArchiveTask的API

13.可再生的归档

使用下面的方法,可以在不同的环境中生成完全一样的归档文件。笔者暂时不知道什么情况下会用到这个功能。

tasks.withType(AbstractArchiveTask) {
 preserveFileTimestamps = false
 reproducibleFileOrder = true
}

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接