第16章 Working With Files 跟文件工作

2018-02-24 15:56 更新

大多數(shù)構(gòu)建工作都要使用到文件。Gradle 添加了一些概念和 API 來幫助您實現(xiàn)這一目標。

16.1. Locating files 定位文件

使用?Project.file()?方法來定位相對于 project 目錄相關(guān)的文件。

Example 16.1. Locating files

build.gradle

// Using a relative path 使用相對路徑
File configFile = file('src/config.xml')

// Using an absolute path 使用絕對路徑
configFile = file(configFile.absolutePath)

// Using a File object with a relative path 使用文件對象中的相對路徑
configFile = file(new File('src/config.xml'))

您可以把任何對象傳遞給 file() 方法,而它將嘗試將其轉(zhuǎn)換為一個絕對路徑的 File 對象。通常情況下,你會傳給它一個 String 或 File 的實例。如果這個路徑是一個絕對路徑,它會用于構(gòu)構(gòu)一個 File 實例。否則,會通過先計算所提供的路徑相對于項目目錄的相對路徑來構(gòu)造 File 實例。這個 file() 方法也可以識別 URL,例如是 file:/some/path.xml。

這是把一些用戶提供的值轉(zhuǎn)換為一個相對路徑的 File 對象的有用方法。由于 file() 方法總是去計算所提供的路徑相對于項目目錄的路徑,最好是使用 new File(somePath),因為它是一個固定的路徑,而不會因為用戶運行 Gradle 的具體工作目錄而改變。

16.2. File collections 文件集合

一個文件集合簡單的說就是一組文件。它通過?FileCollection?接口來表示。Gradle API 中的許多對象都實現(xiàn)了此接口。比如,15.3 依賴配置章節(jié) 就實現(xiàn)了 FileCollection 這一接口。

使用?Project.files()?方法是獲取一個 FileCollection 實例的其中一個方法。你可以向這個方法傳入任意個對象,而它們會被轉(zhuǎn)換為一組 File 對象。這個 files() 方法接受任何類型的對象作為其參數(shù)。根據(jù)16.1 章節(jié) “定位文件”里對 file() 方法的描述,它的結(jié)果會被計算為相對于項目目錄的相對路徑。你也可以將集合,迭代變量,map 和數(shù)組傳遞給 files() 方法。它們會被展開,并且內(nèi)容會轉(zhuǎn)換為 File 實例。

Example 16.2. Creating a file collection

build.gradle

FileCollection collection = files('src/file1.txt',
                                  new File('src/file2.txt'),
                                  ['src/file3.txt', 'src/file4.txt'])

一個文件集合是可迭代的,并且可以使用 as 操作符轉(zhuǎn)換為其他類型的對象集合。您還可以使用 + 運算符把兩個文件集合相加,或使用 - 運算符減去一個文件集合。這里是一些使用文件集合的例子

Example 16.3. Using a file collection

build.gradle

// Iterate over the files in the collection
collection.each {File file ->
    println file.name
}

// Convert the collection to various types
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

// Add and subtract collections
def union = collection + files('src/file3.txt')
def different = collection - files('src/file3.txt')

你也可以向 files() 方法傳一個閉包或一個 Callable 實例。它會在查詢集合內(nèi)容,并且它的返回值被轉(zhuǎn)換為一組文件實例時被調(diào)用。這個閉包或Callable 實例的返回值可以是 files() 方法所支持的任何類型的對象。這是 “實現(xiàn)” FileCollection 接口的簡單方法。

Example 16.4. Implementing a file collection

build.gradle

task list << {
    File srcDir

    // Create a file collection using a closure
    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 }
}

執(zhí)行 gradle -q list

> gradle -q list
Contents of src
src/dir1
src/file1.txt
Contents of src2
src2/dir1
src2/dir2

你可以向 files() 傳入一些其他類型的對象:

FileCollection

它們會被展開,并且內(nèi)容會被包含在文件集合內(nèi)。

Task

任務(wù)的輸出文件會被包含在文件集合內(nèi)。

TaskOutputs

TaskOutputs 的輸出文件會被包含在文件集合內(nèi)。

要注意的一個地方是,一個文件集合的內(nèi)容是緩計算的,它只在需要的時候才計算。這意味著您可以,比如創(chuàng)建一個 FileCollection 對象而里面的文件會在以后才創(chuàng)建,比方說在一些任務(wù)中才創(chuàng)建。

16.3. File trees 文件樹

文件樹是按層次結(jié)構(gòu)排序的文件集合。例如,文件樹可能表示一個目錄樹或 ZIP 文件的內(nèi)容。它通過 FileTree 接口表示。FileTree 接口繼承自FileCollection,所以你可以用對待文件集合一樣的方式來對待文件樹。Gradle 中的幾個對象都實現(xiàn)了 FileTree 接口,例如 source sets。

使用 Project.fileTree() 方法是獲取一個 FileTree 實例的其中一種方法。它將定義一個基目錄創(chuàng)建 FileTree 對象,并可以選擇加上一些 Ant 風(fēng)格的包含與排除模式

Example 16.5. Creating a file tree

build.gradle

// Create a file tree with a base directory
FileTree tree = fileTree(dir: 'src/main')

// Add include and exclude patterns to the tree
tree.include '**/*.java'
tree.exclude '**/Abstract*'

// Create a tree using path
tree = fileTree('src').include('**/*.java')

// Create a tree using closure
tree = fileTree('src') {
    include '**/*.java'
}

// Create a tree using a map
tree = fileTree(dir: 'src', include: '**/*.java')
tree = fileTree(dir: 'src', includes: ['**/*.java', '**/*.xml'])
tree = fileTree(dir: 'src', include: '**/*.java', exclude: '**/*test*/**')

你可以像使用一個文件集合的方式一樣來使用一個文件樹。你也可以使用 Ant 風(fēng)格的模式來訪問文件樹的內(nèi)容或選擇一個子樹:

Example 16.6. Using a file tree

build.gradle

// Iterate over the contents of a tree
tree.each {File file ->
    println file
}

// Filter a tree
FileTree filtered = tree.matching {
    include 'org/gradle/api/**'
}

// Add trees together
FileTree sum = tree + fileTree(dir: 'src/test')

// Visit the elements of the tree
tree.visit {element ->
    println "$element.relativePath => $element.file"
}

16.4. Using the contents of an archive as a file tree 使用歸檔文件的內(nèi)容作為文件樹

您可以使用檔案的內(nèi)容,如 ZIP 或者 TAR 文件,作為一個文件樹。你可以通過使用 Project.zipTree() 或 Project.tarTree() 方法來實現(xiàn)這一過程。這些方法返回一個 FileTree 實例,您可以像使用任何其他文件樹或文件集合一樣使用它。例如,您可以用它來通過復(fù)制內(nèi)容擴大歸檔,或把一些檔案合并到另一個歸檔文件中。

Example 16.7. Using an archive as a file tree

build.gradle

// Create a ZIP file tree using path
FileTree zip = zipTree('someFile.zip')

// Create a TAR file tree using path
FileTree tar = tarTree('someFile.tar')

//tar tree attempts to guess the compression based on the file extension
//however if you must specify the compression explicitly you can:
FileTree someTar = tarTree(resources.gzip('someTar.ext'))

16.5. Specifying a set of input files 指定一組輸入文件

Gradle 中的許多對象都有一個接受一組輸入文件的屬性。例如, JavaCompile 任務(wù)有一個 source 屬性,定義了要編譯的源代碼文件。你可以使用上面所示的 files() 方法所支持的任意類型的對象設(shè)置此屬性。這意味著您可以通過如 File、String、 集合、 FileCollection對象,或甚至是一個閉包來設(shè)置此屬性。這里有一些例子:

Example 16.8. Specifying a set of files

build.gradle

// Use a File object to specify the source directory
compile {
    source = file('src/main/java')
}

// Use a String path to specify the source directory
compile {
    source = 'src/main/java'
}

// Use a collection to specify multiple source directories
compile {
    source = ['src/main/java', '../shared/java']
}

// Use a FileCollection (or FileTree in this case) to specify the source files
compile {
    source = fileTree(dir: 'src/main/java').matching { include 'org/gradle/api/**' }
}

// Using a closure to specify the source files.
compile {
    source = {
        // Use the contents of each zip file in the src dir
        file('src').listFiles().findAll {it.name.endsWith('.zip')}.collect { zipTree(it) }
    }
}

通常情況下,有一個與屬性相同名稱的方法,可以追加這個文件集合。再者,這個方法接受 files() 方法所支持的任何類型的參數(shù)。

Example 16.9. Specifying a set of files

build.gradle

compile {
    // Add some source directories use String paths
    source 'src/main/java', 'src/main/groovy'

    // Add a source directory using a File object
    source file('../shared/java')

    // Add some source directories using a closure
    source { file('src/test/').listFiles() }
}

16.6. Copying files 拷貝文件

你可以使用 Copy 任務(wù)來復(fù)制文件。復(fù)制任務(wù)非常靈活,并允許您進行,比如篩選要復(fù)制的文件的內(nèi)容,或映射文件的名稱。

若要使用 Copy 任務(wù),您必須提供用于復(fù)制的源文件和目標目錄。您還可以在復(fù)制文件的時候指定如何轉(zhuǎn)換文件。你可以使用一個復(fù)制規(guī)范來做這些。一個復(fù)制規(guī)范通過 CopySpec 接口來表示。Copy 任務(wù)實現(xiàn)了此接口。你可以使用 CopySpec.from() 方法指定源文件,使用 CopySpec.into() 方法使用目標目錄。

Example 16.10. Copying files using the copy task

build.gradle

task copyTask(type: Copy) {
    from 'src/main/webapp'
    into 'build/explodedWar'
}

from() 方法接受和 files() 方法一樣的任何參數(shù)。當參數(shù)解析為一個目錄時,該目錄下的所有文件(不包含目錄本身) 都會遞歸復(fù)制到目標目錄。當參數(shù)解析為一個文件時,該文件會復(fù)制到目標目錄中。當參數(shù)解析為一個不存在的文件時,參數(shù)會被忽略。如果參數(shù)是一個任務(wù),那么任務(wù)的輸出文件 (即該任務(wù)創(chuàng)建的文件)會被復(fù)制,并且該任務(wù)會自動添加為Copy任務(wù)的依賴項。 into() 方法接受和 files() 方法一樣的任何參數(shù)。這里是另一個示例:

Example 16.11. Specifying copy task source files and destination directory

build.gradle

task anotherCopyTask(type: Copy) {
    // Copy everything under src/main/webapp
    from 'src/main/webapp'
    // Copy a single file
    from 'src/staging/index.html'
    // Copy the output of a task
    from copyTask
    // Copy the output of a task using Task outputs explicitly.
    from copyTaskWithPatterns.outputs
    // Copy the contents of a Zip file
    from zipTree('src/main/assets.zip')
    // Determine the destination directory later
    into { getDestDir() }
}

您可以使用 Ant 風(fēng)格的包含或排除模式,或使用一個閉包,來選擇要復(fù)制的文件:

Example 16.12. Selecting the files to copy

build.gradle

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() 方法來復(fù)制文件。它是與任務(wù)一樣的工作方式,盡管它有一些主要的限制。首先, copy() 不能進行增量操作(見15.9章節(jié),"跳過處于最新狀態(tài)的任務(wù)")。

Example 16.13. Copying files using the copy() method without up-to-date check

build.gradle

task copyMethod << {
    copy {
        from 'src/main/webapp'
        into 'build/explodedWar'
        include '**/*.html'
        include '**/*.jsp'
    }
}

第二,當一個任務(wù)用作復(fù)制源(即作為 from() 的參數(shù))的時候,copy()方法不能建立任務(wù)依賴性,因為它是一個方法,而不是一個任務(wù)。因此,如果您在任務(wù)的 action 里面使用 copy() 方法,必須顯式聲明所有的輸入和輸出以得到正確的行為。

Example 16.14. Copying files using the copy() method with up-to-date check

build.gradle

task copyMethodWithExplicitDependencies{
    // up-to-date check for inputs, plus add copyTask as dependency
    inputs.file copyTask
    outputs.dir 'some-dir' // up-to-date check for outputs
    doLast{
        copy {
            // Copy the output of copyTask
            from copyTask
            into 'some-dir'
        }
    }
}

在可能的情況下,最好是使用 Copy 任務(wù),因為它支持增量構(gòu)建和任務(wù)依賴關(guān)系推理,而不需要你額外付出。copy() 方法可以作為一個任務(wù)執(zhí)行的部分來復(fù)制文件。即,這個 copy() 方法旨在用于自定義任務(wù) (見Chapter 58. Writing Custom Task Classes 編寫自定義任務(wù)類)中,需要文件復(fù)制作為其一部分功能的時候。在這種情況下,自定義任務(wù)應(yīng)充分聲明與復(fù)制操作有關(guān)的輸入/輸出。

16.6.1. Renaming files 重命名

Example 16.15. Renaming files as they are copied

build.gradle

task rename(type: Copy) {
    from 'src/main/webapp'
    into 'build/explodedWar'
    // Use a closure to map the file name
    rename { String fileName ->
        fileName.replace('-staging-', '')
    }
    // Use a regular expression to map the file name
    rename '(.+)-staging-(.+)', '$1$2'
    rename(/(.+)-staging-(.+)/, '$1$2')
}

16.6.2. Filtering files 過濾文件

Example 16.16. Filtering files as they are copied

build.gradle

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'
    // Substitute property tokens in files
    expand(copyright: '2009', version: '2.3.1')
    expand(project.properties)
    // Use some of the filters provided by Ant
    filter(FixCrLfFilter)
    filter(ReplaceTokens, tokens: [copyright: '2009', version: '2.3.1'])
    // Use a closure to filter each line
    filter { String line ->
        "[$line]"
    }
}

在源文件中,“expand” 和 “filter” 操作查找的 “token” ,被格式化成“@tokenName@” (名稱是 “tokenName”)

16.6.3. Using the CopySpec class 使用CopySpec類

復(fù)制規(guī)范用來組織一個層次結(jié)構(gòu)。一個復(fù)制規(guī)范繼承其目標路徑,包含模式,排除模式,復(fù)制操作,名稱映射和過濾器。

Example 16.17. Nested copy specs

build.gradle

task nestedSpecs(type: Copy) {
    into 'build/explodedWar'
    exclude '**/*staging*'
    from('src/dist') {
        include '**/*.html'
    }
    into('libs') {
        from configurations.runtime
    }
}

16.7. Using the Sync task 使用Sync任務(wù)

Sync 任務(wù)繼承了 Copy 任務(wù)。當它執(zhí)行時,它會將源文件復(fù)制到目標目錄中,然后從目標目錄移除所有不是它復(fù)制的文件。這可以用來做一些事情,比如安裝你的應(yīng)用程序、 創(chuàng)建你的歸檔文件的 exploded 副本,或維護項目的依賴項的副本。

這里是一個例子,維護在 build/libs 目錄中的項目運行時依賴的副本。

Example 16.18. Using the Sync task to copy dependencies

build.gradle

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

16.8. Creating archives 創(chuàng)建歸檔文件

一個項目可以有你所想要的一樣多的 JAR 文件。您也可以將 WAR、 ZIP 和TAG 文件添加到您的項目。使用各種歸檔任務(wù)可以創(chuàng)建以下的歸檔文件: Zip, Tar, Jar, War, and Ear. 他們的工作方式都一樣,所以讓我們看看如何創(chuàng)建一個 ZIP 文件。

Example 16.19. Creating a ZIP archive

build.gradle

apply plugin: 'java'

task zip(type: Zip) {
    from 'src/dist'
    into('libs') {
        from configurations.runtime
    }
}

歸檔任務(wù)與 Copy 任務(wù)的工作方式一樣,并且實現(xiàn)了相同的 CopySpec 接口。像使用 Copy 任務(wù)一樣,你需要使用 from() 的方法指定輸入的文件,并可以選擇是否通過 into() 方法指定最終在存檔中的位置。您可以通過一個復(fù)制規(guī)范來篩選文件的內(nèi)容、 重命名文件和進行其他你可以做的事情。

16.8.1. Archive naming

生成的歸檔的默認名稱是 projectName-version.type。舉個例子:

Example 16.20. Creation of ZIP archive

build.gradle

apply plugin: 'java'

version = 1.0

task myZip(type: Zip) {
    from 'somedir'
}

println myZip.archiveName
println relativePath(myZip.destinationDir)
println relativePath(myZip.archivePath)

執(zhí)行 gradle -q myZip

> gradle -q myZip
zipProject-1.0.zip
build/distributions
build/distributions/zipProject-1.0.zip

它添加了一個名稱為 myZip 的 ZIP 歸檔任務(wù),產(chǎn)生 ZIP 文件 zipProject 1.0.zip 。區(qū)分歸檔任務(wù)的名稱和歸檔任務(wù)生成的歸檔文件的名稱是很重要的。歸檔的默認名稱可以通過項目屬性 archivesBaseName 來更改。還可以在以后的任何時候更改歸檔文件的名稱。

這里有很多你可以在歸檔任務(wù)中設(shè)置的屬性。它們在以下的表 16.1,"存檔任務(wù)-命名屬性"中列出。你可以,比方說,更改歸檔文件的名稱:

Example 16.21. Configuration of archive task - custom archive name

build.gradle

apply plugin: 'java'
version = 1.0

task myZip(type: Zip) {
    from 'somedir'
    baseName = 'customName'
}

println myZip.archiveName

執(zhí)行 gradle -q myZip

> gradle -q myZip
customName-1.0.zip

您可以進一步自定義存檔名稱:

Example 16.22. Configuration of archive task - appendix & classifier

build.gradle

apply plugin: 'java'
archivesBaseName = 'gradle'
version = 1.0

task myZip(type: Zip) {
    appendix = 'wrapper'
    classifier = 'src'
    from 'somedir'
}

println myZip.archiveName

執(zhí)行 gradle -q myZip

> gradle -q myZip
gradle-wrapper-1.0-src.zip

Table 16.1. Archive tasks - naming properties

屬性名稱 類型 默認值 描述
archiveName String baseName-appendix-version-classifier.extension 如果這些屬性中的任何一個為空,那后面的-不會被添加到該名稱中。 生成的歸檔文件的基本文件名
archivePath File destinationDir/archiveName 生成的歸檔文件的絕對路徑。
destinationDir File 依賴于歸檔類型。JAR包和 WAR包會生成到 project.buildDir/libraries中。ZIP文件和 TAR文件會生成到project.buildDir/distributions中。 存放生成的歸檔文件的目錄
baseName String *project.name* 歸檔文件的名稱中的基本名稱部分。
appendix String null 歸檔文件的名稱中的附錄部分
version String *project.version* 歸檔文件的名稱中的版本部分。
classifier String null 歸檔文件的名稱中的分類部分。
extension String 依賴于歸檔的類型,用于TAR文件,可以是以下壓縮類型: zip, jar, war, tar, tgz or tbz2. 歸檔文件的名稱中的擴展名稱部分。

16.8.2. Sharing content between multiple archives 共享多個歸檔之間的內(nèi)容

你可以使用Project.copySpec()方法在歸檔之間共享內(nèi)容。

你經(jīng)常會想要發(fā)布一個歸檔文件,這樣就可從另一個項目中使用它。這一過程在Chapter 52. Publishing artifacts 發(fā)布 artifact會講到

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號