Gradle 使用文件

2022-08-03 14:41 更新

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

定位文件

你可以使用 Project.file()方法來找到一個相對于項目目錄的文件 。

查找文件

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 的實例。而所提供的這個對象的 tostring() 方法的值會作為文件路徑。如果這個路徑是一個絕對路徑,它會用于構(gòu)構(gòu)一個 File 實例。否則,會通過先計算所提供的路徑相對于項目目錄的相對路徑來構(gòu)造 File 實例。這個 file()方法也可以識別URL,例如是 file:/some/path.xml。

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

文件集合

一個文件集合只是表示一組文件。它通過 FileCollection 接口來表示。Gradle API 中的許多對象都實現(xiàn)了此接口。比如,依賴配置 就實現(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 實例。

創(chuàng)建一個文件集合

build.gradle

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

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

使用一個文件集合

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 接口的簡單方法。

實現(xiàn)一個文件集合

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 }
}  

gradle -q list 的輸出結(jié)果

> 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)建。

文件樹

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

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

創(chuàng)建一個文件樹

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)容或選擇一個子樹:

使用文件樹

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"
}  

使用歸檔文件的內(nèi)容作為文件樹

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

使用歸檔文件作為文件樹

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'))  

指定一組輸入文件

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

指定一組文件

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ù)。

指定一組文件

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() }
}  

復(fù)制文件

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

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

使用 copy 任務(wù)復(fù)制文件

build.gradle

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

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

示例 16.11. 指定復(fù)制任務(wù)的源文件和目標(biāo)目錄

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ù)制的文件:

選擇要復(fù)制的文件

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()不能進(jìn)行增量操作。

使用沒有最新狀態(tài)檢查的 copy() 方法復(fù)制文件

build.gradle

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

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

使用有最新狀態(tài)檢查的 copy() 方法復(fù)制文件

build.gradle

task copyMethodWithExplicitDependencies{
    inputs.file copyTask // up-to-date check for inputs, plus add copyTask as dependency
    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ù)中,需要文件復(fù)制作為其一部分功能的時候。在這種情況下,自定義任務(wù)應(yīng)充分聲明與復(fù)制操作有關(guān)的輸入/輸出。

重命名文件

重命名復(fù)制的文件

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')
}  

過濾文件

過濾要復(fù)制的文件

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 references 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]"
    }
}  

使用 CopySpec 類

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

嵌套的復(fù)制規(guī)范

build.gradle

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

使用 Sync 任務(wù)

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

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

使用同步任務(wù)復(fù)制依賴項

build.gradle

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

創(chuàng)建歸檔文件

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

創(chuàng)建一個 ZIP 文件

build.gradle

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

為什么要用 Java 插件?

Java 插件對歸檔任務(wù)添加了一些默認(rèn)值。如果你愿意,使用歸檔任務(wù)時可以不需要 Java 插件。您需要提供一些值給附加的屬性。

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

歸檔命名

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

創(chuàng)建 ZIP 文件

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)  

gradle -q myZip 的輸出結(jié)果

> 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ù)生成的歸檔文件的名稱是很重要的。歸檔的默認(rèn)名稱可以通過項目屬性 archivesBaseName 來更改。還可以在以后的任何時候更改歸檔文件的名稱。

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

配置歸檔任務(wù)-自定義歸檔名稱

build.gradle

apply plugin: 'java'
version = 1.0
task myZip(type: Zip) {
    from 'somedir'
    baseName = 'customName'
}
println myZip.archiveName  

gradle -q myZip 的輸出結(jié)果

gradle -q myZip customName-1.0.zip

您可以進(jìn)一步自定義存檔名稱:

配置歸檔任務(wù) - 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  

gradle -q myZip 的輸出結(jié)果

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

表 16.1. 歸檔任務(wù)-命名屬性

屬性名稱 類型 默認(rèn)值 描述
archiveName String extension

如果這些屬性中的任何一個為空,那后面的-不會被添加到該名稱中。

生成的歸檔文件的基本文件名
archivePath File 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文件,可以是以下壓縮類型: tbz2. 歸檔文件的名稱中的擴展名稱部分。

共享多個歸檔之間的內(nèi)容

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

你經(jīng)常會想要發(fā)布一個歸檔文件,這樣就可從另一個項目中使用它。

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號